Skip to content

AuthZ Client

SentinelAuthz is the browser auth client for authz mode. It manages dual tokens: an IdP token (identity, from Google/EntraID) and a Sentinel authz token (authorization).

Setup

import { SentinelAuthz, IdpConfigs } from '@sentinel-auth/js'

const authz = new SentinelAuthz({
  sentinelUrl: 'http://localhost:9003',
  idps: { google: IdpConfigs.google('your-google-client-id') },
})
Option Type Default Description
sentinelUrl string required Base URL of the Sentinel service
idps Record<string, IdpConfig> {} IdP configs keyed by provider name
redirectUri string ${origin}/auth/callback OAuth redirect URI
storage AuthzTokenStore AuthzMemoryStore Token storage backend
autoRefresh boolean true Refresh authz token before expiry
refreshBuffer number 30 Seconds before expiry to trigger refresh

Built-in IdP helpers: IdpConfigs.google(clientId), IdpConfigs.entraId(clientId, tenantId). Pass a custom IdpConfig object for other providers.

How it works

1. authz.login('google')              -> redirect to Google
2. Google redirects back with #id_token=...
3. authz.handleCallback()             -> extract token, verify nonce
4. authz.resolve(idpToken, provider)  -> POST /authz/resolve, get workspaces
5. authz.selectWorkspace(...)         -> POST /authz/resolve with workspace_id
6. Both tokens stored, auto-refresh scheduled

Methods

login(provider)

Redirect to the IdP's authorization page. Provider must be configured in idps.

authz.login('google')

handleCallback()

Extract id_token from URL hash after IdP redirect. Verifies nonce. Returns null if no hash present.

const cb = authz.handleCallback()
// { idpToken: 'eyJ...', provider: 'google' } | null

resolve(idpToken, provider)

Validate IdP token with Sentinel, discover workspaces.

const result = await authz.resolve(idpToken, 'google')
// result.user       -> { id, email, name }
// result.workspaces -> [{ id, name, slug, role }]

selectWorkspace(idpToken, provider, workspaceId)

Exchange IdP token for a Sentinel authz token scoped to a workspace.

await authz.selectWorkspace(idpToken, 'google', 'ws-uuid')

getUser() / isAuthenticated

const user = authz.getUser()
// { userId, email, name, workspaceId, workspaceSlug, workspaceRole, groups }
if (authz.isAuthenticated) { /* ... */ }

getHeaders()

authz.getHeaders()
// { Authorization: 'Bearer <idp_token>', 'X-Authz-Token': '<authz_token>' }

fetch / fetchJson

Inject dual-token headers. On 401, refresh and retry once.

const res = await authz.fetch('/api/notes')
const notes = await authz.fetchJson<Note[]>('/api/notes')

onAuthStateChange(cb) / logout() / destroy()

const unsub = authz.onAuthStateChange((user) => { /* ... */ })
authz.logout()   // clear tokens, notify listeners
authz.destroy()  // clean up timers

Token storage

Backend Persistence
AuthzMemoryStore (default) Lost on page refresh
AuthzLocalStorageStore Survives refresh, shared across tabs
import { SentinelAuthz, AuthzLocalStorageStore } from '@sentinel-auth/js'
const authz = new SentinelAuthz({
  sentinelUrl: '...', storage: new AuthzLocalStorageStore(),
})

Complete example

import { SentinelAuthz, IdpConfigs, AuthzLocalStorageStore } from '@sentinel-auth/js'

const authz = new SentinelAuthz({
  sentinelUrl: 'http://localhost:9003',
  idps: { google: IdpConfigs.google('your-client-id') },
  storage: new AuthzLocalStorageStore(),
})

// Login page
authz.login('google')

// Callback page (/auth/callback)
const cb = authz.handleCallback()
if (cb) {
  const result = await authz.resolve(cb.idpToken, cb.provider)
  if (result.workspaces?.length === 1) {
    await authz.selectWorkspace(cb.idpToken, cb.provider, result.workspaces[0].id)
    window.location.href = '/dashboard'
  }
}

// After auth
const notes = await authz.fetchJson<Note[]>('/api/notes')

AuthZ vs Proxy mode

AuthZ (SentinelAuthz) Proxy (SentinelAuth)
IdP interaction You configure IdPs, SDK redirects Sentinel manages redirect flow
Tokens stored IdP token + authz token Access + refresh token
Headers sent Authorization + X-Authz-Token Authorization only
PKCE Not needed (implicit flow) Generated by SDK