SeleID

Reference

API reference

Overview · React Native and Expo · Android · iOS · Register an app · API reference · Errors and security · Changelog

Coming soon @seletech/seleid-client is not on npm yet. These pages describe version 0.1.0 so you can plan an integration. See the roadmap.

Package @seletech/seleid-client 0.1.0. Exports ./ and ./testing. No side effects at import time, no secrets, no node: or Expo imports.

SeleIdClient

new SeleIdClient(options?: SeleIdClientOptions)
OptionDefaultMeaning
baseUrlhttps://auth.seleid.devSeleID origin
fetchglobalThis.fetch (looked up lazily)Inject for tests or React Native
timeoutMs4000Hard timeout per attempt (timer race, so a fetch that ignores signal still cannot hang)
retries2Retries for idempotent calls only, exponential backoff with jitter
budgetMs10000Total time for one logical operation
contextTimeoutMs3000Timeout and budget for sessionContext
useDiscoverytrueRead endpoints from the discovery document (origin-checked)
expectedIssuerhttps://auth.seleid.devIssuer the verifier requires
onEventnoneTelemetry callback

Methods

MethodReturnsNotes
discovery()OpenIdConfigurationCached; issuer and endpoints must belong to the configured origin
jwks()Jwk[]
createVerifier({ audience, issuer?, clockSkewSeconds?, crypto?, staleIfErrorMs? })AccessTokenVerifier with .keysRS256, typ at+jwt, checks iss, aud, exp, iat, sv; 60 s skew; JWKS cache 300 s, unknown kid refetches once, at most one refetch per 60 s. Planned server side.
exchangeCode({ clientId, code, redirectUri, codeVerifier })TokenSetNever retried. A rejected code is unauthenticated, not revoked
refresh(clientId, refreshToken)TokenSetNever retried (tokens rotate). Prefer TokenManager
sessionContext(accessToken)SessionContextGET /v1/session/context, 3 s total, one in-flight call per token. Planned server side.
listSessions(accessToken)DeviceSession[]GET /v1/account/sessions. Planned server side.
revokeSession(accessToken, id), revokeAllSessions(accessToken, { keepCurrent? })RevokeResultNever retried. Planned server side.
getPreferences(accessToken), patchPreferences(accessToken, patch)AccountPreferencesPatch accepts marketing, product, selerank and ifVersion. Security notices cannot be turned off. Planned server side.
listEvents(accessToken, { after?, limit? })SeleIdAccountEvent[]Limit 1 to 100. Planned server side.

Token endpoint note: the SDK accepts the camelCase body SeleID returns today and the standard snake_case body planned for the future.

TokenManager

new TokenManager({ client, store, clientId, refreshSkewMs = 30000 })
await tokens.setTokens(tokenSet)
await tokens.getTokens()              // TokenSet | null
await tokens.getAccessToken({ forceRefresh?, correlationId? })
await tokens.clear()

Concurrent callers share one refresh (single flight). The rotated refresh token is written to the store before anyone uses it. A timeout, network error or outage keeps the tokens. A definitive refusal (revoked, disabled, deleted) clears them. An unreadable stored blob is cleared and treated as signed out.

SessionGuard

new SessionGuard({ client, tokens, verifier })
const { claims, context, source } = await guard.check({
  sensitive?: boolean,   // online context required, answer at most 30 s old, fails closed
  withContext?: boolean, // ask for context but allow a recent good answer on outage
  correlationId?: string,
})

source is local, online, cached or last_good. For a sensitive check the context must be active and its generation must equal the token’s sgen; anything else is revoked. last_good reuses a recent active answer for at most min(validFor, 900) seconds, only for non-sensitive checks.

Token storage ports

interface TokenStore { get(): Promise<string | null>; set(value: string): Promise<void>; clear(): Promise<void>; }
new MemoryTokenStore()                         // tests, short-lived processes
new SecureStoreTokenStore(SecureStore, key?)   // expo-secure-store shape; key defaults to 'seleid.tokens.v1'

Types

interface TokenSet { accessToken: string; refreshToken: string; idToken: string; scope: string; expiresAt: number /* ms epoch */ }
interface AccessTokenClaims { iss; sub; aud: string | string[]; iat; exp; jti; scope; client_id; sgen: number; sv: number }
interface SessionContext {
  schemaVersion: 1; subjectId: string; kind: 'person' | 'page_host';
  flags: { emailConfirmed; passwordRecoveryPending; minor; staff: boolean };
  verification: { level: 'none' | 'email' | 'identity' | 'official'; updatedAt: string | null };
  consent: { marketing; selerankPersonalization; selerankCrossApp: boolean; version: number };
  generation: number;
  status: 'active' | 'recovering' | 'suspended' | 'banned' | 'disabled' | 'deleted';
  pageHost?: { pageId: string; role: 'owner' | 'admin' | 'editor' | 'analyst' };
  issuedAt: string; validFor: number /* seconds */;
}

Telemetry

onEvent receives { type, op, correlationId, attempt, durationMs, errorKind?, code? } where type is start, retry, success, failure, timeout or stuck. The shape is closed on purpose: tokens, emails, subject ids and response bodies cannot appear in it.

Testing helpers

@seletech/seleid-client/testing exports FakeSeleId (create, fetch, rotateKey, issueAccessToken, issueRefreshToken, revokeRefreshToken, bumpGeneration, setStatus, failNext, count, plus hang, delayMs and ignoreAbort to simulate slow servers).

Last updated 2026-11-12Suggest an editReport a problem with this page