OAuth login flow
On the web the flow is three separate steps — redirect, callback, and finalize. On mobile the SDK runs all three for you in a single call:- Present —
loginWithOAuthopens the provider’s login page in a system web session (ASWebAuthenticationSessionon iOS, a Chrome Custom Tab on Android) - Callback — the provider redirects back to your app’s custom URL scheme with a
challenge_token, which the SDK captures - Finalize — the SDK exchanges the
challenge_tokenfor a session and persists the tokens in the platform’s secure store
FinalizeOAuthLoginResult: either the user is logged in, or an OTP step is required. The second case only happens when the provider has verify_email enabled and the IdP returns an email it has not verified — see Verify email via OTP below.
How is the OAuth flow secured?
How is the OAuth flow secured?
The entire flow is protected by PKCE (Proof Key for Code Exchange). The SDK generates a unique
code_verifier and code_challenge pair for each login attempt, and the challenge_token can only be finalized once. This protects against:- Authorization code interception — a stolen
challenge_tokenis useless without thecode_verifier, which never leaves the device - Replay attacks — the
challenge_tokenis invalidated after a single use - Cross-site request forgery (CSRF) — the server validates that the finalize request matches the original authorization request
Platform setup
The provider redirects back to your app through a custom URL scheme (e.g.myapp://oauth-callback) — not an https URL. Passing an http/https redirect throws an invalid-configuration error before any network call. The redirect URI must also be allowlisted in your provider’s configuration.
- iOS
- Android
- Flutter
- React Native
No additional setup is required. The system web session captures the redirect scheme directly, so you don’t need to register it in
Info.plist.Social login lives in the PreludeAuthSocial product — an opt-in module so apps that skip social pull no extra code. Add it alongside PreludeAuth in Package.swift:Sign in with a provider
CallloginWithOAuth with a provider and your redirect URI. The SDK presents the provider’s page, handles the callback, and finalizes the session. Supported providers are google, apple, microsoft, github, okta, and facebook.
- iOS
- Android
- Flutter
- React Native
prefersEphemeralSession: true to start every login from a clean session with no shared browser cookies.Handle the result
On success the SDK persists the access and refresh tokens in the platform’s secure store and returns the outcome. Handle the two cases: the user is logged in, or an email OTP step is required. A dismissed page surfaces as a cancellation — usually swallowed rather than shown as an error.- iOS
- Android
- Flutter
- React Native
Verify email via OTP
When the provider config hasverify_email enabled and the IdP returns an unverified email, loginWithOAuth does not finalize the login. Instead it returns an OTP-required result carrying a resumable challenge and the email the code was sent to.
The code has already been sent. Show your OTP screen, then pass the code the user enters — along with the challenge from the result — to checkOAuthEmailOTP. On success it returns the authenticated user. A wrong code leaves the challenge valid so the user can retry.
- iOS
- Android
- Flutter
- React Native
Present your own web session
loginWithOAuth presents the provider page for you. If you’d rather present it yourself — a custom web view or your own browser session — use the lower-level pair instead. initiateOAuthLogin returns the provider’s authorization URL; present it, capture the challenge_token delivered to your redirect URI, then redeem it with finalizeOAuthLogin, which returns the same result type as loginWithOAuth.
- iOS
- Android
- Flutter
- React Native
Try it
Try it
Each example presents provider buttons, completes the login, and drops into an OTP screen when the provider’s email needs verifying. Use the redirect scheme you configured in Platform setup.
- iOS
- Android
- Flutter
- React Native
Add the
PreludeAuthSocial product, then replace ContentView.swift with:ContentView.swift