To migrate from AppAuth-Android, AppAuth-iOS or flutter_appauth (which wraps both) to the Versola OAuth SDK, you replace the request, redirect and token-storage code with one OAuthClient and one login() call. This page maps AppAuth concepts to the SDK, lists what you stop doing and what behaves differently, and shows before and after code for Android, iOS and Flutter. The condition: the SDK serves public clients that sign in through the system browser, so read What does AppAuth do that the SDK does not do? first.
Prerequisites
- An app that signs in with AppAuth-Android, AppAuth-iOS or
flutter_appauthtoday. - A provider client you can register as public: no secret, PKCE
S256. - The provider’s page in Providers, for example Keycloak, to check scopes, redirect URIs and sign-out.
What changes when you replace AppAuth?
- Your app stops holding authentication state. There is no
AuthStateto serialize and no refresh token in your hands.OAuthClientowns storage, refresh and sign-out, and you observe a session. - Sign-in is one call,
login(). PKCE,state,nonce, redirect validation, the code exchange and ID-token validation happen inside it and cannot be switched off. - The Versola OAuth SDK protects every stored token with the Android Keystore or the iOS Keychain.
- Users sign in once after the update, because existing refresh tokens cannot be imported (see What happens to existing users and tokens?).
- Provider failures reach your app as four error categories, and the SDK repeats safe requests itself (see How does error handling change?).
How do AppAuth concepts map to the SDK?
Kotlin names are from versola.oauth (and versola.oauth.core for the error and state types), Swift names from the module OAuthClient (package product VersolaOAuth), Dart names from package:versola_oauth/versola_oauth.dart.
| AppAuth concept | AppAuth names | This SDK |
|---|---|---|
| Provider configuration by discovery | Android AuthorizationServiceConfiguration.fetchFromIssuer; iOS OIDAuthorizationService.discoverConfiguration(forIssuer:); Flutter issuer or discoveryUrl | OAuthConfiguration(issuer = ...). Discovery runs lazily on the first operation, is cached on disk and is refetched after 24 hours or on a failure that a changed endpoint could explain. |
| Provider configuration by endpoints | Android new AuthorizationServiceConfiguration(authorizationEndpoint, tokenEndpoint); iOS OIDServiceConfiguration(authorizationEndpoint:tokenEndpoint:); Flutter serviceConfiguration | OAuthConfiguration(endpoints = ProviderEndpoints(...)). issuer, authorizationEndpoint, tokenEndpoint and jwksUri are required, because an ID token that cannot be verified is refused. Exclusive with issuer. |
| Discovery at a non-standard URL | Android fetchFromUrl | No equivalent. Give explicit endpoints. |
| The request | Android AuthorizationRequest.Builder; iOS OIDAuthorizationRequest; Flutter AuthorizationTokenRequest | Not an object you build. clientId, redirectUri and scopes live in OAuthConfiguration; per-login choices are LoginOptions (scopes, prompt, loginHint, uiLocales, acrValues, maxAge, resources, extraParameters, ephemeralBrowser). |
| Running the flow | Android AuthorizationService.performAuthorizationRequest or getAuthorizationRequestIntent, then performTokenRequest; iOS OIDAuthState.authState(byPresenting:presenting:); Flutter authorizeAndExchangeCode | client.login(options): one suspending call that runs the browser, validates the redirect, exchanges the code and validates the ID token. |
| The service object | Android AuthorizationService; iOS OIDAuthorizationService | OAuthClient, from OAuthClient.create(...). One instance per configuration per process. |
| Session state | AuthState (Android), OIDAuthState (iOS), the response objects (Flutter) | Not exposed. Observe client.sessionState (Kotlin, a Flow) or client.sessionStates() (Swift AsyncStream, Dart Stream), or ask for the current value. Seven states, listed in Session states and errors. |
| Persisting the state | You: AuthState.jsonSerializeString() to SharedPreferences (Android README); archive OIDAuthState (the iOS README says it is the only object to serialize); store the response (Flutter) | The SDK: the refresh token and the ID token sit in a store wrapped by the Keystore or the Keychain, never in a place you can read. No API exports or imports it. |
| Fresh tokens | Android authState.performActionWithFreshTokens(service, action) with execute(accessToken, idToken, ex); iOS authState.performAction { accessToken, idToken, error in }; Flutter token(TokenRequest(... refreshToken ...)) | client.accessToken() refreshes first if needed, with one refresh in flight however many callers. Or let the client attach the token: client.interceptor(origins), client.authenticatedClient(origins) or client.call(request) (Kotlin), client.authenticatedSession(origins:) (Swift). Flutter: accessToken() only. |
| Forcing a refresh | AppAuth refreshes when the token is expiring | client.refresh() spends the stored refresh token once, now. |
| ID token | AuthState carries it; Android validates a few claims, iOS exposes OIDIDToken | client.claims (typed IdTokenClaims), never the raw token. userInfo() is separate. |
| End session | Android EndSessionRequest.Builder(...).setIdTokenHint(...).setPostLogoutRedirectUri(...) and performEndSessionRequest; iOS OIDEndSessionRequest; Flutter endSession(EndSessionRequest(idTokenHint, postLogoutRedirectUrl)) | client.logout(LogoutOptions) revokes the refresh token when the provider advertises revocation, ends the provider session in the system browser when it advertises an end-session endpoint, and wipes local state. postLogoutRedirectUri is configuration. It returns LogoutOutcome(sessionEnded, tokenRevoked). |
| Redirect, Android | Manifest placeholder appAuthRedirectScheme, or an intent filter on net.openid.appauth.RedirectUriReceiverActivity | An intent filter on versola.sdk.browser.VersolaRedirectActivity, preferably a verified App Link. Under an Auth Tab the browser returns the redirect to the SDK directly. |
| Redirect, iOS | The app delegate’s application(_:open:options:) calls resumeExternalUserAgentFlow(...) on a stored currentAuthorizationFlow | Nothing in the normal case: ASWebAuthenticationSession returns the redirect to the SDK. For a Universal Link received while the app was cold, call await client.handleRedirect(url). |
| Redirect, Flutter | Android placeholder or filter; iOS CFBundleURLSchemes in Info.plist | The same native setup as the Android and iOS rows, in the host app. |
| Browser choice | Android BrowserAllowList, BrowserDenyList, VersionedBrowserMatcher; iOS OIDExternalUserAgentIOSCustomBrowser | None. The SDK picks an Auth Tab where the device’s Custom Tabs provider supports it, a Custom Tab otherwise and any browser last. iOS uses ASWebAuthenticationSession. |
| Ephemeral session | Flutter preferEphemeralSession | LoginOptions.ephemeralBrowser, best effort. Off by default because it breaks passkeys and single sign-on. |
| Custom HTTP | Android AppAuthConfiguration with setConnectionBuilder; iOS OIDURLSessionProvider | OAuthConfiguration.httpClient (OkHttp) or urlSession (Swift). Redirects are switched off on the SDK’s own requests whatever your client does. |
| Cancellation and errors | Android AuthorizationException; iOS NSError codes; Flutter FlutterAppAuthUserCancelledException and FlutterAppAuthPlatformException | Four categories: Recoverable, Reauthenticate, StepUp, Configuration (see Session states and errors). A user cancel is Recoverable(UserCancelled). |
What do you stop doing?
- Persisting
AuthState. No serialization, noSharedPreferencesor Keychain code for tokens, noAuthStateManagerof your own. After a restartcreate()restoresAuthenticatedwithout network I/O, orAuthorizingif the app was killed mid-browser. - Coordinating refresh. Concurrent callers coalesce onto one refresh. A rotated refresh token is stored before the old one is forgotten and is never spent twice.
invalid_grantisReauthenticate(GrantRejected), not a retry. - Handling PKCE,
stateandnonce. The SDK generates them from the platform’s secure random source, persists them before the browser opens and validates them on return. You don’t pass, store or compare them. - Validating the ID token. It happens before the grant is persisted (see What behaves differently from AppAuth?). You don’t need a separate JWT library for it.
- Handing a redirect to the library. On Android the SDK’s activity receives it, or the Auth Tab returns it. On iOS the system session returns it. Only a cold-start Universal Link needs
handleRedirect. - Attaching tokens. The interceptor or the authenticated session adds
Authorizationto requests for the origins you named, refreshes once on a 401 and replays once. - Writing retry loops around 500, 503 and 429. The SDK repeats the requests that are safe to repeat and waits as long as the provider asks (see How does error handling change?).
What behaves differently from AppAuth?
The SDK checks more than AppAuth does in the places below. A provider that worked with AppAuth can therefore fail with a Configuration error on the first run, which tells you what the provider or the client registration needs to fix.
| Area | AppAuth | This SDK |
|---|---|---|
| ID token | Android IdToken.validate checks iss, aud/azp, exp, iat and, for the code grant, the nonce; its source says it relies on TLS instead of verifying the signature. iOS OIDIDToken parses the token and, per its documentation, does not verify the signature. | The signature is verified against jwks_uri with a closed set of asymmetric algorithms (none and HMAC refused). Then iss, aud/azp, exp, iat, nonce, auth_time under maxAge and at_hash are checked, before anything is persisted. A refusal is Configuration(IdTokenRejected(reason)). The same rules apply to an ID token on a refresh response. |
iss | Android checks the ID token’s issuer against discovery when a discovery document is present. | The discovery document’s issuer must equal the configured one (one trailing slash is ignored). The ID token’s iss must equal the discovered value byte for byte. The redirect’s iss is checked (RFC 9207) when present, and required when discovery advertises it. A templated issuer is unsupported. |
| PKCE | The iOS README says PKCE is used if the server supports it. | Always S256, never plain, never off. A provider that lists code_challenge_methods_supported without S256 is ProviderUnsupported("S256"). |
| Client type | A client secret can be supplied (Android ClientSecretBasic, ClientSecretPost; the iOS README’s sample passes a clientSecret). | Public clients only. No clientSecret exists in any configuration type. |
| Browser | System browser; WebView not supported. | System browser only, and a web view is unrepresentable in the public API. |
| Token storage | Your code. | The SDK, in the Keystore (Android) or the Keychain (iOS, AfterFirstUnlockThisDeviceOnly, not synchronized). On Android the files sit under noBackupFilesDir, so a backup cannot replay them on another device. |
| Errors | Platform exception types and codes. | Four categories. error_description is attached for logging and never matched on. |
| Server errors and retries | Your code. | The SDK repeats safe requests on 408, 429, 500, 502, 503 and 504 and honours Retry-After on 429 and 503. See Server errors, 429 and Retry-After. |
| Opening the browser | The app calls it. | Same, and the SDK never opens it on its own: Reauthenticate is a diagnosis, and you decide when login() runs. login() while Authorizing is refused with InvalidConfiguration. |
| Cold-start redirect | None. | A redirect that reaches a cold-started process completes the pending request (kept for up to 10 minutes) and publishes Authenticated on sessionState. |
| Tokens and origins | Whatever your code does with the token. | The authenticated client attaches a token only to the protectedOrigins you configured. Any other request passes untouched. |
| Issuer scheme | Android allows setSkipIssuerHttpsCheck(true) for testing. | https only, with http accepted on a loopback host only. No flag. |
| Platform floor | AppAuth-Android README: API 16+, App Links API 23+. AppAuth-iOS README: iOS 15+, HTTPS redirects iOS 17.4+. | minSdk 26 on Android; iOS 17.4 for the package. |
| Redirect URI | A custom scheme is the README’s recommendation on Android; HTTPS is supported. | An https verified link is preferred. A private-use scheme works and logs a warning on every create(). Cleartext non-loopback http is refused. |
| Flutter data | flutter_appauth returns access, refresh and ID tokens to Dart. | Dart gets the short-lived access token from accessToken() and nothing else credential-shaped. |
What does AppAuth do that the SDK does not do?
The Versola OAuth SDK is a public-client, authorization-code, system-browser library. AppAuth features outside that shape have no counterpart: the three platforms declare no public symbol for them. Check this list against your current setup before you start.
- Client secrets and confidential clients. The configuration has no secret field, by design (RFC 8252 section 8.5). If your provider client uses a secret, register a secret-less public client for the app. A provider that insists on a secret from a native app is unsupported.
- Other client authentication at the token endpoint (AppAuth-Android has a
ClientAuthenticationtype). The token request carriesclient_idonly. - Dynamic client registration (AppAuth
RegistrationRequest/OIDRegistrationRequest). No counterpart. - Embedded user agents. A web view cannot be passed in.
- Choosing the browser. No allow list, deny list, custom browser or custom Custom Tabs intent.
- Discovery at a custom URL (
fetchFromUrl). Use explicit endpoints. - Splitting the flow. Flutter’s
authorizefollowed bytoken(code), or a manualperformTokenRequest, has no counterpart.login()is atomic, and no authorization code, verifier or nonce is ever handed to the app. - Reading or injecting the refresh token. Your backend cannot receive the app’s refresh token, and
token(TokenRequest(... refreshToken: ...))has no counterpart.refresh()spends the stored token. - The raw ID token.
claimsexposes the typed claims of the validated token, and the raw token stays encrypted forid_token_hintonly. If a backend needs the ID token itself, this SDK cannot supply it. - Turning off checks.
setNonce(null)andsetSkipIssuerHttpsCheck(true)(AppAuth-Android), a non-httpsissuer, a missingissand an unverified ID token are all refused. A provider that needs one of them can’t be used with the SDK. - Several accounts at once. One session per configuration. A configuration with another issuer or
clientIdis a separate client with its own storage. - macOS, tvOS and desktop. AppAuth-iOS documents macOS and tvOS support. The SDK targets Android and iOS, and Flutter on those two.
- A parameter on the token request.
extraParametersgoes on the authorization request. The token request carries what the SDK builds, plusresourcewhen you gaveresources. - Custom user-agent or request interception beyond the HTTP client.
httpClient/urlSessionis the only seam.
What happens to existing users and tokens?
Existing refresh tokens cannot be imported, so every user signs in once after the update. No public declaration of the three platforms (the Android API dump, the Swift public surface of OAuthClient, the Dart exports of versola_oauth) accepts a refresh token, an ID token or a serialized state, and the configuration types have no field for one. The credential stores that hold them are internal wiring, behind the @VersolaInternalApi opt-in marker on Android and package access on iOS. The first create() finds nothing and reports Unauthenticated.
Plan the rollout around that:
- Ship the SDK in the app update and treat the first launch as signed out. Show your sign-in screen. In most cases
login()completes with the user’s existing provider session, with no password entry if the browser is still signed in. - Delete the old state. After the first successful
login(), remove the persisted AppAuth state: theAuthStateJSON inSharedPreferencesor wherever your own manager put it, the archivedOIDAuthStatein the Keychain or in defaults, and the tokensflutter_appauthreturned if you kept them (for example in a secure-storage plugin). A leftover refresh token is a credential that nobody uses and nobody guards. - Optionally revoke the old refresh token at the provider before you delete it, if the provider has a revocation endpoint (RFC 7009:
client_idandtoken, no secret for a public client). The SDK can’t do it for you, because it never sees that token. - Keep the old redirect URI registered while old app versions are in use. If you move from a custom scheme to an
httpsApp Link or Universal Link, add the new URI. Don’t replace the old one until the old versions are gone. - If the old client used a secret, the SDK needs a different provider client, registered as public. Register it next to the old one and move the app over.
- The same
clientIdand redirect URI can stay. The SDK keys its storage by issuer andclientId, and it does not read AppAuth’s.
How does error handling change?
AppAuth reports a failure as a platform exception type or code (AuthorizationException on Android, NSError on iOS, FlutterAppAuth exceptions in Flutter) and leaves retries to your app. The Versola OAuth SDK sorts every failure into one of four categories, and the category tells the app what to do:
| Category | What your app does |
|---|---|
Recoverable | Offer a retry. A user cancel is Recoverable(UserCancelled). |
Reauthenticate | Send the user to login(). A rejected refresh token (invalid_grant) is Reauthenticate(GrantRejected). |
StepUp | Call stepUp. |
Configuration | Fix the deployment. Don’t catch it silently in production. |
When a provider answers 500, 503 or 429, the SDK repeats the requests that are safe to repeat, waits as long as Retry-After asks, and then returns one Recoverable error. Your app needs no retry loops of its own for these cases. The rules, with code for each platform, are on Server errors, 429 and Retry-After.
How do I migrate from AppAuth-Android?
The AppAuth side below is abridged from the README’s examples. The SDK side is the target API.
Before (AppAuth-Android)
// build.gradle: manifestPlaceholders = ['appAuthRedirectScheme': 'com.example.app']
AuthorizationServiceConfiguration.fetchFromIssuer(Uri.parse("https://id.example.com")) { config, ex ->
val request = AuthorizationRequest.Builder(
config!!, "example-app", ResponseTypeValues.CODE, Uri.parse("com.example.app:/oauth"),
).setScope("openid profile").build()
// performAuthorizationRequest(request, completionIntent, cancelIntent), then in the
// completion activity: AuthorizationResponse.fromIntent(intent), AuthState.update(...),
// authService.performTokenRequest(resp.createTokenExchangeRequest(), callback)
}
// Later, with state restored from AuthState.jsonDeserialize(json):
authState.performActionWithFreshTokens(authService) { accessToken, idToken, ex ->
// write the updated AuthState back, then call the API with accessToken
}
After (Versola OAuth SDK)
-
Dependency. Remove
net.openid:appauth. Add the SDK from Maven Central, withgoogle()andmavenCentral()in your repositories.minSdkbecomes 26.dependencies { implementation("io.versola:oauth-android:<version>") } -
Manifest. Remove the
appAuthRedirectSchemeplaceholder and anynet.openid.appauth.RedirectUriReceiverActivityfilter. Add the intent filter on the SDK’s activity for your redirect URI; the snippet is on the Android page. For a verified App Link, also serveassetlinks.jsonon the host. -
Create the client once, with the same issuer, client id and scopes. Use an
httpsredirect URI if you can register one with the provider.val client = OAuthClient.create( context, OAuthConfiguration( issuer = "https://id.example.com", clientId = "example-app", redirectUri = "https://app.example.com/oauth", postLogoutRedirectUri = "https://app.example.com/signed-out", scopes = setOf("openid", "profile"), protectedOrigins = setOf("https://api.example.com"), ), ) -
Sign in. Replace the request, the redirect activity and the token exchange with one call:
scope.launch { try { client.login() } catch (e: VersolaError) { // Recoverable, Reauthenticate, StepUp or Configuration } } -
Call your API. Replace
performActionWithFreshTokensand theAuthStatewrite-back with the interceptor on your ownOkHttpClient, or withclient.call(request):val http = OkHttpClient.Builder() .addInterceptor(client.interceptor()) .build()If you need the value,
client.accessToken()returns anAccessToken. It is the escape hatch, and a token handed to your code is one you can log. -
Observe the session instead of reading your stored state: collect
client.sessionState. -
Sign out with
client.logout(). It returnsLogoutOutcome(sessionEnded, tokenRevoked). -
Delete the old
AuthStatestorage (see What happens to existing users and tokens?).
How do I migrate from AppAuth-iOS?
Before (AppAuth-iOS)
OIDAuthorizationService.discoverConfiguration(forIssuer: issuer) { configuration, error in
guard let configuration else { return }
let request = OIDAuthorizationRequest(
configuration: configuration, clientId: clientID,
clientSecret: clientSecret, // the README's sample passes a secret; a public client has none
scopes: [OIDScopeOpenID, OIDScopeProfile], redirectURL: redirectURI,
responseType: OIDResponseTypeCode, additionalParameters: nil)
appDelegate.currentAuthorizationFlow =
OIDAuthState.authState(byPresenting: request, presenting: self) { authState, error in
// keep authState
}
}
func application(_ app: UIApplication, open url: URL,
options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
if let flow = self.currentAuthorizationFlow {
try? flow.resumeExternalUserAgentFlow(url) // abridged: the README also handles the error
self.currentAuthorizationFlow = nil
return true
}
return false
}
authState?.performAction { accessToken, idToken, error in
// call the API with accessToken
}
After (Versola OAuth SDK)
-
Dependency. Remove AppAuth from your package or Xcode project. Add the SDK’s public repository and the
VersolaOAuthproduct, thenimport OAuthClient. The package needs iOS 17.4.dependencies: [ .package(url: "https://github.com/versolauth/versola-sdk", from: "<version>"), ], targets: [ .target( name: "App", dependencies: [.product(name: "VersolaOAuth", package: "versola-sdk")] ), ] -
Entitlements for an
httpsredirect: Associated Domains for the host, and theapple-app-site-associationfile, as described on the iOS page. A private-use scheme needs none of that and logs a warning. -
Create the client (async, once). The
presentationAnchoris a closure that returns the window the browser sheet is presented from:let configuration = try OAuthConfiguration( issuer: "https://id.example.com", clientId: "example-app", redirectUri: "https://app.example.com/oauth", postLogoutRedirectUri: "https://app.example.com/signed-out", scopes: ["openid", "profile"], protectedOrigins: ["https://api.example.com"] ) let client = try await OAuthClient.create( configuration: configuration, presentationAnchor: { window } // a UIWindow the app owns ) -
Sign in. Delete
currentAuthorizationFlowand theapplication(_:open:options:)plumbing:do { try await client.login() } catch let error as OAuthError { // .recoverable, .reauthenticate, .stepUp or .configuration }Keep one handler, only for Universal Links that arrive while the app is cold:
.onOpenURL { url in Task { _ = await client.handleRedirect(url) } } -
Call your API with the authenticated session instead of
performAction:let session = try client.authenticatedSession() let (data, response) = try await session.data(for: URLRequest(url: apiURL))Or call
try await client.accessToken()where you need the value. -
Observe with
for await state in await client.sessionStates() { ... }. -
Sign out with
await client.logout(), which returns aLogoutOutcome. -
Delete the archived
OIDAuthState(see What happens to existing users and tokens?).
How do I migrate from flutter_appauth?
Before (flutter_appauth)
final appAuth = FlutterAppAuth();
final result = await appAuth.authorizeAndExchangeCode(
AuthorizationTokenRequest(
'<client_id>',
'<redirect_url>',
issuer: '<issuer>',
scopes: ['openid', 'profile', 'offline_access'],
),
);
// You store result's tokens, and later refresh with
// appAuth.token(TokenRequest('<client_id>', '<redirect_url>', refreshToken: ..., ...)).
await appAuth.endSession(EndSessionRequest(
idTokenHint: '<idToken>',
postLogoutRedirectUrl: '<postLogoutRedirectUrl>',
issuer: '<issuer>',
));
After (versola_oauth)
-
Dependency. Replace
flutter_appauthinpubspec.yamlwithversola_oauth. The Flutter page covers the install. Remove the plugin’s own setup:appAuthRedirectSchemeon Android and theCFBundleURLTypescustom scheme on iOS, unless you keep a custom-scheme redirect. -
Native setup in the host app. Android: the
VersolaRedirectActivityintent filter. iOS: iOS 17.4 or newer, Associated Domains for anhttpsredirect, andVersolaOAuthPlugin.presentationAnchorif the app has its own window handling. See the Flutter page. -
Create and use:
final client = await OAuthClient.create(const OAuthConfiguration( issuer: 'https://id.example.com', clientId: 'example-app', redirectUri: 'https://app.example.com/oauth', postLogoutRedirectUri: 'https://app.example.com/signed-out', scopes: {'openid', 'profile'}, )); client.sessionStates().listen((state) => print(state)); try { await client.login(); final token = await client.accessToken(); // the only credential that crosses the bridge } on StepUpError catch (e) { await client.stepUp(e); } on ReauthenticateError { // sign in again } on RecoverableError { // retry, or ask the user again } on ConfigurationError { // a deployment mistake } -
Remove your token storage. Nothing credential-shaped is kept in Dart, so delete the code that stores and refreshes tokens.
accessToken()refreshes natively. -
Sign out with
client.logout(). It returns aLogoutOutcome. -
Errors.
FlutterAppAuthUserCancelledExceptionbecomesRecoverableError, and the platform exception becomes the matching category.
Dart has no authenticated HTTP client. Call your API with the value accessToken() returns, and call it again for each request or batch rather than caching it.
Migration checklist
- The provider client is public and has PKCE
S256; no secret is used anywhere. - The configured
issuerequals the discovery document’sissuermember. -
offline_accessis inscopesif the provider needs it for a refresh token (see Providers). - The redirect URI is registered with the provider exactly as configured, and for
httpsthe app’sassetlinks.jsonorapple-app-site-associationfile is served. -
postLogoutRedirectUriis registered with the provider. - No code reads, writes or sends a refresh token or a raw ID token.
- The old
AuthState(or archivedOIDAuthState, or Dart token storage) is deleted after the first successfullogin(). - Your sign-in screen handles the four error categories, and a
Configurationerror is not caught silently in production.
Sources
The AppAuth facts on this page were read on 2026-10-30. The SDK side comes from the SDK’s own API specification and public declarations.
- AppAuth-Android README: https://github.com/openid/AppAuth-Android. Used for configuration and discovery, the request and intents, the redirect (
appAuthRedirectScheme,RedirectUriReceiverActivity),AuthStatepersistence andperformActionWithFreshTokens,EndSessionRequest, client secrets and dynamic registration, browser allow and deny lists, the WebView statement, PKCE, the API levels,setNonce(null)andsetSkipIssuerHttpsCheck. - AppAuth-Android
IdToken.java, for the ID-token checks and the statement that the signature is not verified. - AppAuth-iOS README: https://github.com/openid/AppAuth-iOS and https://raw.githubusercontent.com/openid/AppAuth-iOS/master/README.md. Used for discovery,
OIDAuthorizationRequest,OIDAuthState.authState(byPresenting:presenting:),resumeExternalUserAgentFlow,performAction, user agents, WebView, PKCE, HTTPS redirects and thewebcredentialsrequirement, and OS floors. - AppAuth-iOS API index, for the class list (end session, registration, token request classes): https://openid.github.io/AppAuth-iOS/docs/latest/annotated.html, and for
OIDIDToken: https://openid.github.io/AppAuth-iOS/docs/latest/interface_o_i_d_i_d_token.html. flutter_appauthREADME: https://github.com/MaikuB/flutter_appauth and https://raw.githubusercontent.com/MaikuB/flutter_appauth/master/flutter_appauth/README.md.
Next steps
- Android, iOS and Flutter: set up the SDK on each platform.
- Session states and errors: the four error categories and the seven session states your sign-in screen handles.
- Server errors, 429 and Retry-After: what the SDK retries and what your app does with the rest.
- Providers: redirect URIs, scopes and sign-out for your provider, such as Keycloak.