Authentication
Every call MeroKit makes carries a JWT access token. There are two ways to obtain one — direct credentials and hosted SSO — and the SDK keeps it fresh for you.
Direct credentials
Section titled “Direct credentials”For first-party apps, exchange a username and password for a token bundle.
bootstrapSecret is only needed for the very first login on a fresh node.
let tokens = try await mero.authenticate( Credentials(username: "alice", password: "s3cr3t", bootstrapSecret: "…") // only on a fresh node's first login)let authed = await mero.isAuthenticated // trueYou can also put credentials in the config and call authenticate() with no
argument:
let mero = Mero(config: MeroConfig( baseURL: URL(string: "https://your-node.example")!, credentials: Credentials(username: "alice", password: "s3cr3t")))try await mero.authenticate() // uses config.credentialsThe token lifecycle
Section titled “The token lifecycle”Refresh is reactive: the SDK never refreshes proactively (the server rejects
a refresh while the access token is still valid). A 401 with
x-auth-error: token_expired drives a single refresh and one retry — invisible
to the caller.
Because Mero is an actor, concurrent requests that all hit a 401 share
one in-flight refresh. And because refresh tokens are single-use
(rotated on every refresh), the SDK re-reads the token store first — so if
another process or app extension already rotated the token, that newer bundle is
adopted instead of replaying a consumed one (which would revoke the whole token
family).
Hosted SSO (deep link)
Section titled “Hosted SSO (deep link)”For hosted login, build the node’s login URL, present it in
ASWebAuthenticationSession, and feed the callback URL back in — the native
analog of the web redirect flow.
import AuthenticationServices
// 1. Build the login URL.let loginURL = Mero.buildAuthLoginUrl( nodeUrl: "https://your-node.example", options: AuthLoginOptions(callbackUrl: "myapp://auth-callback", mode: "login", permissions: ["admin"]))
// 2. Present it (ASWebAuthenticationSession), then on the callback URL:if let callback = Mero.parseAuthCallback(callbackURL.absoluteString) { await mero.setTokenData(from: callback) // callback also carries applicationId, contextId, contextIdentity, nodeUrl}parseAuthCallback reads the URL hash fragment
(access_token, refresh_token, application_id, context_id,
context_identity, node_url) and returns nil if no access token is present.
Both SSO helpers are static on Mero.
Persistence
Section titled “Persistence”Pass a tokenStore in the config so tokens survive app launches. The Keychain
store is the secure default; the memory store is ephemeral.
// Secure, persisted across launches — backed by the iOS/macOS Keychainlet mero = Mero(config: MeroConfig( baseURL: URL(string: "https://your-node.example")!, tokenStore: KeychainTokenStore()))
// Share tokens with an app extension via a Keychain access group:let shared = KeychainTokenStore(accessGroup: "TEAMID.group.myapp")
// Ephemeral (the default when tokenStore is nil)let ephemeral = Mero(config: MeroConfig( baseURL: URL(string: "https://your-node.example")!, tokenStore: MemoryTokenStore()))Conform to TokenStore (getTokens / setTokens / clear) to back tokens with
anything else — see the reference.
Inspecting and clearing auth state
Section titled “Inspecting and clearing auth state”await mero.isAuthenticated // Bool — is a bundle present?await mero.currentTokenData() // TokenData? (debugging / handoff)await mero.clearToken() // drop the bundle + clear the storeawait mero.logout() // clearToken() + close() — full sign-out