Commit d8df1f8
feat(auth-clerk): Add IUserProvider + ISSOProvider + ISessionProvider for Studio login (#16659)
## Summary
Enables Clerk users to log into Mastra Studio via OAuth SSO. Previously,
the Clerk auth provider only supported API token verification — Studio
login was not possible because the provider lacked the required
`ISSOProvider` and `ISessionProvider` interfaces.
## What Changed
### `auth/clerk/src/index.ts`
- **IUserProvider**: `getCurrentUser` (extracts JWT from Authorization
header or `__session` cookie, verifies via JWKS), `getUser` (calls Clerk
API `clerk.users.getUser`), `getUserProfileUrl`
- **ISSOProvider**: OAuth 2.0 Authorization Code flow via Clerk OAuth
endpoints (`/oauth/authorize`, `/oauth/token`). Includes `getLoginUrl`,
`handleCallback`, `getLoginButtonConfig`. Requires a separate Clerk
OAuth Application (Client ID + Client Secret) created in the Clerk
Dashboard.
- **ISessionProvider**: Encrypted cookie sessions using PBKDF2 key
derivation + AES-GCM encryption. Includes `createSession`,
`validateSession`, `destroySession`, `refreshSession`,
`getSessionIdFromRequest`, `getSessionHeaders`, `getClearSessionHeaders`
- **authenticateToken**: Updated to check session cookie first when SSO
is enabled, then falls back to JWT verification
- **authorizeUser**: Accepts both `user.sub` (JWT) and `user.id`
(session cookie) fields
- SSO methods are only attached when `oauthClientId` +
`oauthClientSecret` are configured, keeping the provider duck-typing
safe for `buildCapabilities()` detection
### `auth/clerk/src/index.test.ts`
- 48 tests covering all interfaces (up from 13 original tests)
- Tests for: `authenticateToken`, `authorizeUser`, `getCurrentUser`,
`getUser`, `getLoginUrl`, `handleCallback`, `getLoginButtonConfig`,
session encryption/decryption, cookie management, state validation
## Configuration
```typescript
import { MastraAuthClerk } from "@mastra/auth-clerk";
const auth = new MastraAuthClerk({
publishableKey: process.env.CLERK_PUBLISHABLE_KEY!,
// Required for Studio SSO login (create OAuth app in Clerk Dashboard)
oauthClientId: process.env.CLERK_OAUTH_CLIENT_ID,
oauthClientSecret: process.env.CLERK_OAUTH_CLIENT_SECRET,
cookiePassword: process.env.CLERK_COOKIE_PASSWORD, // min 32 chars
});
```
## Test Plan
1. **Unit tests**: 48 tests passing (`pnpm test` in `auth/clerk`)
2. **Type check**: Zero errors (`tsc --noEmit`)
3. **End-to-end**: Verified Clerk OAuth login flow against live Clerk
tenant
- `/api/auth/capabilities` returns `login.type=sso` with Clerk provider
- OAuth redirect → Clerk login → callback → session cookie set
- `/api/auth/me` returns authenticated user
- `/api/agents` and other protected routes return 200 with session
cookie
- Logout clears session cookie
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## ELI5
This PR lets Mastra Studio users sign in with Clerk via OAuth and keeps
them logged in using secure, encrypted cookies, while still supporting
Clerk-issued JWTs for API requests.
---
## Overview
Converts MastraAuthClerk from JWT-only verification into a full
Clerk-backed provider implementing IUserProvider and, when OAuth
credentials are provided, exposing ISSOProvider and ISessionProvider for
Studio SSO. Adds encrypted cookie sessions (PBKDF2 → AES‑GCM), stateless
HMAC-signed short-lived OAuth state tokens, OAuth Authorization Code
handling (authorize → token → userinfo/id_token), and updates auth logic
to prefer session cookies when SSO is enabled.
## Key changes
- auth/clerk/src/index.ts
- Implements IUserProvider:
- getCurrentUser(request): prefers encrypted session cookie if SSO
enabled; otherwise extracts token from Authorization header or Clerk
__session cookie, verifies via JWKS, and optionally enriches via Clerk
Users API with a JWT-claims fallback.
- getUser(userId): fetches user via Clerk API (returns null on error).
- getUserProfileUrl(user): returns profile path.
- isSSOEnabled(), getFapiUrl().
- Adds ISSOProvider (attached only when oauthClientId &
oauthClientSecret configured):
- Full OAuth2 Authorization Code flow against Clerk (/oauth/authorize,
/oauth/token).
- getLoginUrl: async-capable; builds authorize URL and encodes stateless
signed+expiring state (HMAC).
- handleCallback: verifies state, exchanges code for tokens (10s fetch
timeout), derives user from id_token (via JWKS) or /oauth/userinfo,
optionally enriches via Clerk API, returns encrypted session cookie +
tokens.
- getLoginButtonConfig: login button metadata.
- Adds ISessionProvider (cookie-backed, attached when SSO enabled):
- Session encryption/decryption using PBKDF2-derived AES‑GCM key; cookie
encoded as base64(salt || iv || ciphertext).
- createSession returns encrypted cookie headers;
validateSession/refreshSession are effectively no-ops for this
lightweight model; destroySession clears cookie.
- getSessionIdFromRequest / getSessionHeaders / getClearSessionHeaders
for cookie handling.
- Enforces minimum 32-character cookie password; warns on auto-generated
password (ephemeral across restarts).
- Authentication behavior:
- authenticateToken(token, request?): when SSO enabled and request
provided, tries session cookie first, then falls back to JWKS
verification.
- authorizeUser accepts both JWT-style (payload.sub) and
session-cookie-style (id) users.
- Utilities:
- deriveFapiUrl(publishableKey) for Clerk FAPI URL.
- Timing-safe HMAC-SHA256 state signing/verifying replaces earlier
approach.
- Tests
- auth/clerk/src/index.test.ts expanded from ~13 to 48 tests covering
initialization, authenticateToken, authorizeUser (incl. custom/org-based
logic), getCurrentUser/getUser, SSO flows (getLoginUrl + state,
handleCallback token exchange and userinfo fallback), session
encryption/decryption, cookie management, state expiry/validation, and
logout cookie clearing.
- Packaging / examples
- .changeset adds a minor release note documenting Studio SSO, JWT
validation, and encrypted sessions.
- examples/agent: adds `@mastra/auth-clerk` dependency, adds initClerk()
example that logs SSO vs JWT-only mode, and registers 'clerk' provider
in examples.
- Core interface updates
- packages/core/src/auth/interfaces/sso.ts: getLoginUrl widened to
return string | Promise<string>.
- CompositeAuth and server SSO handler updated to await/handle async
getLoginUrl results.
## Configuration
Dual modes supported:
- JWT-only (API): configure jwksUri + keys.
- Studio SSO: additionally provide oauthClientId, oauthClientSecret, and
session.cookiePassword (min 32 chars) or allow auto-generation (with
warning that sessions won’t survive restarts).
Example SSO config: publishableKey, jwksUri, secretKey, oauthClientId,
oauthClientSecret, session.cookiePassword.
## Verification
- 48 unit tests added (author reports all passing).
- TypeScript type-check: zero errors.
- E2E: author verified Clerk OAuth flow against a live Clerk tenant —
OAuth redirect → Clerk login → callback sets encrypted session cookie,
/api/auth/me and protected routes succeed with session cookie, logout
clears session cookie, and /api/auth/capabilities reports login.type=sso
when configured.
## Notes & highlights
- ISSOProvider/ISessionProvider are only attached when oauthClientId and
oauthClientSecret are configured (preserves duck-typing for
buildCapabilities()).
- State tokens are stateless, HMAC-signed, short-lived, and verified
timing-safely (suitable for serverless/load-balanced deployments).
- Token exchange uses fetch with a 10s timeout.
- Session cookie encryption uses PBKDF2 (100k iterations) + AES‑GCM with
salt+iv; cookie stores base64(salt||iv||ciphertext).
- validateSession / refreshSession intentionally no-op for this
cookie-backed session model.
- Recommended deployment: run separate MastraAuthClerk instances for API
(JWT-only) and Studio (SSO-enabled) when appropriate.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Mastra Code (anthropic/claude-opus-4-6) <noreply@mastra.ai>1 parent 082d1ef commit d8df1f8
10 files changed
Lines changed: 1265 additions & 18 deletions
File tree
- .changeset
- auth/clerk/src
- examples/agent
- src/mastra/auth
- packages
- core/src
- auth/interfaces
- server
- server/src/server/handlers
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
0 commit comments