Skip to content

Commit f0777d2

Browse files
committed
Client secrets should not be removed, but expiry checked in middleware
1 parent 094b81f commit f0777d2

File tree

3 files changed

+40
-6
lines changed

3 files changed

+40
-6
lines changed

src/server/auth/clients.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export interface OAuthRegisteredClientsStore {
1212
/**
1313
* Registers a new client with the server. The client ID and secret will be automatically generated by the library. A modified version of the client information can be returned to reflect specific values enforced by the server.
1414
*
15-
* NOTE: Implementations must ensure that client secrets, if present, are expired in accordance with the `client_secret_expires_at` field.
15+
* NOTE: Implementations should NOT delete expired client secrets in-place. Auth middleware provided by this library will automatically check the `client_secret_expires_at` field and reject requests with expired secrets. Any custom logic for authenticating clients should check the `client_secret_expires_at` field as well.
1616
*
1717
* If unimplemented, dynamic client registration is unsupported.
1818
*/

src/server/auth/middleware/clientAuth.test.ts

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,18 @@ describe('clientAuth middleware', () => {
1515
redirect_uris: ['https://example.com/callback']
1616
};
1717
} else if (clientId === 'expired-client') {
18-
// Client with expired secret
18+
// Client with no secret
1919
return {
2020
client_id: 'expired-client',
2121
redirect_uris: ['https://example.com/callback']
22-
// No client_secret due to expiration
22+
};
23+
} else if (clientId === 'client-with-expired-secret') {
24+
// Client with an expired secret
25+
return {
26+
client_id: 'client-with-expired-secret',
27+
client_secret: 'expired-secret',
28+
client_secret_expires_at: Math.floor(Date.now() / 1000) - 3600, // Expired 1 hour ago
29+
redirect_uris: ['https://example.com/callback']
2330
};
2431
}
2532
return undefined;
@@ -101,9 +108,22 @@ describe('clientAuth middleware', () => {
101108
client_id: 'expired-client'
102109
});
103110

104-
// Since the expired client has no secret, this should pass without providing one
111+
// Since the client has no secret, this should pass without providing one
105112
expect(response.status).toBe(200);
106113
});
114+
115+
it('rejects request when client secret has expired', async () => {
116+
const response = await supertest(app)
117+
.post('/protected')
118+
.send({
119+
client_id: 'client-with-expired-secret',
120+
client_secret: 'expired-secret'
121+
});
122+
123+
expect(response.status).toBe(400);
124+
expect(response.body.error).toBe('invalid_client');
125+
expect(response.body.error_description).toBe('Client secret has expired');
126+
});
107127

108128
it('handles malformed request body', async () => {
109129
const response = await supertest(app)

src/server/auth/middleware/clientAuth.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,22 @@ export function authenticateClient({ clientsStore }: ClientAuthenticationMiddlew
3939
throw new InvalidClientError("Invalid client_id");
4040
}
4141

42-
if (client.client_secret !== client_secret) {
43-
throw new InvalidClientError("Invalid client_secret");
42+
// If client has a secret, validate it
43+
if (client.client_secret) {
44+
// Check if client_secret is required but not provided
45+
if (!client_secret) {
46+
throw new InvalidClientError("Client secret is required");
47+
}
48+
49+
// Check if client_secret matches
50+
if (client.client_secret !== client_secret) {
51+
throw new InvalidClientError("Invalid client_secret");
52+
}
53+
54+
// Check if client_secret has expired
55+
if (client.client_secret_expires_at && client.client_secret_expires_at < Math.floor(Date.now() / 1000)) {
56+
throw new InvalidClientError("Client secret has expired");
57+
}
4458
}
4559

4660
req.client = client;

0 commit comments

Comments
 (0)