@cueplusplus/id
v0.1.0
Published
CUE++ ID — runtime-agnostic OIDC core (jose-based).
Readme
@cueplusplus/id
Runtime-agnostic OIDC core for CUE++ ID —
the identity provider behind id.cueplusplus.com. It builds authorization URLs,
exchanges and refreshes authorization codes, verifies ID and access tokens with
jose, and seals/unseals the session payload.
No Node built-ins, no framework: it runs in Node, edge runtimes and workers.
Most apps should use @cueplusplus/id-nextjs
(Next.js) or @cueplusplus/id-react
(React client) instead — both are built on this package.
Install
pnpm add @cueplusplus/id josejose is a peer dependency (^5.9.0 || ^6.0.0), declared the same way by
@cueplusplus/id-react and @cueplusplus/id-nextjs — every package whose dist
loads it says so.
Usage
import { createCueIdClient, generateState, generateNonce, generateCodeVerifier } from '@cueplusplus/id'
const issuer = 'https://<project-ref>.supabase.co/auth/v1'
const cue = createCueIdClient({
issuer,
clientId: process.env.CUE_ID_CLIENT_ID!,
clientSecret: process.env.CUE_ID_CLIENT_SECRET!,
redirectUri: 'https://app.example.com/api/auth/callback',
scopes: ['openid', 'profile', 'email'],
jwksUri: `${issuer}/.well-known/jwks.json`,
// Optional. How a CONFIDENTIAL client authenticates at the token endpoint.
// Default: 'client_secret_basic' — what RFC 6749
// §2.3.1 says clients SHOULD use and servers MUST support, and what the
// CUE++ ID client registration uses by default. Set 'client_secret_post'
// only if the client is registered with that method; a mismatch fails
// `invalid_client` on the primary sign-in path.
tokenEndpointAuthMethod: 'client_secret_basic',
// Optional. TOTAL budget for one operation in ms, default 5000. An operation
// makes at most two sequential network calls — the token endpoint, then the
// JWKS fetch that verifies what it returned — and each leg gets half, so the
// operation as a whole never exceeds this. Sized to leave a retry inside
// GoTrue's 10s refresh-token reuse interval (`GOTRUE_REUSE_INTERVAL_MS`).
timeoutMs: 5000,
})
// 1. Authorization request (persist state / nonce / verifier in a short-lived cookie).
const state = generateState()
const nonce = generateNonce()
const verifier = generateCodeVerifier()
const codeChallenge = await cue.oauth.generateCodeChallenge(verifier)
const { data: url } = await cue.oauth.buildAuthorizationUrl({ state, nonce, codeChallenge })
// 2. Callback — exchange the code, with the flow nonce bound into id_token verification.
// data is { tokens, claims } — claims are already id_token-verified against the flow nonce.
const { data, error } = await cue.oauth.exchangeCode({ code, codeVerifier: verifier, nonce })
// 3. Verify a token you received elsewhere.
const { data: claims } = await cue.tokens.verifyIdToken(idToken, { nonce })Every async call returns { data, error } — error is a CueIdError
discriminated on kind: 'transient' | 'terminal' (see classifyError,
isTransientError, isTerminalError).
There is no oauth.revoke() — sign-out is local
Supabase's OAuth server has no token-revocation endpoint. Probed against
vvkdlkbortbmmoujwzbz on 2026-08-19: POST {issuer}/oauth/revoke returns 404
with the body 404 page not found, byte-identical to a path invented on the
spot (/oauth/definitely-not-a-real-endpoint-9f3a), while endpoints that do
exist answer specifically — /oauth/token 400, /oauth/userinfo 405,
/user/oauth/grants 401. Discovery advertises no revocation_endpoint.
This package used to ship cue.oauth.revoke({ refreshToken }) implementing
RFC 7009 against that URL. It has been removed, rather than left in place
returning an error: a method whose only possible outcome is a 404 misrepresents
the provider, and downstream it made x-cue-id-revoke: failed fire on every
single sign-out — an alarm that is always on is not an alarm.
So sign-out is local. @cueplusplus/id-nextjs's POST /sign-out clears the
sealed session cookie and makes no upstream call. The refresh token stays valid
at the provider until it expires. To end the CUE++ ID session itself, the user
signs out at id.cueplusplus.com, which calls GoTrue's POST /auth/v1/logout
(an endpoint that exists). To end every session for a user, an admin calls
admin.users.revokeSessions.
The revocation Supabase does implement is grant revocation:
DELETE {issuer}/user/oauth/grants?client_id=…, authenticated with the user's
access token rather than client credentials, revoking the whole grant rather than
one token and forcing re-consent. Different credential, different blast radius —
a separate capability if it is ever wanted, not a drop-in replacement.
License
MIT — see LICENSE.
