Skip to main content

ThunderIDClient

ThunderIDClient is the core authentication client in the ThunderID package. It manages the full authentication lifecycle: initialization, sign-in (both app-native and redirect-based), session, token management, and user profile operations.

When you use the ThunderIDSwiftUI package, a ThunderIDClient instance is created and managed automatically. You can access it via ThunderIDState.client from any view.

Usage

import ThunderID

let client = ThunderIDClient()

try await client.initialize(config: ThunderIDConfig(
baseUrl: "https://localhost:8090",
clientId: "<your-client-id>",
scopes: ["openid", "profile", "email"],
afterSignInUrl: "io.thunderid.b2c://callback",
afterSignOutUrl: "io.thunderid.b2c://logout"
))
note

When using ThunderIDSwiftUI, you do not need to create a ThunderIDClient directly. Apply .thunderIDProvider(config:) to your root view and access the client through @EnvironmentObject var state: ThunderIDState.


Initialization

initialize(config:storage:)

Initializes the SDK. You must call this before calling any other method.

func initialize(config: ThunderIDConfig, storage: StorageAdapter? = nil) async throws -> Bool
ParameterTypeRequiredDescription
configThunderIDConfigSDK configuration. See Configuration.
storageStorageAdapter?Custom token storage backend. Defaults to KeychainStorageAdapter.

Returns: true when initialization succeeds.

Throws: IAMError with code .alreadyInitialized if the SDK is already initialized, or .invalidConfiguration if baseUrl is empty or does not use HTTPS.


reInitialize(baseUrl:clientId:)

Updates the base URL or client ID and reinitializes the SDK without creating a new instance.

func reInitialize(baseUrl: String? = nil, clientId: String? = nil) async throws -> Bool

Throws: IAMError with code .sdkNotInitialized if initialize() has not been called first.


getConfiguration()

Returns the current SDK configuration.

func getConfiguration() throws -> ThunderIDConfig

Throws: IAMError with code .sdkNotInitialized if the SDK has not been initialized.


App-Native Authentication

These methods drive the Flow Execution API loop for embedded (in-app) sign-in without leaving your application.

signIn(payload:request:sessionId:)

Submits a step in the sign-in flow. Call this in a loop: first with an empty payload to initiate the flow, then with user inputs and the action ID to advance each step until flowStatus is .complete.

func signIn(
payload: EmbeddedSignInPayload,
request: EmbeddedFlowRequestConfig,
sessionId: String? = nil
) async throws -> EmbeddedFlowResponse
ParameterTypeRequiredDescription
payloadEmbeddedSignInPayloadFlow step payload. Set flowId to nil on the first call to initiate.
requestEmbeddedFlowRequestConfigFlow configuration including the application ID and flow type.
sessionIdString?Session identifier for multi-session support.

Returns: EmbeddedFlowResponse with flowStatus of .promptOnly (more steps needed), .complete (tokens issued), or .error.

note

The SignIn and SignUp SwiftUI components manage this loop for you. Use signIn(payload:request:sessionId:) directly only when building a fully custom sign-in UI.


signUp(payload:request:)

Submits a step in the registration flow. Same loop pattern as signIn.

func signUp(
payload: EmbeddedSignInPayload? = nil,
request: EmbeddedFlowRequestConfig? = nil
) async throws -> EmbeddedFlowResponse

Redirect-Based Authentication

These methods implement the OAuth 2.0 Authorization Code flow with PKCE for apps that redirect to a browser.

buildSignInURL(options:)

Builds the OAuth 2.0 authorization URL to open in ASWebAuthenticationSession or SFSafariViewController.

func buildSignInURL(options: SignInOptions? = nil) throws -> URL
ParameterTypeRequiredDescription
optionsSignInOptions?Optional sign-in hints: prompt, loginHint, fidp.

Returns: The authorization URL with PKCE code_challenge and code_challenge_method=S256 parameters.

Throws: IAMError with code .invalidConfiguration if clientId is not set.


handleRedirectCallback(url:)

Exchanges the authorization code from the callback URL for tokens and returns the signed-in user.

func handleRedirectCallback(url: URL) async throws -> User

Throws: IAMError with code .invalidGrant if the URL does not contain a valid authorization code, or if the PKCE verifier is missing (call buildSignInURL first).


Session

isSignedIn(sessionId:)

Returns true if an access token is present in the token store.

func isSignedIn(sessionId: String? = nil) async throws -> Bool

signOut(options:sessionId:)

Revokes the refresh token, clears the local session, and returns the post-logout redirect URL.

func signOut(options: SignOutOptions? = nil, sessionId: String? = nil) async throws -> String
ParameterTypeRequiredDescription
optionsSignOutOptions?Optional: idTokenHint for server-side logout.
sessionIdString?Session identifier for multi-session support.

Returns: The afterSignOutUrl from your configuration, or "/" if not set.


clearSession(sessionId:)

Clears the local session without revoking the refresh token on the server.

func clearSession(sessionId: String? = nil)

isLoading()

Returns true while a sign-in or sign-out operation is in progress.

func isLoading() -> Bool

Token Management

getAccessToken(sessionId:)

Returns the current access token, automatically refreshing it if expired.

func getAccessToken(sessionId: String? = nil) async throws -> String

Throws: IAMError with code .sessionExpired if the refresh token is also expired.


decodeJwtToken(_:)

Decodes a JWT string into any Decodable type.

func decodeJwtToken<R: Decodable>(_ token: String) throws -> R

Throws: IAMError with code .invalidInput if the token is not a valid JWT.

Example: decode access token claims into a dictionary:

let claims: [String: AnyCodable] = try client.decodeJwtToken(accessToken)
let sub = claims["sub"]?.value as? String

exchangeToken(config:sessionId:)

Performs an OAuth 2.0 token exchange (RFC 8693). Useful for Security Token Service (STS) scenarios.

func exchangeToken(
config: TokenExchangeRequestConfig,
sessionId: String? = nil
) async throws -> TokenResponse

User and Profile

getUser(options:)

Returns the authenticated user. Reads claims from the access token JWT if available; otherwise calls /oauth2/userinfo.

func getUser(options: [String: Any]? = nil) async throws -> User

Returns: A User with sub, username, email, displayName, profilePicture, and raw claims.


getUserProfile(options:)

Fetches the full user profile from /scim2/Me.

func getUserProfile(options: [String: Any]? = nil) async throws -> UserProfile

Returns: A UserProfile with id and a claims dictionary containing all SCIM attributes.


updateUserProfile(payload:userId:)

Updates the user profile via /scim2/Me (or /scim2/Users/<userId> if userId is provided).

func updateUserProfile(payload: [String: Any], userId: String? = nil) async throws -> User

Error Handling

All throwing methods raise an IAMError on failure. Catch it to handle errors:

do {
let user = try await client.getUser()
} catch let error as IAMError {
print("[\(error.code.rawValue)] \(error.message)")
} catch {
print("Unexpected error: \(error)")
}

Error Codes

CodeValueDescription
.sdkNotInitializedSDK_NOT_INITIALIZEDA method was called before initialize()
.alreadyInitializedALREADY_INITIALIZEDinitialize() was called more than once
.invalidConfigurationINVALID_CONFIGURATIONMissing or invalid configuration value
.invalidRedirectUriINVALID_REDIRECT_URIThe redirect URI is not registered
.authenticationFailedAUTHENTICATION_FAILEDCredentials are incorrect
.userAccountLockedUSER_ACCOUNT_LOCKEDThe user account is locked
.userAccountDisabledUSER_ACCOUNT_DISABLEDThe user account is disabled
.sessionExpiredSESSION_EXPIREDThe session has expired and cannot be refreshed
.mfaRequiredMFA_REQUIREDMulti-factor authentication is required
.mfaFailedMFA_FAILEDMulti-factor authentication failed
.invalidGrantINVALID_GRANTThe authorization code or token is invalid
.consentRequiredCONSENT_REQUIREDUser consent is required
.userAlreadyExistsUSER_ALREADY_EXISTSRegistration failed because the user already exists
.invalidInputINVALID_INPUTInput validation failed
.invitationCodeInvalidINVITATION_CODE_INVALIDThe invitation code is not valid
.invitationCodeExpiredINVITATION_CODE_EXPIREDThe invitation code has expired
.registrationDisabledREGISTRATION_DISABLEDRegistration is disabled for this application
.recoveryFailedRECOVERY_FAILEDPassword recovery failed
.confirmationCodeInvalidCONFIRMATION_CODE_INVALIDThe confirmation code is not valid
.confirmationCodeExpiredCONFIRMATION_CODE_EXPIREDThe confirmation code has expired
.networkErrorNETWORK_ERRORA network request failed
.requestTimeoutREQUEST_TIMEOUTThe request timed out
.serverErrorSERVER_ERRORThe server returned an error response
.unknownErrorUNKNOWN_ERRORAn unexpected error occurred
ThunderID LogoThunderID Logo

Product

DocsAPIsSDKs
© WSO2 LLC. All rights reserved.Privacy PolicyCookie Policy