Mobile Widget SDKs

# Authentication

The mobile app obtains a short-lived embed token from its own trusted backend. Returning.AI access credentials never enter the app.

Server-authenticated mobile widget

The native SDK owns the widget exchange while your backend owns customer identity and long-lived credentials.

1. Request an embed token

   Signed-in mobile app

   Call your own authenticated backend. Do not send a customer identifier selected by an untrusted app screen.
2. [Resolve the trusted user](https://docs.returning.ai/broker-integrations/users.md)

   Your backend

   Read the current host session and map it to the identifiers configured for the target Returning.AI widget.
3. Mint the embed token

   Your backend and Returning.AI

   Use server-only access credentials to call the Returning.AI token endpoint and return only the short-lived embed token.
4. Exchange inside the native SDK

   Mobile Widget SDK

   The provider supplies the embed token. The SDK authenticates the configured widget with its exact clientOrigin.
5. Deliver the access token

   SDK-owned WebView

   The WebView receives only the exchanged access token and expiry. The embed token does not cross the bridge.

## Mint the token on your backend

Your backend calls the Returning.AI endpoint after it has authenticated the app user. Replace the example identifier with the configured mapping for that widget.

`your-server-to-returning.http`

```
POST https://api-v2.returning.ai/v2/api/widget-access-keys/token
Content-Type: application/json

{
  "accessId": "SERVER_ACCESS_ID",
  "accessKey": "SERVER_ACCESS_KEY",
  "userIdentifiers": {
    "data-customer-id": "VALUE_FROM_TRUSTED_HOST_SESSION"
  }
}

// Returning.AI response:
// { "data": { "embedToken": "...", "expiresIn": 900 } }
```

Flatten the successful upstream response for the app. Keep the response non-cacheable and return safe errors without upstream bodies or credentials.

`your-server-to-app.http`

```
200 OK
Cache-Control: no-store
Content-Type: application/json

{
  "embedToken": "SHORT_LIVED_TOKEN",
  "expiresIn": 900
}
```

> Never bundle dashboard credentials
>
> Do not place `accessId`, `accessKey`, or a signing secret in mobile source, environment files, application packages, WebView HTML, or logs.

## Connect the token provider

Each platform exposes the same asynchronous provider contract using its native language:

**Flutter**

`Future<String> Function()`

**Android**

`suspend fun invoke(): String`

**iOS**

`@Sendable () async throws → String`

**React Native**

`() → Promise<string>`

## Allow the exact client origin

`clientOrigin` is an HTTPS origin without a path, query, or fragment. It is configured per widget. Another widget working with the same origin does not prove this widget is allowlisted.

An allowlist miss normally returns `403 Domain not authorized` during native authentication and may appear as a WebView HTTP 502.

## Return a fresh token after expiry

When the widget session expires, the SDK clears its native access-token cache. The next authentication request calls the provider again. Fetch a new embed token from your backend instead of returning a cached value.

Continue with a platform guide: [Flutter](https://docs.returning.ai/mobile-widget-sdk/flutter.md), [Android](https://docs.returning.ai/mobile-widget-sdk/android.md), [iOS](https://docs.returning.ai/mobile-widget-sdk/ios.md), or [React Native](https://docs.returning.ai/mobile-widget-sdk/react-native.md).
