Proxed.AI

Expo & React Native + Proxed.AI DeviceCheck

End-to-end guide to securing your Expo / React Native app's calls to Proxed.AI using Apple's DeviceCheck, partial API keys and the Bearer-token authentication flow.

Secure Proxed.AI Calls in Expo / React Native Using Apple DeviceCheck

TL;DR – You'll build a custom Expo dev client, fetch a DeviceCheck token on iOS, then send Authorization: Bearer <partialKey>.<token> to Proxed.AI. That's it.

Why Do I Need This?

• Protect the real provider key. Proxed uses a split-key model. Your app only knows the client part (partialKey) – the server part lives safely on Proxed. • Block emulators, scripts & jail-broken devices. DeviceCheck proves the request came from a legitimate iOS installation of your app. • Keep development friction-free. Test Mode + a test key lets you skip DeviceCheck when you're iterating.

How Authentication Works with Proxed

ScenarioHeaderNotes
ProdAuthorization: Bearer <partialKey>.<deviceToken>Recommended. Device token is the DCDevice base64 string. No extra headers required.
Dev / Test Modex-proxed-test-key, x-ai-keyUse when Test Mode ON in your project settings. DeviceCheck not required.
Legacyx-device-token, x-ai-keyStill supported but will be deprecated – migrate to Bearer.

The first segment of the Bearer token is always treated as the partial key, so you never have to send x-ai-key when using the recommended flow.


1 – Prerequisites

  1. Apple Developer account.
  2. DeviceCheck enabled for your App ID + a DeviceCheck private key (.p8), Key ID and Team ID (see Apple DeviceCheck guide).
  3. A Proxed project with: • Device Check configured (upload that .p8). • A partial API key (OpenAI, Anthropic, Google AI, etc.).
  4. Expo SDK ✹ 49+ and EAS CLI installed.

2 – Install Packages

npx expo install react-native-device-info
# optional, but handy for custom config plugins
bun add -D @expo/config-plugins

react-native-device-info already wraps the DeviceCheck native APIs.

3 – Create a Custom Dev Client

DeviceCheck isn't available in Expo Go. Generate native projects and build a dev client:

npx expo prebuild --clean          # generates ios/ & android/ directories
EAS_BUILD_PROFILE=development \
  eas build --platform ios

Install the resulting .ipa on a physical device – no simulators.

4 – Fetch the Token & Talk to Proxed

lib/proxed-service.ts:

import DeviceInfo from "react-native-device-info";
import { z } from "zod";

const ENDPOINT = "https://api.proxed.ai/v1/vision";          // or /text, /pdf, /openai/{projectId}/* ...
const PROJECT_ID = "YOUR_PROJECT_UUID";                       // path variant also works
const PARTIAL_KEY = "pk-abc123…";                             // client half of your provider key

// Define expected response structure to keep TS happy
const resultSchema = z.object({ /* … */ });
export type VisionResponse = z.infer<typeof resultSchema>;

export async function analyzeImage(base64: string): Promise<VisionResponse> {
  // 1 – Get DeviceCheck token (skip on Android / emulator)
  const isEmu = await DeviceInfo.isEmulator();
  let dcToken: string | null = null;
  if (!isEmu && DeviceInfo.getSystemName() === "iOS") {
    dcToken = await DeviceInfo.getDeviceToken(); // throws on sim
  }

  // 2 – Build headers
  const headers: Record<string, string> = {
    "Content-Type": "application/json",
    "x-project-id": PROJECT_ID,
    Authorization: `Bearer ${PARTIAL_KEY}.${dcToken ?? ""}`,
  };

  // 3 – POST to Proxed
  const res = await fetch(`${ENDPOINT}/${PROJECT_ID}`, {
    method: "POST",
    headers,
    body: JSON.stringify({ image: base64 }),
  });

  if (!res.ok) throw new Error(`Proxed error ${res.status}`);

  const json = await res.json();
  return resultSchema.parse(json);
}

Development Shortcut: Test Mode

When Test Mode is enabled in the Proxed dashboard:

headers["x-ai-key"] = PARTIAL_KEY;
headers["x-proxed-test-key"] = "YOUR_TEST_KEY"; // no Bearer needed

This bypasses DeviceCheck, letting you test on simulator / Android.


5 – Common Pitfalls

  1. Expo Go? Won't work – needs custom client.
  2. Simulators? generateToken() always fails. Use Test Mode or a real device.
  3. Entitlements missing? Inspect the built .ipa – you need com.apple.developer.devicecheck = true.
  4. Wrong Bearer format? Must be partialKey.token. No spaces, no Bearer: prefix inside the string.
  5. 401 UNAUTHORIZED? Check • Project ID matches. • Partial key matches the server half. • DeviceCheck is enabled in the project. • Token is valid (base64). Use Apple's sandbox endpoint for dev keys.

6 – What Happens Server-Side?

  1. Proxed splits the Bearer at the first . → partialKey, token.
  2. Looks up the server part of the key.
  3. Verifies the DeviceCheck token with Apple unless Test Mode is on.
  4. Reassembles the full provider key in-memory only.
  5. Forwards the request to OpenAI/Anthropic/etc.
  6. Validates the model's response against your schema.
  7. Streams / returns the validated JSON back to the app.

All within a few hundred ms.


7 – Next Steps

• Build Android variant (skip DeviceCheck but still benefits from partial keys). • Use structured responses to get type-safe JSON back. • Add consumption notifications to watch for spikes.

Happy shipping ✌️

On this page