@stackonward/identity-browser
v0.0.2
Published
Framework-neutral browser client for opaque identity sessions
Maintainers
Readme
@stackonward/identity-browser
Framework-neutral browser client for a same-origin opaque identity-session contract. It validates server projections, owns CSRF synchronization, exposes a subscribable session store, and converts WebAuthn challenges into browser API requests.
The browser never receives access tokens or refresh tokens through this API.
Install
pnpm add @stackonward/identity-browser @stackonward/identity-sessionQuick start
import { IdentityBrowserClient } from "@stackonward/identity-browser";
const identity = new IdentityBrowserClient({ basePath: "/api/onex" });
const unsubscribe = identity.store.subscribe((session) => {
renderSession(session);
});
const session = await identity.getSession();
await identity.login({ email, password });basePath must be a relative same-origin path. Requests always use
credentials: "include" and manual redirect handling.
Operations
| Area | Methods |
| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| Session | getSession, logout |
| Registration and login | register, login, loginWithOAuth |
| MFA and WebAuthn | verifyMfa, beginWebAuthn, finishWebAuthn |
| Guest identity | createGuest, requestGuestActivation |
| Verification and recovery | verifyEmail, resendVerification, requestPasswordRecovery, verifyPasswordRecoveryCode, exchangePasswordRecoveryLink, resetPassword |
| Account | changePassword, updateProfile, requestAccountDeletion, cancelAccountDeletion |
Each mutation first synchronizes the server-authoritative session, extracts its CSRF token, and then sends the command. Mutations are never replayed automatically.
WebAuthn
import {
isWebAuthnAssertionSupported,
requestWebAuthnAssertion,
} from "@stackonward/identity-browser";
if (isWebAuthnAssertionSupported()) {
const challenge = await identity.beginWebAuthn();
const action = challenge.next_action;
if (action?.type === "webauthn" && action.public_key_credential_request_options) {
const assertion = await requestWebAuthnAssertion(
action.public_key_credential_request_options,
navigator.credentials,
);
await identity.finishWebAuthn(assertion);
}
}The WebAuthn helpers validate request options, decode base64url fields, call
the Credentials API, and serialize the assertion. Failures are classified by
WebAuthnAssertionError as unsupported, invalid options, credential-request
failure, or invalid credential.
Coordination and lifecycle
- Concurrent session operations share one exclusive coordinator.
- Browsers use the Web Locks API so same-origin contexts do not interleave a session read and mutation.
- Non-browser test runtimes use a process-local coordinator.
- Request waits are bounded from 500 to 30,000 milliseconds; the default is 10,000.
IdentitySessionStore.loaddeduplicates concurrent loads, andsubscribereturns an unsubscribe function.- Call the returned unsubscribe function when the owning UI lifecycle ends.
Browser runtimes fail closed when the Web Locks API is unavailable. There is no uncoordinated mutation fallback.
Errors and session invalidation
IdentityBrowserError exposes type, code, HTTP status, optional param,
field errors, and decoded retry delay. Responses must use the declared JSON
media type and exact identity projection shape.
A canonical HTTP 401 authentication_error clears the in-memory projection
immediately. Transient upstream failures preserve the last projection and do
not trigger mutation replay. logout clears the local store even if the remote
operation fails.
Public API
The main entry exports IdentityBrowserClient, IdentitySessionStore,
IdentityBrowserError, challenge-selection helpers, WebAuthn helpers, and the
browser-safe contracts re-exported from @stackonward/identity-session.
Compatibility
- Node.js 20 or newer for the package runtime contract
- Modern browsers with Fetch, Web Locks, WebAuthn, and Credentials APIs for the corresponding operations
- ESM with TypeScript declarations
Related packages
@stackonward/identity-sessionowns shared contracts and the server engine.@stackonward/onex-identity-nuxtprovides the complete OneX Nuxt BFF integration.
