@etranzact/e-identity-core
v1.0.3
Published
Core PKCE, state, token storage, and API client for the e-identity SDK
Readme
@etranzact/e-identity-core
Core primitives for the e-identity SDK — PKCE helpers, state/nonce generation, token storage, JWT parsing, and the full API client. Framework-agnostic; works in browsers, Node.js, and React Native.
This package is consumed by @etranzact/e-identity-react, @etranzact/e-identity-nextjs, and @etranzact/e-identity-react-native. You only need to install it directly if you are building a custom integration.
Installation
npm install @etranzact/e-identity-coreNo peer dependencies. Requires an environment with the Web Crypto API (crypto.subtle, crypto.getRandomValues) — available natively in browsers, Node 19+, and React Native via react-native-get-random-values.
Quick Start
import {
generateVerifier, generateChallenge, generateState, generateNonce,
EIdentityApiClient,
parseUser, isTokenExpired,
createTokenStorage,
} from '@etranzact/e-identity-core';
// Build a PKCE authorization URL
const verifier = generateVerifier();
const challenge = await generateChallenge(verifier);
const state = generateState();
const nonce = generateNonce();
const params = new URLSearchParams({
response_type: 'code',
client_id: 'your-app-uuid',
redirect_uri: 'https://app.example.com/auth/callback',
scope: 'openid profile email',
state, nonce,
code_challenge: challenge,
code_challenge_method: 'S256',
});
window.location.href = `https://id.example.com/identity/authorize?${params}`;
// Exchange the code after redirect
const client = new EIdentityApiClient({ issuerUrl: 'https://id.example.com' });
const tokens = await client.exchangeCode({
clientId: 'your-app-uuid',
code: 'auth-code-from-callback',
codeVerifier: verifier,
redirectUri: 'https://app.example.com/auth/callback',
});
const user = parseUser(tokens.accessToken);
console.log(user.sub, user.emailAddress);PKCE Utilities
import { generateVerifier, generateChallenge, base64UrlEncode } from '@etranzact/e-identity-core';| Function | Signature | Description |
|---|---|---|
| generateVerifier | (length?: number) => string | Cryptographically random code verifier (default 64 chars, URL-safe) |
| generateChallenge | (verifier: string) => Promise<string> | SHA-256 hash of the verifier, base64url-encoded (S256 method) |
| base64UrlEncode | (buffer: Uint8Array) => string | Base64url encoding without padding |
State & Nonce Utilities
import { generateState, generateNonce, validateState } from '@etranzact/e-identity-core';| Function | Signature | Description |
|---|---|---|
| generateState | () => string | 32-byte random base64url string for CSRF protection |
| generateNonce | () => string | 16-byte random base64url string for replay protection |
| validateState | (expected: string, received: string) => boolean | Constant-time equality check for state parameter |
Token Storage
import { createTokenStorage, MemoryTokenStorage, SessionStorageTokenStorage } from '@etranzact/e-identity-core';
import type { TokenStorage } from '@etranzact/e-identity-core';| Class / Function | Description |
|---|---|
| MemoryTokenStorage | In-process storage; tokens lost on page reload. Safest option. |
| SessionStorageTokenStorage | sessionStorage-backed; survives page refresh within the same tab. |
| createTokenStorage(type) | Factory: 'memory' → MemoryTokenStorage, 'sessionStorage' → SessionStorageTokenStorage |
All storage classes implement the TokenStorage interface:
interface TokenStorage {
getTokens(): TokenSet | null;
setTokens(tokens: TokenSet): void;
clearTokens(): void;
}Token Parser
import { parseJwtPayload, parseUser, isTokenExpired } from '@etranzact/e-identity-core';| Function | Signature | Description |
|---|---|---|
| parseJwtPayload | (token: string) => Record<string, unknown> | Decode JWT payload without verification (client-side only) |
| parseUser | (accessToken: string) => User | Parse the access token and cast claims to the User type |
| isTokenExpired | (expiresAt: number, bufferSeconds?: number) => boolean | Returns true when now >= expiresAt - buffer (default buffer 30 s) |
EIdentityApiClient
import { EIdentityApiClient } from '@etranzact/e-identity-core';
const client = new EIdentityApiClient({ issuerUrl: 'https://id.example.com' });OAuth2 / Token Methods
| Method | Description |
|---|---|
| getLoginConfig(appId) | Fetch branding and allowed login methods for an application |
| exchangeCode({ clientId, code, codeVerifier, redirectUri }) | Authorization Code + PKCE token exchange |
| refreshTokens({ clientId, refreshToken }) | Refresh access token using a refresh token |
| revokeToken({ clientId, token, tokenTypeHint? }) | Revoke an access or refresh token |
MFA Challenge (public — no access token required)
These are called during the login flow when the server returns a challengeToken instead of tokens.
| Method | Description |
|---|---|
| verifyTotpChallenge({ challengeToken, code }) | Verify a 6-digit TOTP code |
| verifyOtpChallenge({ challengeToken, code }) | Verify an email/SMS OTP code |
| resendOtpChallenge({ challengeToken }) | Resend OTP; returns { maskedDestination } |
| verifyRecoveryCode({ challengeToken, recoveryCode }) | Use a backup recovery code |
| getPasskeyChallengeOptions({ challengeToken }) | Fetch WebAuthn get() options JSON |
| verifyPasskeyChallenge({ challengeToken, responseJson }) | Submit WebAuthn assertion response |
All challenge methods return a TokenSet on success (promoting the session to AAL2), or throw an Error with the server's error code as the message (e.g. INVALID_MFA_CODE, CHALLENGE_TOKEN_INVALID).
MFA Enrollment (authenticated — requires access token)
| Method | Description |
|---|---|
| enrollTotpOptions(accessToken) | Start TOTP enrollment; returns { setupToken, qrCodeDataUri, secret } |
| confirmTotpEnrollment(accessToken, { setupToken, code }) | Confirm TOTP with a valid code; returns { recoveryCodes } |
| enrollPasskeyOptions(accessToken) | Start passkey registration; returns { setupToken, optionsJson } |
| confirmPasskeyEnrollment(accessToken, { setupToken, responseJson }) | Submit WebAuthn registration response |
MFA Management (authenticated)
| Method | Description |
|---|---|
| listMfaMethods(accessToken) | List enrolled MFA methods with status and recovery code counts |
| regenerateRecoveryCodes(accessToken) | Generate a new set of recovery codes (invalidates the old set) |
Types
import type {
EIdentityConfig,
TokenSet,
User,
AcrValue,
MfaMethodType,
MfaMethodSummary,
LoginConfig,
MosResponse,
AuthorizationParams,
} from '@etranzact/e-identity-core';EIdentityConfig
| Field | Type | Description |
|---|---|---|
| issuerUrl | string | Base URL of the e-identity server |
| appId | string | Application UUID (appAId) |
| redirectUri | string | Registered OAuth2 callback URL |
| scopes? | string[] | Defaults to ['openid', 'profile', 'email'] |
| tokenStorage? | 'memory' \| 'sessionStorage' | Storage backend for tokens |
TokenSet
| Field | Type | Description |
|---|---|---|
| accessToken | string | JWT access token |
| idToken? | string | OIDC ID token (present on initial exchange) |
| refreshToken? | string | Refresh token (offline_access scope required) |
| expiresIn | number | Lifetime in seconds |
| expiresAt | number | Absolute Unix timestamp (seconds) — use with isTokenExpired |
| tokenType | string | Always 'Bearer' |
| scope? | string | Granted scopes |
User
Key claims parsed from the access token. Additional custom claims are available via the index signature [claim: string]: unknown.
| Claim | Type | Description |
|---|---|---|
| sub | string | Subject identifier |
| userId? | number | Internal user ID |
| emailAddress? | string | User's email |
| phoneNumber? | string | User's phone |
| twoFaEnabledAuth? | number | 1 when MFA is enabled on the account |
| mfa_verified? | boolean | true on AAL2 tokens (MFA completed this session) |
| forcePasswordChange? | number | 1 when the user must change their password |
| acr? | string | Authentication Context Reference ('aal1' or 'aal2') |
| amr? | string[] | Authentication Method References (e.g. ['pwd', 'totp', 'mfa']) |
| pb_roles? | Record<string, Record<string, string>> | Product-scoped RBAC roles |
| products? | string[] | Products the user has access to |
MfaMethodSummary
Returned by listMfaMethods:
interface MfaMethodSummary {
method: 'TOTP' | 'EMAIL_OTP' | 'SMS_OTP' | 'PASSKEY';
active: boolean;
enrolledAt: string | null;
recoveryCodesRemaining: number | null;
}Requirements
- Web Crypto API (
crypto.subtle,crypto.getRandomValues) - Browser, Node.js 19+, or React Native with
react-native-get-random-values
