Repository navigation
theAuth 0.8.0
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,trustedOriginsandcsrf. Both checks are on by default; setrateLimit: falseorcsrf: falseto opt out. - Adapter rate limit counters use the
rateLimitsecondary storage, so one limit holds across instances when a shared store is configured.guard.admitis now async. - Adapter rate limits take
skip(leave a request out) andkey(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.clientCredentialsadds the client_credentials grant for confidential clients, mapped to a principal throughresolvePrincipal, scope-limited, with no refresh token and optional DPoP binding.tokenExchangeadds RFC 8693 exchange of access tokens issued by this server with scope narrowing, configured-resource audience changes, a nestedactclaim capped at 3 hops, revocation checks and DPoP binding checks.jwtBearerredeems 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 withprivate_key_jwt(registeredjwksorjwks_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-upupgrade_url, which pointed at/authorizeinstead of/mcp/authorize. Metadata advertises the enabled grants andprivate_key_jwt. The client_credentials grant, previously rejected, now parses and returnsunsupported_grant_typeunless enabled. /mcp/revokeacceptsprivate_key_jwtclient authentication (client_assertionandclient_assertion_type), reusing the token endpoint verifier. The assertionaudmust be the revocation endpoint or the issuer, eachjtiis single use across the token and revocation endpoints, and an assertion cannot be combined with a client secret.client_secret_basic,client_secret_postand public clients are unchanged. Discovery now listsprivate_key_jwtand the signing algorithms underrevocation_endpoint_auth_methods_supportedandrevocation_endpoint_auth_signing_alg_values_supported.- Native app social sign-in. The oauth plugin takes
mobile: { redirectUris }and adds a PKCE code exchange atPOST /auth/oauth/mobile/exchange, so a session token is never placed in a redirect URL.@glinr/theauth-expogainssignInWithProvideranduseSocialSignIn,createSecureStoreStorage, and anappStateoption that refreshes the session when the app returns to the foreground. The oauth plugin now also creates thetheauth_one_time_tokenstable. - Working sign-in providers for Linear, Kick, Vercel, PayPal and Kakao:
createLinearProvider,createKickProvider,createVercelProvider,createPaypalProvider(withsandbox) andcreateKakaoProvider. The olderlinearProvider,kickProvider,vercelProvider,paypalProviderandkakaoProviderpresets read the wrong userinfo shape or the wrong endpoints and are unchanged.genericOIDCgains optionalscopeSeparator,defaultScopes,userinfoMethod,userinfoBody,authorizationParams,tokenAuth("basic") andpkce, andmapProfilemay returnemailVerified. 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)andunlinkAccount(userId, provider)with a last-credential guard, plus typedOAuthTokenErrorcodes (no_account,no_refresh_token,refresh_failed,revoked,decrypt_failed,last_credential). The plugin addsGET /auth/oauth/accountsandDELETE /auth/oauth/accounts/:providerand exposes the module aspluginContext.oauth. OptionaltokenEncryptionseals 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 implementrefreshTokens. Stateless sessions now share onerefreshAccessTokencall between concurrentgetAccessTokencalls. - 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 newonPasswordChanged(password reset),onChange(TOTP) andonCredentialChange(passkey) callbacks plus the existing sessiononRevokeand adminonAdminAction. Adds thetheauth_ssf_*tables when the plugin is present. - Sign-in gate covers every session-issuing path.
SignInMethodgainsphone,email_otp,one_tap,siweanddevice(exported asSIGN_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 withSignInGateErroror the standard challenge response. Therequire2faFordefault 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,createEmailOtpModuleandcreateOneTapModuletake an optional trailingsignInGate, andverifyCodeaccepts{ request }for trusted-device cookies.createDeviceAuthModule'sissueTokenmay throwSignInGateErrorto answer the token poll with a challenge. SIWE only verifies a signature and issues no session, so it has no call site yet; thesiwemethod 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 atwo_factor_requiredchallenge for the methods insignInGate.require2faFor(default whensignInGateis set: password, social, magic_link). New routesPOST /auth/sign-in/two-factorandPOST /auth/sign-in/password-changefinish 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.SignInGateErroris thrown byusername.signInandmagicLink.verify, with the existing "Password reset required" message kept. (details in the package changelog) createStatelessSessionsnow refuses token cookies written before the identity binding by default (acceptLegacyTokenCookieswastrue). Set it totruefor one cookie lifetime when upgrading with live sessions; otherwise affected users sign in again once.- Typed plugin endpoints.
defineEndpointvalidates params, query and body with zod and exposes the shapes throughApiOf;createEndpointClientin 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-orm0.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
subandissuedAt). New cookies use av2:format. Legacy cookies are still read for one release: they are re-sealed in the new format on the nextgetAccessToken, andonDeprecationis called once per read so you can log it. SetacceptLegacyTokenCookies: falseonce old cookies have expired (aftermaxAge) to close the window; the default staystrue.
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.0Go: 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