mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-04-27 01:16:23 +02:00
feat: integrate Supabase OAuth with OIDC discovery for authentication
Add rowboat auth flow using Supabase as the OIDC provider. User info is fetched via the standard OIDC userinfo endpoint (discovered from issuer metadata) instead of a hard-coded Supabase URL. Includes login screen, auth state hook, IPC handlers, logout button, and id_token_sub persistence for userinfo fetches across app restarts. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
84c101fa21
commit
bbe82c124d
10 changed files with 368 additions and 8 deletions
|
|
@ -159,13 +159,16 @@ export function buildAuthorizationUrl(
|
|||
state: string;
|
||||
}
|
||||
): URL {
|
||||
return client.buildAuthorizationUrl(config, {
|
||||
const url = client.buildAuthorizationUrl(config, {
|
||||
redirect_uri: params.redirectUri,
|
||||
scope: params.scope,
|
||||
code_challenge: params.codeChallenge,
|
||||
code_challenge_method: 'S256',
|
||||
state: params.state,
|
||||
});
|
||||
|
||||
console.log(`[OAuth] Authorization URL: ${url}`);
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -176,7 +179,7 @@ export async function exchangeCodeForTokens(
|
|||
callbackUrl: URL,
|
||||
codeVerifier: string,
|
||||
expectedState: string
|
||||
): Promise<OAuthTokens> {
|
||||
): Promise<{ tokens: OAuthTokens; sub?: string }> {
|
||||
console.log(`[OAuth] Exchanging authorization code for tokens...`);
|
||||
|
||||
const response = await client.authorizationCodeGrant(config, callbackUrl, {
|
||||
|
|
@ -184,8 +187,27 @@ export async function exchangeCodeForTokens(
|
|||
expectedState,
|
||||
});
|
||||
|
||||
const claims = response.claims();
|
||||
console.log(`[OAuth] Token exchange successful`);
|
||||
return toOAuthTokens(response);
|
||||
return {
|
||||
tokens: toOAuthTokens(response),
|
||||
sub: claims?.sub,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch user info from the OIDC userinfo endpoint (discovered via issuer metadata)
|
||||
*/
|
||||
export async function fetchUserInfo(
|
||||
config: client.Configuration,
|
||||
accessToken: string,
|
||||
expectedSubject: string
|
||||
): Promise<{ email: string; name?: string }> {
|
||||
const userInfo = await client.fetchUserInfo(config, accessToken, expectedSubject);
|
||||
return {
|
||||
email: userInfo.email ?? '',
|
||||
name: userInfo.name,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -77,7 +77,22 @@ const providerConfigs: ProviderConfig = {
|
|||
'profile',
|
||||
'email',
|
||||
]
|
||||
}
|
||||
},
|
||||
rowboat: {
|
||||
discovery: {
|
||||
mode: 'issuer',
|
||||
issuer: 'https://yhafoahozylbdyyyqjep.supabase.co/auth/v1',
|
||||
},
|
||||
client: {
|
||||
mode: 'static',
|
||||
clientId: '0b8a99ec-b5b2-4ddf-8e14-69a3a1675114',
|
||||
},
|
||||
scopes: [
|
||||
'openid',
|
||||
'email',
|
||||
'profile',
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ export const OAuthTokens = z.object({
|
|||
expires_at: z.number(), // Unix timestamp
|
||||
token_type: z.literal('Bearer').optional(),
|
||||
scopes: z.array(z.string()).optional(), // Granted scopes from OAuth response
|
||||
id_token_sub: z.string().optional(), // Subject claim from ID token
|
||||
});
|
||||
|
||||
export type OAuthTokens = z.infer<typeof OAuthTokens>;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue