Skip to content

Commit 5b99c24

Browse files
authored
Add OIDC ID token support (#680)
1 parent 5b536ab commit 5b99c24

File tree

2 files changed

+39
-18
lines changed

2 files changed

+39
-18
lines changed

src/server/auth/handlers/token.test.ts

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,18 @@ jest.mock('pkce-challenge', () => ({
1616
})
1717
}));
1818

19+
const mockTokens = {
20+
access_token: 'mock_access_token',
21+
token_type: 'bearer',
22+
expires_in: 3600,
23+
refresh_token: 'mock_refresh_token'
24+
};
25+
26+
const mockTokensWithIdToken = {
27+
...mockTokens,
28+
id_token: 'mock_id_token'
29+
}
30+
1931
describe('Token Handler', () => {
2032
// Mock client data
2133
const validClient: OAuthClientInformationFull = {
@@ -58,12 +70,7 @@ describe('Token Handler', () => {
5870

5971
async exchangeAuthorizationCode(client: OAuthClientInformationFull, authorizationCode: string): Promise<OAuthTokens> {
6072
if (authorizationCode === 'valid_code') {
61-
return {
62-
access_token: 'mock_access_token',
63-
token_type: 'bearer',
64-
expires_in: 3600,
65-
refresh_token: 'mock_refresh_token'
66-
};
73+
return mockTokens;
6774
}
6875
throw new InvalidGrantError('The authorization code is invalid or has expired');
6976
},
@@ -291,18 +298,36 @@ describe('Token Handler', () => {
291298
);
292299
});
293300

301+
it('returns id token in code exchange if provided', async () => {
302+
mockProvider.exchangeAuthorizationCode = async (client: OAuthClientInformationFull, authorizationCode: string): Promise<OAuthTokens> => {
303+
if (authorizationCode === 'valid_code') {
304+
return mockTokensWithIdToken;
305+
}
306+
throw new InvalidGrantError('The authorization code is invalid or has expired');
307+
};
308+
309+
const response = await supertest(app)
310+
.post('/token')
311+
.type('form')
312+
.send({
313+
client_id: 'valid-client',
314+
client_secret: 'valid-secret',
315+
grant_type: 'authorization_code',
316+
code: 'valid_code',
317+
code_verifier: 'valid_verifier'
318+
});
319+
320+
expect(response.status).toBe(200);
321+
expect(response.body.id_token).toBe('mock_id_token');
322+
});
323+
294324
it('passes through code verifier when using proxy provider', async () => {
295325
const originalFetch = global.fetch;
296326

297327
try {
298328
global.fetch = jest.fn().mockResolvedValue({
299329
ok: true,
300-
json: () => Promise.resolve({
301-
access_token: 'mock_access_token',
302-
token_type: 'bearer',
303-
expires_in: 3600,
304-
refresh_token: 'mock_refresh_token'
305-
})
330+
json: () => Promise.resolve(mockTokens)
306331
});
307332

308333
const proxyProvider = new ProxyOAuthServerProvider({
@@ -359,12 +384,7 @@ describe('Token Handler', () => {
359384
try {
360385
global.fetch = jest.fn().mockResolvedValue({
361386
ok: true,
362-
json: () => Promise.resolve({
363-
access_token: 'mock_access_token',
364-
token_type: 'bearer',
365-
expires_in: 3600,
366-
refresh_token: 'mock_refresh_token'
367-
})
387+
json: () => Promise.resolve(mockTokens)
368388
});
369389

370390
const proxyProvider = new ProxyOAuthServerProvider({

src/shared/auth.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ export const OAuthMetadataSchema = z
6262
export const OAuthTokensSchema = z
6363
.object({
6464
access_token: z.string(),
65+
id_token: z.string().optional(), // Optional for OAuth 2.1, but necessary in OpenID Connect
6566
token_type: z.string(),
6667
expires_in: z.number().optional(),
6768
scope: z.string().optional(),

0 commit comments

Comments
 (0)