- Wallet authentication (user → wallet provider): the user proves control of their wallet directly to the provider (Turnkey, Portal, or Privy). This happens outside Rain, through the vendor’s own SDK, and produces the authenticated material you hand to the Rain SDK. It keeps the wallet non-custodial: Rain never sees key material.
- Rain session (app → Rain API), optional: if your app needs to call user-scoped Rain endpoints directly instead of proxying through your backend, a short-lived Client Session Token (CST) authenticates it. The SDK’s built-in Rain API client mints and caches CSTs for you, or your backend can mint one and forward it. Otherwise, keep Rain calls on your backend with your API key. See Backend API key vs. Client Session Token.
Authentication happens entirely outside Rain, through the provider’s own SDK. Your wallet provider secures the key material — for example, in a secure enclave (TEE) or as an encrypted key share on the user’s device. Rain never sees the key material and stores only wallet addresses.
The two headers are different: your primary API key goes in the
Api-Key header (backend only); a CST is a cst_-prefixed bearer token the app sends as Authorization: Bearer <cst>. The Client SDK sets the bearer header for you. For what the app can call with a CST, see Common flows.Wallet providers
Provider authentication happens in your app, through the vendor’s own SDK. Each Rain adapter receives the authenticated material and resolves it into a wallet client. The Turnkey and Privy adapters probe the wallet when you resolve the client, so an unusable session fails fast atrain.provider(...). Portal validates its session lazily, on the first operation.
Here’s how they compare:
Turnkey (managed program)
Under the managed Turnkey program, Rain provisions and configures Turnkey for you and gives you anorganizationId and authProxyConfigId. Authenticate with Turnkey’s SDK, using a passkey (recommended: seamless and phishing-resistant) or an email or SMS one-time passcode. Then hand the authenticated TurnkeyContext to the adapter. The snippets below call Turnkey’s own SDK, not Rain’s:
TurnkeyProvider(TurnkeyConfig(turnkey: turnkeyContext)). Omit walletAddress to use the first Ethereum account from the Turnkey context, or pass an explicit address to override it. Don’t mutate the context (re-auth, logout, wallet switch) while Rain calls are in flight. Rebuild the SDK after such changes.
If your app pins bcprov-jdk18on, align it with bcprov-jdk15to18: on Android, the core module forces that version to resolve a conflict between Turnkey’s and web3j’s crypto dependencies.
Portal
Supply a valid Portal session token; the adapter constructs and initializes Portal itself from the token and the SDK’s RPC endpoints (mapped to CAIP-2eip155:<chainId> form). On iOS, backup storage (iCloud, Keychain, password) is configured for you.
RainSDKError.unauthorized at resolution, and an invalid or expired token surfaces as RAIN_501 on the first operation. On Android, chainId optionally sets the default chain (falls back to RainChain.AVALANCHE_MAINNET when configured, else the first configured chain).
To reach Portal-only features (backup, recovery) on iOS without importing PortalSwift into your Rain code, use the creation hook:
Privy
Your app owns Privy authentication end to end. Initialize the Privy SDK (on Android, inApplication.onCreate()), log the user in (SMS, email, OAuth, or passkey), and make sure an embedded Ethereum wallet exists. Then hand the authenticated Privy singleton to the adapter.
walletAddress to use the user’s first embedded Ethereum wallet. Signing and broadcasting route through Privy’s EIP-1193 embedded-wallet provider; balance and fee reads go through the RPC endpoints you configured on the builder, not through Privy.
- If no user is logged in, the adapter throws
RAIN_201. - If there’s no embedded wallet, it throws
RAIN_404. getTransactionsreturns an empty result, since Privy has no history endpoint.
Built-in Rain API client
Independently of wallet auth,RainSdk includes a client for the Rain issuing API. Configure it once and the SDK fetches the user’s collateral contracts and admin withdrawal signatures itself, minting and caching the required short-lived CST automatically. These calls never resolve or authenticate a wallet provider.
Configure it at build time, or later on the built instance:
Session handling
The SDK exchanges yourApi-Key for a CST (POST /v1/issuing/users/{userId}/sessions) and sends it as Authorization: Bearer <cst> on data calls.
- The token is cached and reused while valid, and re-minted within 60 seconds of expiry.
- A
401/403invalidates the cached token and retries the mint exactly once before surfacingRAIN_202. - Calling any built-in API method without configuring credentials throws
RAIN_104. - Non-auth HTTP failures surface as
RAIN_302with the status code.
Mint a Rain session token (backend)
If you’d rather not put any API key in the app, your backend can mint the CST instead and forward it. The user must exist in Rain before you can mint a session, so create the user’s application first (backend, with the wallet address; see Common flows). Then exchange your primaryApi-Key for a user-scoped Client Session Token and return the token to your app.
userId and expires after roughly an hour. Your app sends it as Authorization: Bearer <cst>. When it expires, mint a new one: the SDK surfaces an expired provider session as RAIN_201 and an invalid API key or session as RAIN_202.
Use the sandbox host https://api-dev.rain.xyz/v1 while you build, and https://api.rain.xyz/v1 for production. CST issuance is enabled per tenant: contact platform@rain.xyz if a 403 says it isn’t enabled for your account.
What’s next
Set Up a Wallet
Build the SDK and create the user’s wallet.
Common flows
See the full backend-vs-app integration path.