fix
This commit is contained in:
@@ -103,35 +103,61 @@ export async function handleCallback(
|
|||||||
return tokenSet;
|
return tokenSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For native app flows the provider may return only an access token (no ID token)
|
||||||
|
// when the redirect_uri uses a custom scheme. client.grant() calls the token
|
||||||
|
// endpoint directly and does not attempt ID token validation.
|
||||||
|
export async function exchangeNativeCode(
|
||||||
|
code: string,
|
||||||
|
codeVerifier: string,
|
||||||
|
redirectUri: string,
|
||||||
|
): Promise<TokenSet> {
|
||||||
|
const client = getOIDCClient();
|
||||||
|
|
||||||
|
const tokenSet = await client.grant({
|
||||||
|
grant_type: 'authorization_code',
|
||||||
|
code,
|
||||||
|
redirect_uri: redirectUri,
|
||||||
|
code_verifier: codeVerifier,
|
||||||
|
});
|
||||||
|
|
||||||
|
return tokenSet;
|
||||||
|
}
|
||||||
|
|
||||||
export async function getUserInfo(tokenSet: TokenSet): Promise<AuthenticatedUser> {
|
export async function getUserInfo(tokenSet: TokenSet): Promise<AuthenticatedUser> {
|
||||||
const client = getOIDCClient();
|
const client = getOIDCClient();
|
||||||
|
|
||||||
const claims = tokenSet.claims();
|
// ID token claims (only available in web/full OIDC flow)
|
||||||
|
const idTokenClaims = tokenSet.id_token ? tokenSet.claims() : undefined;
|
||||||
|
|
||||||
// Try to get more detailed userinfo if available
|
// Always attempt userinfo; for native flows this is the sole source of claims.
|
||||||
let userInfo: Record<string, unknown> = {};
|
let userInfo: Record<string, unknown> = {};
|
||||||
try {
|
try {
|
||||||
userInfo = await client.userinfo(tokenSet);
|
userInfo = await client.userinfo(tokenSet);
|
||||||
} catch {
|
} catch {
|
||||||
// Some providers don't support userinfo endpoint
|
if (!idTokenClaims) {
|
||||||
// We'll use the claims from the ID token
|
// No ID token and no userinfo — nothing to work with.
|
||||||
|
throw new Error('Unable to retrieve user info: userinfo endpoint failed and no ID token present');
|
||||||
|
}
|
||||||
|
// Web flow: fall back to ID token claims only
|
||||||
}
|
}
|
||||||
|
|
||||||
const id = String(claims.sub);
|
const sub = String(userInfo.sub || idTokenClaims?.sub);
|
||||||
const username = String(userInfo.preferred_username || claims.preferred_username || claims.name || id);
|
const id = sub;
|
||||||
const email = String(userInfo.email || claims.email || '');
|
const username = String(
|
||||||
const fullName = String(userInfo.name || claims.name || '') || null;
|
userInfo.preferred_username ||
|
||||||
|
idTokenClaims?.preferred_username ||
|
||||||
|
userInfo.name ||
|
||||||
|
idTokenClaims?.name ||
|
||||||
|
id
|
||||||
|
);
|
||||||
|
const email = String(userInfo.email || idTokenClaims?.email || '');
|
||||||
|
const fullName = String(userInfo.name || idTokenClaims?.name || '') || null;
|
||||||
|
|
||||||
if (!email) {
|
if (!email) {
|
||||||
throw new Error('Email not provided by OIDC provider');
|
throw new Error('Email not provided by OIDC provider');
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return { id, username, fullName, email };
|
||||||
id,
|
|
||||||
username,
|
|
||||||
fullName,
|
|
||||||
email,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function verifyToken(tokenSet: TokenSet): Promise<boolean> {
|
export async function verifyToken(tokenSet: TokenSet): Promise<boolean> {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
createAuthSession,
|
createAuthSession,
|
||||||
getAuthorizationUrl,
|
getAuthorizationUrl,
|
||||||
handleCallback,
|
handleCallback,
|
||||||
|
exchangeNativeCode,
|
||||||
getUserInfo,
|
getUserInfo,
|
||||||
} from "../auth/oidc";
|
} from "../auth/oidc";
|
||||||
import { requireAuth, syncUser } from "../middleware/auth";
|
import { requireAuth, syncUser } from "../middleware/auth";
|
||||||
@@ -138,14 +139,7 @@ router.post("/token", async (req, res) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const session = {
|
const tokenSet = await exchangeNativeCode(code, oidcSession.codeVerifier, redirect_uri);
|
||||||
codeVerifier: oidcSession.codeVerifier,
|
|
||||||
state: oidcSession.state,
|
|
||||||
nonce: oidcSession.nonce,
|
|
||||||
redirectUri: redirect_uri,
|
|
||||||
};
|
|
||||||
|
|
||||||
const tokenSet = await handleCallback({ code, state }, session);
|
|
||||||
|
|
||||||
const user = await getUserInfo(tokenSet);
|
const user = await getUserInfo(tokenSet);
|
||||||
await syncUser(user);
|
await syncUser(user);
|
||||||
|
|||||||
Reference in New Issue
Block a user