npm.io
0.1.1 • Published 3d ago

@prefig/upact-oidc

Licence
Apache-2.0
Version
0.1.1
Deps
0
Size
77 kB
Vulns
0
Weekly
0

@prefig/upact-oidc

OpenID Connect adapter for the upact identity port. Works with any IDP that supports OpenID Connect Discovery: Dex, Authentik, Keycloak, ZITADEL, Google.

Install

npm install @prefig/upact-oidc @prefig/upact openid-client

Usage

import { createOidcAdapter } from '@prefig/upact-oidc';

// Initialise once per request (SvelteKit example)
const adapter = createOidcAdapter(
  {
    issuer: 'https://accounts.example.com',
    clientId: process.env.OIDC_CLIENT_ID,
    clientSecret: process.env.OIDC_CLIENT_SECRET,
    redirectUri: 'https://app.example.com/auth/callback',
    cookieKey: process.env.COOKIE_KEY, // 64 hex chars (32 bytes)
  },
  event.cookies, // SvelteKit Cookies, or any CookieJar implementation
);
Init phase (authorization redirect)
// In your /auth/login route handler:
const url = await adapter.buildAuthRedirect();
throw redirect(302, url.toString());
Callback phase
// In your /auth/callback route handler:
const result = await adapter.authenticate({
  kind: 'oidc-callback',
  request: event.request,
});
if ('code' in result) {
  // AuthError: redirect to error page
  throw redirect(302, `/login?error=${result.code}`);
}
// result is a Session; store or use as needed
Reading the current identity
const upactor = await adapter.currentUpactor(event.request);
if (!upactor) {
  throw redirect(302, '/login');
}
// upactor.id: deterministic, privacy-preserving identifier
// upactor.lifecycle.expires_at: token expiry
// upactor.provenance.instance: issuer URL
Logout
const url = await adapter.buildLogoutRedirect('https://app.example.com/');
throw redirect(302, url.toString());

Scope policy

email, phone, address, and groups are forbidden: requesting them violates upact §7 privacy minima and throws immediately at adapter construction. Default scopes: openid, offline_access. Add profile to receive a display_hint (preferred_username or name, email-shaped values rejected).

createOidcAdapter({ ..., scopes: ['openid', 'offline_access', 'profile'] }, cookies);

Configuration

Field Required Description
issuer yes OIDC issuer URL (discovery endpoint root)
clientId yes OAuth2 client ID
clientSecret yes OAuth2 client secret
redirectUri yes Callback URI; must match IDP registration
cookieKey yes 64-char hex (32 bytes) for HMAC-SHA256 cookie signing
scopes no Default: ['openid', 'offline_access']
sessionCookieName no Default: '__upact_oidc'
stateCookieName no Default: '__upact_oidc_state'
allowInsecureRequests no Allow HTTP issuers (local development only)

Generate a cookie key:

node -e "console.log(Buffer.from(crypto.getRandomValues(new Uint8Array(32))).toString('hex'))"

Identifier derivation

Upactor.id is the first 32 hex characters of SHA-256("${sub}@${iss}"): deterministic per identity, not reversible from the id alone, and stable across token refreshes.

Conformance

See CONFORMANCE.md for the full conformance statement against upact v0.1.1.

Integration testing

The integration suite expects a local Dex IDP. The maintainers run it from the upact-oidc-poc compose rig; any OIDC-discovery-compliant IDP on the configured issuer works:

npm run test:integration

Status

v0.1.1. Breaking changes between v0.x revisions are permitted; v1.0 marks the first stable version.

Licence

Apache-2.0.

Keywords