fix(api)!: enforce declared rate limits and correct route auth (#2500)

This commit is contained in:
Hampus authored and GitHub committed 2026-09-06 15:28:32 +02:00
1 parent 908e1b8bd4
commit f8a04b8985
13 files changed
+317 -77

No files matched your search

+4 -6
View File
@@ -181,21 +181,19 @@ export function AuthController(app: HonoApp) {
app.post(
'/auth/logout',
RateLimitMiddleware(RateLimitConfigs.AUTH_LOGOUT),
LoginRequiredAllowSuspicious,
OpenAPI({
operationId: 'logout_user',
summary: 'Logout account',
responseSchema: null,
statusCode: 204,
security: ['bearerToken', 'sessionToken'],
security: ['botToken', 'bearerToken', 'sessionToken'],
tags: ['Auth'],
description:
'Invalidate the current authentication token and end the session. The auth token in the Authorization header will no longer be valid.',
'Invalidate the current authentication token and end the session. The auth token in the Authorization header will no longer be valid. A bot token has no session to end, so the call answers 204 and the token stays valid.',
}),
async (ctx) => {
await ctx.get('authRequestService').logout({
authorizationHeader: ctx.req.header('Authorization') ?? undefined,
authToken: ctx.get('authToken') ?? undefined,
});
await ctx.get('authRequestService').logout({authToken: ctx.get('authToken')});
return ctx.body(null, 204);
},
);
@@ -84,7 +84,6 @@ interface AuthLoginMfaRequest {
}
interface AuthLogoutRequest {
authorizationHeader?: string;
authToken?: string;
}
@@ -187,10 +186,9 @@ export class AuthRequestService {
return await this.toAuthTokenResponse(result);
}
async logout({authorizationHeader, authToken}: AuthLogoutRequest): Promise<void> {
const token = authToken ?? authorizationHeader;
if (token) {
await AuthSession.revokeToken(this.apiContext, token);
async logout({authToken}: AuthLogoutRequest): Promise<void> {
if (authToken) {
await AuthSession.revokeToken(this.apiContext, authToken);
}
}
@@ -101,6 +101,18 @@ describe('Auth login and sessions', () => {
await createBuilder(harness, account.token).post('/auth/logout').expect(204).execute();
await createBuilder(harness, account.token).get('/users/@me').expect(401).execute();
});
it('logout requires a live session', async () => {
await createBuilderWithoutAuth(harness).post('/auth/logout').expect(401).execute();
await createBuilder(harness, createFakeAuthToken()).post('/auth/logout').expect(401).execute();
const account = await createTestAccount(harness);
await createBuilder(harness, account.token).post('/auth/logout').expect(204).execute();
await createBuilder(harness, account.token).post('/auth/logout').expect(401).execute();
});
it('logout with a Bearer-prefixed session token revokes it', async () => {
const account = await createTestAccount(harness);
await createBuilder(harness, `Bearer ${account.token}`).post('/auth/logout').expect(204).execute();
await createBuilder(harness, account.token).get('/users/@me').expect(401).execute();
});
it('treats /auth/sessions/logout as idempotent and removes targeted sessions', async () => {
let account: TestAccount = await createTestAccount(harness);
await createBuilder(harness, account.token)