Add nonce for inclusion in id_token
Signed-off-by: JP Dhabolt <john.p.dhabolt@gmail.com>
This commit is contained in:
@@ -32,6 +32,7 @@ import express from 'express';
|
||||
describe('oidcAuthenticator', () => {
|
||||
let implementation: any;
|
||||
let oauthState: OAuthState;
|
||||
let nonce: string;
|
||||
let idToken: string;
|
||||
let publicKey: JWK;
|
||||
const revokedTokenMap: Record<string, boolean> = {};
|
||||
@@ -65,22 +66,7 @@ describe('oidcAuthenticator', () => {
|
||||
request_object_signing_alg_values_supported: ['RS256', 'RS512', 'HS256'],
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
const keyPair = await generateKeyPair('RS256');
|
||||
const privateKey = await exportJWK(keyPair.privateKey);
|
||||
publicKey = await exportJWK(keyPair.publicKey);
|
||||
publicKey.alg = privateKey.alg = 'RS256';
|
||||
|
||||
idToken = await new SignJWT({
|
||||
sub: 'test',
|
||||
iss: 'https://oidc.test',
|
||||
iat: Date.now(),
|
||||
aud: 'clientId',
|
||||
exp: Date.now() + 10000,
|
||||
})
|
||||
.setProtectedHeader({ alg: privateKey.alg, kid: privateKey.kid })
|
||||
.sign(keyPair.privateKey);
|
||||
});
|
||||
beforeAll(async () => {});
|
||||
|
||||
beforeEach(() => {
|
||||
mswServer.use(
|
||||
@@ -96,6 +82,14 @@ describe('oidcAuthenticator', () => {
|
||||
rest.get('https://oidc.test/jwks.json', async (_req, res, ctx) =>
|
||||
res(ctx.status(200), ctx.json({ keys: [{ ...publicKey }] })),
|
||||
),
|
||||
rest.get(
|
||||
'https://oidc.test/oauth2/authorize',
|
||||
async (req, _res, _ctx) => {
|
||||
nonce =
|
||||
new URL(req.url).searchParams.get('nonce') ??
|
||||
'nonceGeneratedByAuthServer';
|
||||
},
|
||||
),
|
||||
rest.post('https://oidc.test/oauth2/token', async (req, res, ctx) => {
|
||||
const formBody = new URLSearchParams(await req.text());
|
||||
if (
|
||||
@@ -104,6 +98,23 @@ describe('oidcAuthenticator', () => {
|
||||
) {
|
||||
return res(ctx.json({}));
|
||||
}
|
||||
|
||||
const keyPair = await generateKeyPair('RS256');
|
||||
const privateKey = await exportJWK(keyPair.privateKey);
|
||||
publicKey = await exportJWK(keyPair.publicKey);
|
||||
publicKey.alg = privateKey.alg = 'RS256';
|
||||
|
||||
idToken = await new SignJWT({
|
||||
sub: 'test',
|
||||
iss: 'https://oidc.test',
|
||||
iat: Date.now(),
|
||||
aud: 'clientId',
|
||||
exp: Date.now() + 10000,
|
||||
nonce,
|
||||
})
|
||||
.setProtectedHeader({ alg: privateKey.alg, kid: privateKey.kid })
|
||||
.sign(keyPair.privateKey);
|
||||
|
||||
return res(
|
||||
req.headers.get('Authorization')
|
||||
? ctx.json({
|
||||
@@ -198,6 +209,15 @@ describe('oidcAuthenticator', () => {
|
||||
expect(searchParams.get('response_type')).toBe('code');
|
||||
});
|
||||
|
||||
it('passes a nonce', async () => {
|
||||
const startResponse = await oidcAuthenticator.start(
|
||||
startRequest,
|
||||
implementation,
|
||||
);
|
||||
const { searchParams } = new URL(startResponse.url);
|
||||
expect(searchParams.get('nonce')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('passes client ID from config', async () => {
|
||||
const startResponse = await oidcAuthenticator.start(
|
||||
startRequest,
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import crypto from 'crypto';
|
||||
import {
|
||||
custom,
|
||||
CustomHttpOptionsProvider,
|
||||
@@ -131,6 +132,7 @@ export const oidcAuthenticator = createOAuthAuthenticator({
|
||||
const options: Record<string, string> = {
|
||||
scope: input.scope || initializedScope || 'openid profile email',
|
||||
state: input.state,
|
||||
nonce: crypto.randomBytes(16).toString('base64'),
|
||||
};
|
||||
const prompt = initializedPrompt || 'none';
|
||||
if (prompt !== 'auto') {
|
||||
|
||||
@@ -30,6 +30,7 @@ import { authModuleOidcProvider } from './module';
|
||||
describe('authModuleOidcProvider', () => {
|
||||
let backstageServer: Server;
|
||||
let appUrl: string;
|
||||
let nonce: string;
|
||||
let idToken: string;
|
||||
let publicKey: JWK;
|
||||
|
||||
@@ -56,22 +57,7 @@ describe('authModuleOidcProvider', () => {
|
||||
request_object_signing_alg_values_supported: ['RS256', 'RS512', 'HS256'],
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
const keyPair = await generateKeyPair('RS256');
|
||||
const privateKey = await exportJWK(keyPair.privateKey);
|
||||
publicKey = await exportJWK(keyPair.publicKey);
|
||||
publicKey.alg = privateKey.alg = 'RS256';
|
||||
|
||||
idToken = await new SignJWT({
|
||||
sub: 'test',
|
||||
iss: 'https://oidc.test',
|
||||
iat: Date.now(),
|
||||
aud: 'clientId',
|
||||
exp: Date.now() + 10000,
|
||||
})
|
||||
.setProtectedHeader({ alg: privateKey.alg, kid: privateKey.kid })
|
||||
.sign(keyPair.privateKey);
|
||||
});
|
||||
beforeAll(async () => {});
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks();
|
||||
@@ -86,6 +72,14 @@ describe('authModuleOidcProvider', () => {
|
||||
ctx.json(issuerMetadata),
|
||||
),
|
||||
),
|
||||
rest.get(
|
||||
'https://oidc.test/oauth2/authorize',
|
||||
async (req, _res, _ctx) => {
|
||||
nonce =
|
||||
new URL(req.url).searchParams.get('nonce') ??
|
||||
'nonceGeneratedByAuthServer';
|
||||
},
|
||||
),
|
||||
rest.get('https://oidc.test/oauth2/authorize', async (req, res, ctx) => {
|
||||
const callbackUrl = new URL(req.url.searchParams.get('redirect_uri')!);
|
||||
callbackUrl.searchParams.set('code', 'authorization_code');
|
||||
@@ -103,6 +97,22 @@ describe('authModuleOidcProvider', () => {
|
||||
res(ctx.status(200), ctx.json({ keys: [{ ...publicKey }] })),
|
||||
),
|
||||
rest.post('https://oidc.test/oauth2/token', async (req, res, ctx) => {
|
||||
const keyPair = await generateKeyPair('RS256');
|
||||
const privateKey = await exportJWK(keyPair.privateKey);
|
||||
publicKey = await exportJWK(keyPair.publicKey);
|
||||
publicKey.alg = privateKey.alg = 'RS256';
|
||||
|
||||
idToken = await new SignJWT({
|
||||
sub: 'test',
|
||||
iss: 'https://oidc.test',
|
||||
iat: Date.now(),
|
||||
aud: 'clientId',
|
||||
exp: Date.now() + 10000,
|
||||
nonce,
|
||||
})
|
||||
.setProtectedHeader({ alg: privateKey.alg, kid: privateKey.kid })
|
||||
.sign(keyPair.privateKey);
|
||||
|
||||
return res(
|
||||
req.headers.get('Authorization')
|
||||
? ctx.json({
|
||||
@@ -187,7 +197,8 @@ describe('authModuleOidcProvider', () => {
|
||||
const startUrl = new URL(startResponse.get('location'));
|
||||
expect(startUrl.origin).toBe('https://oidc.test');
|
||||
expect(startUrl.pathname).toBe('/oauth2/authorize');
|
||||
expect(Object.fromEntries(startUrl.searchParams)).toEqual({
|
||||
const expected = Object.fromEntries(startUrl.searchParams);
|
||||
expect(expected).toEqual({
|
||||
response_type: 'code',
|
||||
scope: 'openid profile email',
|
||||
client_id: 'clientId',
|
||||
@@ -196,6 +207,7 @@ describe('authModuleOidcProvider', () => {
|
||||
prompt: 'none',
|
||||
code_challenge: expect.any(String),
|
||||
code_challenge_method: `S256`,
|
||||
nonce: expected.nonce,
|
||||
});
|
||||
|
||||
expect(decodeOAuthState(startUrl.searchParams.get('state')!)).toEqual({
|
||||
|
||||
Reference in New Issue
Block a user