chore: clientId and clientSecret are not to be passed to the token endpoint

Signed-off-by: benjdlambert <ben@blam.sh>
This commit is contained in:
benjdlambert
2025-09-08 13:12:36 +02:00
parent 838429ac89
commit 025fdd20ea
4 changed files with 3 additions and 164 deletions
@@ -548,8 +548,6 @@ describe('OidcRouter', () => {
.send({
grant_type: 'authorization_code',
code: authorizationCode,
client_id: client.clientId,
client_secret: client.clientSecret,
redirect_uri: 'https://example.com/callback',
})
.expect(200);
@@ -646,8 +644,6 @@ describe('OidcRouter', () => {
.send({
grant_type: 'authorization_code',
code: authorizationCode,
client_id: client.clientId,
client_secret: client.clientSecret,
redirect_uri: 'https://example.com/callback',
code_verifier: codeVerifier,
})
@@ -668,95 +664,8 @@ describe('OidcRouter', () => {
});
});
it('should reject token exchange with invalid client credentials', async () => {
const {
mocks: { auth, service, httpAuth },
router,
} = await createRouter(databaseId);
httpAuth.credentials.mockResolvedValueOnce({
principal: {
type: 'user',
userEntityRef: 'user:default/test-user',
},
$$type: '@backstage/BackstageCredentials',
});
auth.isPrincipal.mockReturnValueOnce(true);
const client = await service.registerClient({
clientName: 'Test Client',
redirectUris: ['https://example.com/callback'],
responseTypes: ['code'],
grantTypes: ['authorization_code'],
scope: 'openid',
});
const authSession = await service.createAuthorizationSession({
clientId: client.clientId,
redirectUri: 'https://example.com/callback',
responseType: 'code',
scope: 'openid',
});
const { server } = await startTestBackend({
features: [
createBackendPlugin({
pluginId: 'auth',
register(reg) {
reg.registerInit({
deps: { httpRouter: coreServices.httpRouter },
async init({ httpRouter }) {
httpRouter.use(router.getRouter());
httpRouter.addAuthPolicy({
path: '/',
allow: 'unauthenticated',
});
},
});
},
}),
],
});
const approvalResponse = await request(server)
.post(`/api/auth/v1/sessions/${authSession.id}/approve`)
.set('Authorization', `Bearer ${MOCK_USER_TOKEN}`)
.expect(200);
const redirectUrl = new URL(approvalResponse.body.redirectUrl);
const authorizationCode = redirectUrl.searchParams.get('code');
const tokenResponse = await request(server)
.post('/api/auth/v1/token')
.send({
grant_type: 'authorization_code',
code: authorizationCode,
client_id: client.clientId,
client_secret: 'invalid-secret',
redirect_uri: 'https://example.com/callback',
})
.expect(401);
expect(tokenResponse.body).toEqual({
error: 'invalid_client',
error_description: 'Invalid client credentials',
});
});
it('should reject token exchange with invalid authorization code', async () => {
const {
mocks: { service },
router,
} = await createRouter(databaseId);
const client = await service.registerClient({
clientName: 'Test Client',
redirectUris: ['https://example.com/callback'],
responseTypes: ['code'],
grantTypes: ['authorization_code'],
scope: 'openid',
});
const { router } = await createRouter(databaseId);
const { server } = await startTestBackend({
features: [
@@ -783,8 +692,6 @@ describe('OidcRouter', () => {
.send({
grant_type: 'authorization_code',
code: 'invalid-code',
client_id: client.clientId,
client_secret: client.clientSecret,
redirect_uri: 'https://example.com/callback',
})
.expect(401);
@@ -875,8 +782,6 @@ describe('OidcRouter', () => {
.send({
grant_type: 'authorization_code',
code: authorizationCode,
client_id: client.clientId,
client_secret: client.clientSecret,
redirect_uri: 'https://example.com/callback',
code_verifier: codeVerifier,
})
@@ -276,13 +276,11 @@ export class OidcRouter {
const {
grant_type: grantType,
code,
client_id: clientId,
client_secret: clientSecret,
redirect_uri: redirectUri,
code_verifier: codeVerifier,
} = req.body;
if (!grantType || !code || !clientId || !clientSecret || !redirectUri) {
if (!grantType || !code || !redirectUri) {
this.logger.error(
`Failed to exchange code for token: Missing required parameters`,
);
@@ -295,8 +293,6 @@ export class OidcRouter {
try {
const result = await this.oidc.exchangeCodeForToken({
code,
clientId,
clientSecret,
redirectUri,
codeVerifier,
grantType,
@@ -668,8 +668,6 @@ describe('OidcService', () => {
const tokenResult = await service.exchangeCodeForToken({
code,
clientId: client.clientId,
clientSecret: client.clientSecret,
redirectUri: 'https://example.com/callback',
grantType: 'authorization_code',
});
@@ -689,47 +687,12 @@ describe('OidcService', () => {
await expect(
service.exchangeCodeForToken({
code: 'test-code',
clientId: 'test-client',
clientSecret: 'test-secret',
redirectUri: 'https://example.com/callback',
grantType: 'client_credentials',
}),
).rejects.toThrow('Unsupported grant type');
});
it('should throw error for invalid client', async () => {
const { service } = await createOidcService(databaseId);
await expect(
service.exchangeCodeForToken({
code: 'test-code',
clientId: 'invalid-client',
clientSecret: 'test-secret',
redirectUri: 'https://example.com/callback',
grantType: 'authorization_code',
}),
).rejects.toThrow('Invalid client');
});
it('should throw error for invalid client secret', async () => {
const { service } = await createOidcService(databaseId);
const client = await service.registerClient({
clientName: 'Test Client',
redirectUris: ['https://example.com/callback'],
});
await expect(
service.exchangeCodeForToken({
code: 'test-code',
clientId: client.clientId,
clientSecret: 'invalid-secret',
redirectUri: 'https://example.com/callback',
grantType: 'authorization_code',
}),
).rejects.toThrow('Invalid client credentials');
});
it('should handle PKCE verification', async () => {
const { service, mocks } = await createOidcService(databaseId);
const mockToken = 'mock-jwt-token';
@@ -759,8 +722,6 @@ describe('OidcService', () => {
const tokenResult = await service.exchangeCodeForToken({
code,
clientId: client.clientId,
clientSecret: client.clientSecret,
redirectUri: 'https://example.com/callback',
grantType: 'authorization_code',
codeVerifier,
@@ -792,8 +753,6 @@ describe('OidcService', () => {
await expect(
service.exchangeCodeForToken({
code,
clientId: client.clientId,
clientSecret: client.clientSecret,
redirectUri: 'https://example.com/callback',
grantType: 'authorization_code',
codeVerifier: 'invalid-verifier',
@@ -396,34 +396,16 @@ export class OidcService {
public async exchangeCodeForToken(params: {
code: string;
clientId: string;
clientSecret: string;
redirectUri: string;
codeVerifier?: string;
grantType: string;
}) {
const {
code,
clientId,
clientSecret,
redirectUri,
codeVerifier,
grantType,
} = params;
const { code, redirectUri, codeVerifier, grantType } = params;
if (grantType !== 'authorization_code') {
throw new InputError('Unsupported grant type');
}
const client = await this.oidc.getClient({ clientId });
if (!client) {
throw new AuthenticationError('Invalid client');
}
if (client.clientSecret !== clientSecret) {
throw new AuthenticationError('Invalid client credentials');
}
const authCode = await this.oidc.getAuthorizationCode({ code });
if (!authCode) {
throw new AuthenticationError('Invalid authorization code');
@@ -444,9 +426,6 @@ export class OidcService {
if (!session) {
throw new NotFoundError('Invalid authorization session');
}
if (session.clientId !== clientId) {
throw new AuthenticationError('Client ID mismatch');
}
if (session.redirectUri !== redirectUri) {
throw new AuthenticationError('Redirect URI mismatch');