Skip to content

Commit d8df1f8

Browse files
rphansen91Mastra Code (anthropic/claude-opus-4-6)
andauthored
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/clerk-iuserprovider.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
---
2+
'@mastra/auth-clerk': minor
3+
'@mastra/core': patch
4+
'@mastra/server': patch
5+
---
6+
7+
Added full Studio authentication support for Clerk users.
8+
9+
**What's new:**
10+
- **Studio SSO login** — your internal team can now sign in to Mastra Studio using their Clerk accounts via OAuth 2.0/OIDC
11+
- **JWT validation** — API requests with Clerk-issued JWTs are automatically validated
12+
- **Session persistence** — Studio sessions are maintained with encrypted cookies (no need to log in repeatedly)
13+
14+
**Setup:**
15+
1. Create an OAuth Application in your Clerk Dashboard
16+
2. Configure the auth provider with your Clerk credentials
17+
18+
```typescript
19+
import { MastraAuthClerk } from '@mastra/auth-clerk';
20+
21+
const auth = new MastraAuthClerk({
22+
jwksUri: process.env.CLERK_JWKS_URI,
23+
secretKey: process.env.CLERK_SECRET_KEY,
24+
publishableKey: process.env.CLERK_PUBLISHABLE_KEY,
25+
// For Studio SSO login:
26+
oauthClientId: process.env.CLERK_OAUTH_CLIENT_ID,
27+
oauthClientSecret: process.env.CLERK_OAUTH_CLIENT_SECRET,
28+
session: { cookiePassword: process.env.CLERK_COOKIE_PASSWORD },
29+
});
30+
```
31+
32+
**Note:** This release includes updates to `@mastra/core` (ISSOProvider interface now supports async getLoginUrl) and `@mastra/server` (handles async login URLs). All three packages should be updated together.

0 commit comments

Comments
 (0)