@honeyfield/rent2b-mcp 1.2.2 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,26 +1,45 @@
1
1
  import express from 'express';
2
2
  import { mcpAuthRouter, getOAuthProtectedResourceMetadataUrl, } from '@modelcontextprotocol/sdk/server/auth/router.js';
3
3
  import { requireBearerAuth } from '@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js';
4
- import { parseAuthorizeSubmit } from './authorize-page.js';
4
+ import { generatePkce, } from './social.js';
5
+ import { parseAuthorizeSubmit, parseSocialStart, renderAuthorizePage, } from './authorize-page.js';
6
+ function str(v) {
7
+ return typeof v === 'string' ? v : '';
8
+ }
9
+ /** OAuth error redirect back to the client's callback (RFC 6749 §4.1.2.1). */
10
+ function redirectError(res, redirectUri, state, error) {
11
+ try {
12
+ const url = new URL(redirectUri);
13
+ url.searchParams.set('error', error);
14
+ if (state)
15
+ url.searchParams.set('state', state);
16
+ res.redirect(302, url.toString());
17
+ }
18
+ catch {
19
+ res.status(400).send(`Sign-in failed: ${error}`);
20
+ }
21
+ }
5
22
  /**
6
23
  * Wires the OAuth 2.1 + DCR surface onto an Express app and returns the
7
24
  * bearer-auth middleware to guard the MCP endpoint.
8
25
  *
26
+ * Login is the user's own rent2b account: email/password (delegated to the
27
+ * rent2b-api auth endpoints) or Google/Apple (a server-side Supabase PKCE flow
28
+ * run here). The issued token carries the resulting Supabase session (see
29
+ * provider.mintSessionTokens). Raw `r2b_` bearer tokens and `X-API-Key` headers
30
+ * still work (backward compat).
31
+ *
9
32
  * Note on path-prefixed deployment: the MCP SDK serves the auth handlers at the
10
- * origin root (`/authorize`, `/token`, `/register`) and advertises them
11
- * origin-level, dropping any issuer path. Behind the gateway, Caddy strips the
12
- * `/rent2b` prefix before the container sees these paths, so the handlers line
13
- * up — but the SDK's authorization-server metadata would advertise the wrong
14
- * (origin-level) URLs. We therefore override that one document with
15
- * path-correct endpoint URLs. The SDK's protected-resource metadata is already
16
- * correct (it advertises the full issuer URL), so we keep it.
33
+ * origin root and advertises them origin-level, dropping any issuer path.
34
+ * Behind the gateway, Caddy strips `/rent2b` before the container sees these
35
+ * paths, so they line up but the SDK's authorization-server metadata would
36
+ * advertise the wrong (origin-level) URLs, so we override that one document.
17
37
  */
18
38
  export function registerOAuth(app, opts) {
19
- const { oauthCfg, provider, validateKey } = opts;
39
+ const { oauthCfg, provider, apiAuth, social } = opts;
40
+ const socialLogin = !!social;
20
41
  const issuerHref = oauthCfg.issuerUrl.href.replace(/\/$/, '');
21
42
  app.use(express.urlencoded({ extended: false }));
22
- // Override: path-aware authorization-server metadata (registered before the
23
- // SDK router so it shadows the SDK's origin-level document).
24
43
  const authServerMetadata = {
25
44
  issuer: issuerHref,
26
45
  authorization_endpoint: `${issuerHref}/authorize`,
@@ -42,27 +61,122 @@ export function registerOAuth(app, opts) {
42
61
  resourceServerUrl: oauthCfg.resourceUrl,
43
62
  scopesSupported: [],
44
63
  }));
45
- // The API-key entry page (provider.authorize) posts here.
64
+ // Step 1 of social login: start a Supabase PKCE flow and bounce the browser
65
+ // to the provider. The verifier is kept server-side, keyed by `sid`.
66
+ app.get('/authorize/social', (req, res) => {
67
+ if (!social) {
68
+ res.status(404).send('Social login is not enabled');
69
+ return;
70
+ }
71
+ let parsed;
72
+ try {
73
+ parsed = parseSocialStart(req.query);
74
+ }
75
+ catch {
76
+ res.status(400).send('Missing OAuth flow parameters');
77
+ return;
78
+ }
79
+ const { verifier, challenge } = generatePkce();
80
+ const sid = social.store.create({
81
+ verifier,
82
+ clientId: parsed.flow.clientId,
83
+ redirectUri: parsed.flow.redirectUri,
84
+ state: parsed.flow.state,
85
+ codeChallenge: parsed.flow.codeChallenge,
86
+ });
87
+ const redirectTo = `${issuerHref}/authorize/callback?sid=${sid}`;
88
+ res.redirect(302, social.oauth.authorizeUrl(parsed.provider, redirectTo, challenge));
89
+ });
90
+ // Step 2 of social login: PKCE returns `?code=` (server-readable). Exchange it
91
+ // for a Supabase session, then issue our own auth code to the MCP client.
92
+ app.get('/authorize/callback', async (req, res) => {
93
+ if (!social) {
94
+ res.status(404).send('Social login is not enabled');
95
+ return;
96
+ }
97
+ const q = req.query;
98
+ const flow = social.store.take(str(q.sid));
99
+ if (!flow) {
100
+ res.status(400).send('Login session expired — please start again.');
101
+ return;
102
+ }
103
+ const providerError = str(q.error);
104
+ if (providerError) {
105
+ redirectError(res, flow.redirectUri, flow.state, providerError);
106
+ return;
107
+ }
108
+ const code = str(q.code);
109
+ if (!code) {
110
+ redirectError(res, flow.redirectUri, flow.state, 'access_denied');
111
+ return;
112
+ }
113
+ try {
114
+ const session = await social.oauth.exchangeCode(code, flow.verifier);
115
+ const orgId = await apiAuth.resolveOrg(session.access_token);
116
+ const ourCode = provider.issueAuthCode({
117
+ session,
118
+ orgId,
119
+ codeChallenge: flow.codeChallenge,
120
+ redirectUri: flow.redirectUri,
121
+ clientId: flow.clientId,
122
+ });
123
+ const redirect = new URL(flow.redirectUri);
124
+ redirect.searchParams.set('code', ourCode);
125
+ if (flow.state)
126
+ redirect.searchParams.set('state', flow.state);
127
+ res.redirect(302, redirect.toString());
128
+ }
129
+ catch {
130
+ redirectError(res, flow.redirectUri, flow.state, 'server_error');
131
+ }
132
+ });
133
+ // The email/password login page (provider.authorize) posts here.
46
134
  app.post('/authorize/submit', async (req, res) => {
135
+ const body = req.body;
47
136
  let fields;
48
137
  try {
49
- fields = parseAuthorizeSubmit(req.body);
138
+ fields = parseAuthorizeSubmit(body);
50
139
  }
51
140
  catch {
52
141
  res.status(400).send('Missing required fields');
53
142
  return;
54
143
  }
144
+ const reRenderError = (message) => {
145
+ res.status(401).setHeader('Content-Type', 'text/html; charset=utf-8');
146
+ res.send(renderAuthorizePage({
147
+ clientId: fields.clientId,
148
+ redirectUri: fields.redirectUri,
149
+ state: fields.state,
150
+ codeChallenge: fields.codeChallenge,
151
+ scopes: str(body.scopes),
152
+ resource: str(body.resource),
153
+ }, message, socialLogin));
154
+ };
155
+ let session;
55
156
  let orgId;
56
157
  try {
57
- orgId = await validateKey(fields.apiKey);
158
+ if (fields.mode === 'session') {
159
+ session = {
160
+ access_token: fields.accessToken,
161
+ refresh_token: fields.refreshToken,
162
+ };
163
+ orgId = null;
164
+ }
165
+ else {
166
+ const result = await apiAuth.signInWithPassword(fields.email, fields.password);
167
+ session = result.session;
168
+ orgId = result.orgId;
169
+ }
170
+ if (orgId == null) {
171
+ orgId = await apiAuth.resolveOrg(session.access_token);
172
+ }
58
173
  }
59
- catch {
60
- res.status(401).setHeader('Content-Type', 'text/html; charset=utf-8');
61
- res.send('<p>Invalid rent2b API key. <a href="javascript:history.back()">Try again</a>.</p>');
174
+ catch (err) {
175
+ reRenderError(err instanceof Error ? err.message : 'Sign-in failed');
62
176
  return;
63
177
  }
64
178
  const code = provider.issueAuthCode({
65
- apiKey: fields.apiKey,
179
+ session,
66
180
  orgId,
67
181
  codeChallenge: fields.codeChallenge,
68
182
  redirectUri: fields.redirectUri,
@@ -1 +1 @@
1
- {"version":3,"file":"http-oauth.js","sourceRoot":"","sources":["../../src/auth/http-oauth.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"http-oauth.js","sourceRoot":"","sources":["../../src/auth/http-oauth.ts"],"names":[],"mappings":"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"}
@@ -4,11 +4,16 @@ import type { OAuthClientInformationFull, OAuthTokens } from '@modelcontextproto
4
4
  import type { AuthInfo } from '@modelcontextprotocol/sdk/server/auth/types.js';
5
5
  import type { TokenCodec } from './token-crypto.js';
6
6
  import type { InMemoryClientsStore, AuthCodeStore, AuthCodeRecord } from './stores.js';
7
+ import type { ApiAuthClient } from './api-auth.js';
7
8
  export interface ProviderOptions {
8
9
  codec: TokenCodec;
9
10
  clientsStore: InMemoryClientsStore;
10
11
  codeStore: AuthCodeStore;
12
+ apiAuth: ApiAuthClient;
13
+ /** Validates a raw `r2b_` key (backward-compat direct-bearer path) → org id. */
11
14
  validateKey: (apiKey: string) => Promise<number>;
15
+ /** Show Google/Apple buttons on the login page (off until social is verified). */
16
+ socialLogin?: boolean;
12
17
  accessTtlSec?: number;
13
18
  refreshTtlSec?: number;
14
19
  }
@@ -17,12 +22,13 @@ export declare class Rent2bOAuthProvider implements OAuthServerProvider {
17
22
  private readonly pending;
18
23
  constructor(opts: ProviderOptions);
19
24
  get clientsStore(): InMemoryClientsStore;
20
- /** Called by the /authorize/submit handler after a valid r2b_ key is entered. */
25
+ /** Called by the /authorize/submit handler after a successful rent2b login. */
21
26
  issueAuthCode(input: Omit<AuthCodeRecord, 'expiresAt'>): string;
22
27
  authorize(client: OAuthClientInformationFull, params: AuthorizationParams, res: Response): Promise<void>;
23
28
  challengeForAuthorizationCode(_client: OAuthClientInformationFull, authorizationCode: string): Promise<string>;
24
29
  exchangeAuthorizationCode(client: OAuthClientInformationFull, authorizationCode: string, _codeVerifier?: string, _redirectUri?: string): Promise<OAuthTokens>;
25
30
  exchangeRefreshToken(client: OAuthClientInformationFull, refreshToken: string, _scopes?: string[]): Promise<OAuthTokens>;
26
31
  verifyAccessToken(token: string): Promise<AuthInfo>;
27
- private mintTokens;
32
+ private mintSessionTokens;
33
+ private mintApiKeyTokens;
28
34
  }
@@ -1,6 +1,9 @@
1
1
  import { randomUUID } from 'node:crypto';
2
- import { TokenError } from './token-crypto.js';
2
+ import { TokenError, jwtExp } from './token-crypto.js';
3
3
  import { renderAuthorizePage } from './authorize-page.js';
4
+ // Our access token always expires this many seconds before the embedded
5
+ // Supabase access token, so the latter is valid for the whole life of ours.
6
+ const SB_EXPIRY_SAFETY_SEC = 300;
4
7
  export class Rent2bOAuthProvider {
5
8
  opts;
6
9
  // Holds the code record between challengeForAuthorizationCode (peek) and
@@ -12,7 +15,7 @@ export class Rent2bOAuthProvider {
12
15
  get clientsStore() {
13
16
  return this.opts.clientsStore;
14
17
  }
15
- /** Called by the /authorize/submit handler after a valid r2b_ key is entered. */
18
+ /** Called by the /authorize/submit handler after a successful rent2b login. */
16
19
  issueAuthCode(input) {
17
20
  return this.opts.codeStore.create(input);
18
21
  }
@@ -26,7 +29,7 @@ export class Rent2bOAuthProvider {
26
29
  codeChallenge: params.codeChallenge,
27
30
  scopes: (params.scopes ?? []).join(' '),
28
31
  resource: params.resource ? String(params.resource) : '',
29
- }));
32
+ }, undefined, this.opts.socialLogin ?? false));
30
33
  }
31
34
  async challengeForAuthorizationCode(_client, authorizationCode) {
32
35
  let rec = this.pending.get(authorizationCode);
@@ -52,7 +55,7 @@ export class Rent2bOAuthProvider {
52
55
  if (rec.clientId !== client.client_id) {
53
56
  throw new Error('Authorization code was issued to a different client');
54
57
  }
55
- return this.mintTokens(rec.apiKey, rec.orgId, client.client_id);
58
+ return this.mintSessionTokens(rec.session, rec.orgId);
56
59
  }
57
60
  async exchangeRefreshToken(client, refreshToken, _scopes) {
58
61
  let payload;
@@ -64,13 +67,19 @@ export class Rent2bOAuthProvider {
64
67
  }
65
68
  if (payload.type !== 'refresh')
66
69
  throw new Error('Not a refresh token');
67
- return this.mintTokens(payload.apiKey, payload.orgId, client.client_id);
70
+ // Legacy BYO-key refresh tokens keep working until the user re-logs in.
71
+ if (payload.apiKey) {
72
+ return this.mintApiKeyTokens(payload.apiKey, payload.orgId);
73
+ }
74
+ if (!payload.sbRefresh)
75
+ throw new Error('Refresh token carries no session');
76
+ // Spend the (rotating) Supabase refresh token for a fresh session.
77
+ const session = await this.opts.apiAuth.refreshSession(payload.sbRefresh);
78
+ return this.mintSessionTokens(session, payload.orgId);
68
79
  }
69
80
  async verifyAccessToken(token) {
70
81
  if (token.startsWith('r2b_')) {
71
82
  // Backward-compat: a raw rent2b API key used directly as the bearer.
72
- // requireBearerAuth requires an expiry; raw keys don't expire, so we set
73
- // a short rolling window (the key is re-validated on each new session).
74
83
  const orgId = await this.opts.validateKey(token);
75
84
  return {
76
85
  token,
@@ -91,15 +100,57 @@ export class Rent2bOAuthProvider {
91
100
  }
92
101
  if (payload.type !== 'access')
93
102
  throw new Error('Not an access token');
103
+ // Legacy BYO-key access tokens carry the key directly.
104
+ if (payload.apiKey) {
105
+ return {
106
+ token,
107
+ clientId: 'rent2b-oauth',
108
+ scopes: [],
109
+ expiresAt: payload.exp,
110
+ extra: { apiKey: payload.apiKey, orgId: payload.orgId },
111
+ };
112
+ }
113
+ if (!payload.sbAccess)
114
+ throw new Error('Access token carries no session');
115
+ // The embedded Supabase access token is guaranteed valid here (its expiry
116
+ // sits after ours by construction), so callers can use it directly.
94
117
  return {
95
118
  token,
96
119
  clientId: 'rent2b-oauth',
97
120
  scopes: [],
98
121
  expiresAt: payload.exp,
99
- extra: { apiKey: payload.apiKey, orgId: payload.orgId },
122
+ extra: { supabaseAccessToken: payload.sbAccess, orgId: payload.orgId },
123
+ };
124
+ }
125
+ mintSessionTokens(session, orgId) {
126
+ const now = Math.floor(Date.now() / 1000);
127
+ // Cap our access token's life so it expires before the Supabase access JWT.
128
+ const sbExp = jwtExp(session.access_token);
129
+ const cap = now + this.opts.accessTtlSec;
130
+ const accessExp = Math.max(now + 60, sbExp ? Math.min(cap, sbExp - SB_EXPIRY_SAFETY_SEC) : cap);
131
+ const access = this.opts.codec.encrypt({
132
+ orgId,
133
+ type: 'access',
134
+ exp: accessExp,
135
+ jti: randomUUID(),
136
+ sbAccess: session.access_token,
137
+ });
138
+ const refresh = this.opts.codec.encrypt({
139
+ orgId,
140
+ type: 'refresh',
141
+ exp: now + this.opts.refreshTtlSec,
142
+ jti: randomUUID(),
143
+ sbRefresh: session.refresh_token,
144
+ });
145
+ return {
146
+ access_token: access,
147
+ token_type: 'Bearer',
148
+ expires_in: accessExp - now,
149
+ refresh_token: refresh,
150
+ scope: '',
100
151
  };
101
152
  }
102
- mintTokens(apiKey, orgId, _clientId) {
153
+ mintApiKeyTokens(apiKey, orgId) {
103
154
  const now = Math.floor(Date.now() / 1000);
104
155
  const access = this.opts.codec.encrypt({
105
156
  apiKey,
@@ -1 +1 @@
1
- {"version":3,"file":"provider.js","sourceRoot":"","sources":["../../src/auth/provider.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"provider.js","sourceRoot":"","sources":["../../src/auth/provider.ts"],"names":[],"mappings":"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"}
@@ -3,78 +3,110 @@ import { randomBytes } from 'node:crypto';
3
3
  import { createTokenCodec } from './token-crypto.js';
4
4
  import { InMemoryClientsStore, AuthCodeStore } from './stores.js';
5
5
  import { Rent2bOAuthProvider } from './provider.js';
6
- function makeProvider(validateKey = vi.fn(async () => 99)) {
6
+ /** Minimal JWT (header.payload.sig) carrying just an `exp` claim. */
7
+ function fakeJwt(expInSec = 3600) {
8
+ const body = Buffer.from(JSON.stringify({ exp: Math.floor(Date.now() / 1000) + expInSec })).toString('base64url');
9
+ return `h.${body}.s`;
10
+ }
11
+ function makeProvider(overrides) {
7
12
  const codec = createTokenCodec(randomBytes(32));
13
+ const validateKey = vi.fn(overrides?.validateKey ?? (async () => 7));
14
+ const apiAuth = {
15
+ refreshSession: vi.fn(async () => ({
16
+ access_token: fakeJwt(),
17
+ refresh_token: 'RT2',
18
+ })),
19
+ ...overrides?.apiAuth,
20
+ };
8
21
  const provider = new Rent2bOAuthProvider({
9
22
  codec,
10
23
  clientsStore: new InMemoryClientsStore(),
11
24
  codeStore: new AuthCodeStore(),
25
+ apiAuth,
12
26
  validateKey,
13
27
  });
14
- return { provider, codec, validateKey };
28
+ return { provider, codec, validateKey, apiAuth };
15
29
  }
16
30
  const client = { client_id: 'c1', redirect_uris: ['https://claude.ai/cb'] };
31
+ const session = { access_token: fakeJwt(), refresh_token: 'RT1' };
32
+ function issue(provider) {
33
+ return provider.issueAuthCode({
34
+ session,
35
+ orgId: 99,
36
+ codeChallenge: 'CHAL',
37
+ redirectUri: 'https://claude.ai/cb',
38
+ clientId: 'c1',
39
+ });
40
+ }
17
41
  describe('Rent2bOAuthProvider', () => {
18
42
  it('issues a code, exposes its challenge, then exchanges it once', async () => {
19
43
  const { provider } = makeProvider();
20
- const code = provider.issueAuthCode({
21
- apiKey: 'r2b_live',
22
- orgId: 99,
23
- codeChallenge: 'CHAL',
24
- redirectUri: 'https://claude.ai/cb',
25
- clientId: 'c1',
26
- });
44
+ const code = issue(provider);
27
45
  expect(await provider.challengeForAuthorizationCode(client, code)).toBe('CHAL');
28
46
  const tokens = await provider.exchangeAuthorizationCode(client, code);
29
47
  expect(tokens.access_token).toBeTruthy();
30
48
  expect(tokens.token_type).toBe('Bearer');
31
49
  expect(tokens.refresh_token).toBeTruthy();
32
- // one-time: second exchange must fail
33
50
  await expect(provider.exchangeAuthorizationCode(client, code)).rejects.toThrow();
34
51
  });
35
- it('verifyAccessToken decrypts an issued access token to the api key', async () => {
52
+ it('access token expires before the embedded Supabase token', async () => {
53
+ const { provider, codec } = makeProvider();
54
+ const tokens = await provider.exchangeAuthorizationCode(client, issue(provider));
55
+ const payload = codec.decrypt(tokens.access_token);
56
+ // Supabase token exp (~now+3600) must sit strictly after ours.
57
+ const now = Math.floor(Date.now() / 1000);
58
+ expect(payload.exp).toBeLessThan(now + 3600);
59
+ });
60
+ it('verifyAccessToken yields the embedded Supabase access token + org', async () => {
36
61
  const { provider } = makeProvider();
37
- const code = provider.issueAuthCode({
38
- apiKey: 'r2b_live',
39
- orgId: 99,
40
- codeChallenge: 'CHAL',
41
- redirectUri: 'https://claude.ai/cb',
42
- clientId: 'c1',
43
- });
44
- const tokens = await provider.exchangeAuthorizationCode(client, code);
62
+ const tokens = await provider.exchangeAuthorizationCode(client, issue(provider));
45
63
  const info = await provider.verifyAccessToken(tokens.access_token);
46
- expect(info.extra?.apiKey).toBe('r2b_live');
64
+ expect(info.extra?.supabaseAccessToken).toBe(session.access_token);
47
65
  expect(info.extra?.orgId).toBe(99);
48
66
  expect(info.clientId).toBe('rent2b-oauth');
49
67
  });
68
+ it('refresh spends the Supabase refresh token for a fresh session', async () => {
69
+ const refreshed = fakeJwt();
70
+ const { provider, apiAuth } = makeProvider({
71
+ apiAuth: {
72
+ refreshSession: vi.fn(async () => ({
73
+ access_token: refreshed,
74
+ refresh_token: 'RT2',
75
+ })),
76
+ },
77
+ });
78
+ const tokens = await provider.exchangeAuthorizationCode(client, issue(provider));
79
+ const next = await provider.exchangeRefreshToken(client, tokens.refresh_token);
80
+ expect(apiAuth.refreshSession).toHaveBeenCalledWith('RT1');
81
+ const info = await provider.verifyAccessToken(next.access_token);
82
+ expect(info.extra?.supabaseAccessToken).toBe(refreshed);
83
+ });
50
84
  it('verifyAccessToken accepts a raw r2b_ key (backward compat)', async () => {
51
- const validateKey = vi.fn(async () => 7);
52
- const { provider } = makeProvider(validateKey);
85
+ const { provider, validateKey } = makeProvider({ validateKey: async () => 7 });
53
86
  const info = await provider.verifyAccessToken('r2b_directkey');
54
87
  expect(validateKey).toHaveBeenCalledWith('r2b_directkey');
55
88
  expect(info.extra?.apiKey).toBe('r2b_directkey');
56
89
  expect(info.extra?.orgId).toBe(7);
57
- // requireBearerAuth rejects tokens without an expiry — both branches must set it.
58
90
  expect(typeof info.expiresAt).toBe('number');
59
91
  expect(info.expiresAt).toBeGreaterThan(Math.floor(Date.now() / 1000));
60
92
  });
93
+ it('verifyAccessToken still honours legacy api-key access tokens', async () => {
94
+ const { provider, codec } = makeProvider();
95
+ const now = Math.floor(Date.now() / 1000);
96
+ const legacy = codec.encrypt({
97
+ apiKey: 'r2b_old',
98
+ orgId: 5,
99
+ type: 'access',
100
+ exp: now + 3600,
101
+ jti: 'x',
102
+ });
103
+ const info = await provider.verifyAccessToken(legacy);
104
+ expect(info.extra?.apiKey).toBe('r2b_old');
105
+ expect(info.extra?.orgId).toBe(5);
106
+ });
61
107
  it('verifyAccessToken rejects garbage', async () => {
62
108
  const { provider } = makeProvider();
63
109
  await expect(provider.verifyAccessToken('not-a-token')).rejects.toThrow();
64
110
  });
65
- it('refresh token yields a new access token bound to the same key', async () => {
66
- const { provider } = makeProvider();
67
- const code = provider.issueAuthCode({
68
- apiKey: 'r2b_live',
69
- orgId: 99,
70
- codeChallenge: 'CHAL',
71
- redirectUri: 'https://claude.ai/cb',
72
- clientId: 'c1',
73
- });
74
- const tokens = await provider.exchangeAuthorizationCode(client, code);
75
- const refreshed = await provider.exchangeRefreshToken(client, tokens.refresh_token);
76
- const info = await provider.verifyAccessToken(refreshed.access_token);
77
- expect(info.extra?.apiKey).toBe('r2b_live');
78
- });
79
111
  });
80
112
  //# sourceMappingURL=provider.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"provider.test.js","sourceRoot":"","sources":["../../src/auth/provider.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"provider.test.js","sourceRoot":"","sources":["../../src/auth/provider.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAGpD,qEAAqE;AACrE,SAAS,OAAO,CAAC,QAAQ,GAAG,IAAI;IAC9B,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CACtB,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,QAAQ,EAAE,CAAC,CAClE,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IACxB,OAAO,KAAK,IAAI,IAAI,CAAC;AACvB,CAAC;AAED,SAAS,YAAY,CAAC,SAGrB;IACC,MAAM,KAAK,GAAG,gBAAgB,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC;IAChD,MAAM,WAAW,GAAG,EAAE,CAAC,EAAE,CAAC,SAAS,EAAE,WAAW,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACrE,MAAM,OAAO,GAAG;QACd,cAAc,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;YACjC,YAAY,EAAE,OAAO,EAAE;YACvB,aAAa,EAAE,KAAK;SACrB,CAAC,CAAC;QACH,GAAG,SAAS,EAAE,OAAO;KACM,CAAC;IAC9B,MAAM,QAAQ,GAAG,IAAI,mBAAmB,CAAC;QACvC,KAAK;QACL,YAAY,EAAE,IAAI,oBAAoB,EAAE;QACxC,SAAS,EAAE,IAAI,aAAa,EAAE;QAC9B,OAAO;QACP,WAAW;KACZ,CAAC,CAAC;IACH,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC;AACnD,CAAC;AAED,MAAM,MAAM,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC,sBAAsB,CAAC,EAAS,CAAC;AACnF,MAAM,OAAO,GAAG,EAAE,YAAY,EAAE,OAAO,EAAE,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC;AAElE,SAAS,KAAK,CAAC,QAA6B;IAC1C,OAAO,QAAQ,CAAC,aAAa,CAAC;QAC5B,OAAO;QACP,KAAK,EAAE,EAAE;QACT,aAAa,EAAE,MAAM;QACrB,WAAW,EAAE,sBAAsB;QACnC,QAAQ,EAAE,IAAI;KACf,CAAC,CAAC;AACL,CAAC;AAED,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;IACnC,EAAE,CAAC,8DAA8D,EAAE,KAAK,IAAI,EAAE;QAC5E,MAAM,EAAE,QAAQ,EAAE,GAAG,YAAY,EAAE,CAAC;QACpC,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC7B,MAAM,CAAC,MAAM,QAAQ,CAAC,6BAA6B,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAChF,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,yBAAyB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACtE,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,UAAU,EAAE,CAAC;QACzC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACzC,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,UAAU,EAAE,CAAC;QAC1C,MAAM,MAAM,CAAC,QAAQ,CAAC,yBAAyB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;IACnF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yDAAyD,EAAE,KAAK,IAAI,EAAE;QACvE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,YAAY,EAAE,CAAC;QAC3C,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,yBAAyB,CAAC,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;QACjF,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QACnD,+DAA+D;QAC/D,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;QAC1C,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,YAAY,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mEAAmE,EAAE,KAAK,IAAI,EAAE;QACjF,MAAM,EAAE,QAAQ,EAAE,GAAG,YAAY,EAAE,CAAC;QACpC,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,yBAAyB,CAAC,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;QACjF,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,iBAAiB,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QACnE,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,mBAAmB,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QACnE,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACnC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+DAA+D,EAAE,KAAK,IAAI,EAAE;QAC7E,MAAM,SAAS,GAAG,OAAO,EAAE,CAAC;QAC5B,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,YAAY,CAAC;YACzC,OAAO,EAAE;gBACP,cAAc,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;oBACjC,YAAY,EAAE,SAAS;oBACvB,aAAa,EAAE,KAAK;iBACrB,CAAC,CAAC;aACJ;SACF,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,yBAAyB,CAAC,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;QACjF,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,oBAAoB,CAAC,MAAM,EAAE,MAAM,CAAC,aAAc,CAAC,CAAC;QAChF,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAC;QAC3D,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,iBAAiB,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACjE,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,mBAAmB,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC1D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4DAA4D,EAAE,KAAK,IAAI,EAAE;QAC1E,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,YAAY,CAAC,EAAE,WAAW,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;QAC/E,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,iBAAiB,CAAC,eAAe,CAAC,CAAC;QAC/D,MAAM,CAAC,WAAW,CAAC,CAAC,oBAAoB,CAAC,eAAe,CAAC,CAAC;QAC1D,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QACjD,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClC,MAAM,CAAC,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC7C,MAAM,CAAC,IAAI,CAAC,SAAU,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC;IACzE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8DAA8D,EAAE,KAAK,IAAI,EAAE;QAC5E,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,YAAY,EAAE,CAAC;QAC3C,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;QAC1C,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC;YAC3B,MAAM,EAAE,SAAS;YACjB,KAAK,EAAE,CAAC;YACR,IAAI,EAAE,QAAQ;YACd,GAAG,EAAE,GAAG,GAAG,IAAI;YACf,GAAG,EAAE,GAAG;SACT,CAAC,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;QACtD,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC3C,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mCAAmC,EAAE,KAAK,IAAI,EAAE;QACjD,MAAM,EAAE,QAAQ,EAAE,GAAG,YAAY,EAAE,CAAC;QACpC,MAAM,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAC,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;IAC5E,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,40 @@
1
+ import type { SupabaseSession } from './api-auth.js';
2
+ /**
3
+ * Server-side social login (Google/Apple) against Supabase GoTrue, run entirely
4
+ * by the MCP so it works for a headless connector.
5
+ *
6
+ * We drive the PKCE flow ourselves: generate the verifier/challenge, redirect
7
+ * the browser to GoTrue `/authorize`, and — because PKCE returns `?code=` on the
8
+ * redirect (readable server-side, unlike the implicit `#fragment`) — exchange
9
+ * the code for a session in the callback with no client-side JavaScript. The
10
+ * verifier is held in `SocialFlowStore`, keyed by an opaque `sid` carried in the
11
+ * redirect URL, and never leaves the server.
12
+ */
13
+ export interface SocialFlow {
14
+ verifier: string;
15
+ clientId: string;
16
+ redirectUri: string;
17
+ state: string;
18
+ codeChallenge: string;
19
+ expiresAt: number;
20
+ }
21
+ export declare class SocialFlowStore {
22
+ private flows;
23
+ /** Store a pending flow and return its opaque id. */
24
+ create(flow: Omit<SocialFlow, 'expiresAt'>): string;
25
+ /** Consume a flow exactly once; undefined if unknown or expired. */
26
+ take(sid: string): SocialFlow | undefined;
27
+ }
28
+ export declare function generatePkce(): {
29
+ verifier: string;
30
+ challenge: string;
31
+ };
32
+ export declare class SupabaseOAuthClient {
33
+ private readonly url;
34
+ private readonly anonKey;
35
+ constructor(url: string, anonKey: string);
36
+ /** GoTrue `/authorize` URL to redirect the browser to (matches supabase-js). */
37
+ authorizeUrl(provider: 'google' | 'apple', redirectTo: string, codeChallenge: string): string;
38
+ /** Exchange the PKCE auth code + verifier for a Supabase session. */
39
+ exchangeCode(code: string, verifier: string): Promise<SupabaseSession>;
40
+ }