Skip to content

theAuth 0.8.0

Choose a tag to compare

@github-actions github-actions released this 13 Oct 23:55
8d269b5

theAuth 0.8.0

Features and changes

  • Express, Fastify and NestJS adapters now rate limit requests in three tiers and reject cross-site writes made with a cookie session. New options on every adapter: rateLimit, trustedOrigins and csrf. Both checks are on by default; set rateLimit: false or csrf: false to opt out.
  • Adapter rate limit counters use the rateLimit secondary storage, so one limit holds across instances when a shared store is configured. guard.admit is now async.
  • Adapter rate limits take skip (leave a request out) and key (replace the client key) callbacks.
  • MCP authorization server: new opt-in grants and client compatibility options. lenientClients (optionalResource, dropUnknownScopes, loopbackAnyPort, loopbackAllowLocalhost, pathAudience) relaxes strict request checks for real-world clients and is off by default. clientCredentials adds the client_credentials grant for confidential clients, mapped to a principal through resolvePrincipal, scope-limited, with no refresh token and optional DPoP binding. tokenExchange adds RFC 8693 exchange of access tokens issued by this server with scope narrowing, configured-resource audience changes, a nested act claim capped at 3 hops, revocation checks and DPoP binding checks. jwtBearer redeems RFC 7523 ID-JAG assertions (typ: oauth-id-jag+jwt) from trusted issuers with JWKS fetched through the SSRF-safe fetcher, jti replay protection and a subject mapper. Clients can authenticate with private_key_jwt (registered jwks or jwks_uri, or a Client ID Metadata Document). Client ID Metadata Documents honor Cache-Control max-age within 60 seconds to 24 hours and keep a short negative cache for failed fetches. Fixes the step-up upgrade_url, which pointed at /authorize instead of /mcp/authorize. Metadata advertises the enabled grants and private_key_jwt. The client_credentials grant, previously rejected, now parses and returns unsupported_grant_type unless enabled.
  • /mcp/revoke accepts private_key_jwt client authentication (client_assertion and client_assertion_type), reusing the token endpoint verifier. The assertion aud must be the revocation endpoint or the issuer, each jti is single use across the token and revocation endpoints, and an assertion cannot be combined with a client secret. client_secret_basic, client_secret_post and public clients are unchanged. Discovery now lists private_key_jwt and the signing algorithms under revocation_endpoint_auth_methods_supported and revocation_endpoint_auth_signing_alg_values_supported.
  • Native app social sign-in. The oauth plugin takes mobile: { redirectUris } and adds a PKCE code exchange at POST /auth/oauth/mobile/exchange, so a session token is never placed in a redirect URL. @glinr/theauth-expo gains signInWithProvider and useSocialSignIn, createSecureStoreStorage, and an appState option that refreshes the session when the app returns to the foreground. The oauth plugin now also creates the theauth_one_time_tokens table.
  • Working sign-in providers for Linear, Kick, Vercel, PayPal and Kakao: createLinearProvider, createKickProvider, createVercelProvider, createPaypalProvider (with sandbox) and createKakaoProvider. The older linearProvider, kickProvider, vercelProvider, paypalProvider and kakaoProvider presets read the wrong userinfo shape or the wrong endpoints and are unchanged. genericOIDC gains optional scopeSeparator, defaultScopes, userinfoMethod, userinfoBody, authorizationParams, tokenAuth ("basic") and pkce, and mapProfile may return emailVerified. Existing configs behave as before.
  • Social sign-in token API. The OAuth module gains getAccessToken({ userId, provider }) (single flight refresh on expiry, rotated tokens persisted), listAccounts(userId) and unlinkAccount(userId, provider) with a last-credential guard, plus typed OAuthTokenError codes (no_account, no_refresh_token, refresh_failed, revoked, decrypt_failed, last_credential). The plugin adds GET /auth/oauth/accounts and DELETE /auth/oauth/accounts/:provider and exposes the module as pluginContext.oauth. Optional tokenEncryption seals tokens at rest with the token vault AES-256-GCM envelope; legacy plaintext rows stay readable and are re-encrypted on their next write. Built-in providers implement refreshTokens. Stateless sessions now share one refreshAccessToken call between concurrent getAccessToken calls.
  • OpenID Shared Signals Framework 1.0 with CAEP and RISC. New sharedSignals() plugin (also at @glinr/theauth/signals) mounts an SSF transmitter (stream management, status, subjects, verification, push per RFC 8935 with retry and SSRF-safe URLs, poll per RFC 8936) and a receiver that verifies Security Event Tokens (RFC 8417) and revokes local sessions on session-revoked, sessions-revoked and account-disabled. Emission is opt in through new onPasswordChanged (password reset), onChange (TOTP) and onCredentialChange (passkey) callbacks plus the existing session onRevoke and admin onAdminAction. Adds the theauth_ssf_* tables when the plugin is present.
  • Sign-in gate covers every session-issuing path. SignInMethod gains phone, email_otp, one_tap, siwe and device (exported as SIGN_IN_METHODS). Phone verify, email OTP verify, Google One Tap and the device-auth plugin now call the gate before issuing a session and answer with SignInGateError or the standard challenge response. The require2faFor default is unchanged (password, social, magic_link), so the new methods only ask for a second factor when you list them; a forced password change applies to them as it does to the other methods. createPhoneAuthModule, createEmailOtpModule and createOneTapModule take an optional trailing signInGate, and verifyCode accepts { request } for trusted-device cookies. createDeviceAuthModule's issueToken may throw SignInGateError to answer the token poll with a challenge. SIWE only verifies a signature and issues no session, so it has no call site yet; the siwe method name is reserved for apps that mint a session from it. (details in the package changelog)
  • Central sign-in gate. Every session-issuing path (username, magic link, passkey, and the OAuth callback via ctx.signInGate) now asks one gate whether a session may be issued. A forced password change returns a signed challenge instead of a hard block, and users with TOTP enabled get a two_factor_required challenge for the methods in signInGate.require2faFor (default when signInGate is set: password, social, magic_link). New routes POST /auth/sign-in/two-factor and POST /auth/sign-in/password-change finish the flow. Optional trusted devices (signInGate.trustedDevice.enabled, default off) skip 2FA through a random device cookie, are listed and revoked at /auth/trusted-devices, and are revoked on any password change. SignInGateError is thrown by username.signIn and magicLink.verify, with the existing "Password reset required" message kept. (details in the package changelog)
  • createStatelessSessions now refuses token cookies written before the identity binding by default (acceptLegacyTokenCookies was true). Set it to true for one cookie lifetime when upgrading with live sessions; otherwise affected users sign in again once.
  • Typed plugin endpoints. defineEndpoint validates params, query and body with zod and exposes the shapes through ApiOf; createEndpointClient in the client package calls routes by "METHOD /path" key with inferred input and result types.

Fixes

  • Share the rate limit and cross-site refusal writer between the Express and NestJS adapters.
  • The remaining adapters now apply the same rate limits and cross-site write check as Express, Fastify and NestJS (rateLimit, trustedOrigins, csrf). The strict tier also matches when the adapter is mounted under a path prefix.
  • Trim trailing slashes with a scan instead of replace(/\/+$/, ""), which is quadratic on a long run of slashes (federation discovery URL, self-hosted provider presets, CLI token cache key). Match whitespace before the > in the codemod's closing script tag. Repeat the reserved attribute name check inline at each SCIM PATCH property write.
  • Require drizzle-orm 0.45.2 or newer. Earlier versions do not escape SQL identifiers correctly, which allows SQL injection when an identifier is built from untrusted input (GHSA advisory for drizzle-orm below 0.45.2). The ranges in the create-theauth-app template and the examples are bumped too.
  • Security: the stateless session token cookie is now bound to the identity cookie. A token cookie sealed under the same secret for another user, or for an older session of the same user, no longer decrypts (AES-GCM additional data over sub and issuedAt). New cookies use a v2: format. Legacy cookies are still read for one release: they are re-sealed in the new format on the next getAccessToken, and onDeprecation is called once per read so you can log it. Set acceptLegacyTokenCookies: false once old cookies have expired (after maxAge) to close the window; the default stays true.

Packages in this release

Package Version Changelog
@glinr/theauth 0.7.2 to 0.8.0 CHANGELOG
@glinr/theauth-astro 5.0.1 to 5.1.0 CHANGELOG
@glinr/theauth-cli 0.3.3 to 0.4.0 CHANGELOG
@glinr/theauth-client 0.3.0 to 0.4.0 CHANGELOG
@glinr/theauth-dashboard 0.2.0 to 0.3.0 CHANGELOG
@glinr/theauth-email 4.0.0 to 4.1.0 CHANGELOG
@glinr/theauth-expo 0.3.0 to 0.4.0 CHANGELOG
@glinr/theauth-express 5.0.1 to 5.1.0 CHANGELOG
@glinr/theauth-extension 0.1.0 (new) CHANGELOG
@glinr/theauth-fastify 5.0.1 to 5.1.0 CHANGELOG
@glinr/theauth-hono 5.0.1 to 5.1.0 CHANGELOG
@glinr/theauth-nestjs 5.0.1 to 5.1.0 CHANGELOG
@glinr/theauth-nextjs 5.0.1 to 5.1.0 CHANGELOG
@glinr/theauth-nuxt 5.0.1 to 5.1.0 CHANGELOG
@glinr/theauth-plugin-discovery 4.0.0 to 4.0.1 CHANGELOG
@glinr/theauth-react 0.7.0 to 0.7.1 CHANGELOG
@glinr/theauth-solidstart 5.0.1 to 5.1.0 CHANGELOG
@glinr/theauth-sveltekit 5.0.1 to 5.1.0 CHANGELOG
@glinr/theauth-tanstack 5.0.1 to 5.1.0 CHANGELOG
@glinr/theauth-ui 0.2.0 to 0.3.0 CHANGELOG
theauth 0.2.2 to 0.2.3 CHANGELOG

Install

npm install @glinr/theauth@0.8.0

Go: go get github.com/glincker/theauth-go/v2 (releases: https://github.com/glincker/theauth-go/releases). Docs: https://docs.theauth.dev

Full changelog: v0.7.2...v0.8.0

Discussion: https://github.com/glincker/theauth/discussions/172