feat(auth): cross-platform OIDC client package #16
No reviewers
Labels
No labels
app/archiv
app/einkaufslisten
app/imap-client
app/wissensbasis
arch-answered
arch-question
area/api
area/auth
area/infra
area/mobile
area/shared
area/ui
area/web
portfolio-status
prio/high
prio/low
prio/medium
roadmap/public
size/l
size/m
size/s
size/xl
size/xs
status/blocked
status/needs-info
type/bug
type/chore
type/docs
type/feature
type/idea
type/refactor
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
admin-mrrm/mrrmlabapp!16
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/auth-package"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
@mrrmlab/auth— OAuth2 + authorization-code-with-PKCE flow against any OIDC issuer (target: Keycloak)fetch,globalThis.crypto, andjose(already used in the API guard). Nooidc-client-ts, noexpo-auth-session.@mrrmlab/api-clientviagetAccessToken()Modules
pkce.ts— RFC 7636 verifier + S256 challengediscovery.ts—/.well-known/openid-configurationfetch + Zod validationoidc.ts— auth/logout URL builders, token endpoint client (exchange + refresh)id-token.ts—verifyIdTokenviajosewith cached JWKS, claims projectiontoken-store.ts—TokenStoreinterface,InMemoryTokenStore,WebStorageTokenStoreauth-client.ts—AuthClientorchestrator:startLogin,handleCallback,getAccessToken(auto-refresh + skew),getUser,logout,onChangeDesign notes worth surfacing
getAccessToken()callers share one network round-trip via a memoized promise.refresh_tokenfrom the response, falls back to the previous one (Keycloak rotates, the spec doesn't require it).id_token.nonceis verified against the value persisted in the pending login.SecureTokenStore(Expo SecureStore) and drive the redirect withexpo-web-browser. README has the recipe.Test plan
pnpm --filter @mrrmlab/auth buildsucceedsCloses #8
New workspace package @mrrmlab/auth implementing the OAuth2 + authorization-code-with-PKCE flow against any OIDC issuer (target: Keycloak). Same code path runs in Vite, Expo, and Node SSR — only platform primitives are used (fetch, globalThis.crypto, jose for ID token verification). No oidc-client-ts, no expo-auth-session. Modules: - pkce.ts — RFC 7636 verifier + S256 challenge - discovery.ts — fetches and validates /.well-known/openid-configuration - oidc.ts — auth/logout URL builders, token endpoint client (exchange + refresh), TokenEndpointError - id-token.ts — verifyIdToken via jose with cached JWKS, toAuthUser claims projection - token-store.ts — TokenStore interface, InMemoryTokenStore, WebStorageTokenStore (sessionStorage/localStorage) - auth-client.ts — AuthClient orchestrator: startLogin, handleCallback, getAccessToken (with auto-refresh + skew), isAuthenticated, getUser, logout, onChange listeners - types.ts — Zod schemas + inferred types Integration story: AuthClient.getAccessToken() bridges directly into @mrrmlab/api-client.createApiClient({ getToken }), so the rest of the codebase never touches the token store. A couple of intentional design choices: - Logout always succeeds locally even if the issuer is unreachable — we don't want a flaky network to leave the user "still signed in" on the device. RP-Initiated Logout URL is best-effort. - Single in-flight refresh promise so simultaneous getAccessToken() callers share one network round-trip. - Refresh-token rotation: new refresh_token from the response is preferred, but we fall back to the previous one if the issuer didn't rotate (Keycloak does, but the spec doesn't require it). - ID-token nonce verified against the value persisted in the pending login — closes the OIDC replay window. Build setup matches shared-types and api-client (CJS dist, tsBuildInfoFile inside dist/). Smoke-tested locally (27 checks): PKCE primitives, URL builders for auth/logout, store round-trip, expired-token clears state, listener fires on logout. Full network integration deferred until a Keycloak client is configured for the web/mobile apps in #11/#12. Closes #8