This commit is contained in:
@@ -516,6 +516,17 @@ export async function revokeRefreshToken(refreshToken: string): Promise<void> {
|
||||
])
|
||||
}
|
||||
|
||||
export async function revokeRefreshTokensForUser(userId: string): Promise<void> {
|
||||
await prisma.refreshToken.updateMany({
|
||||
where: {
|
||||
userId,
|
||||
revokedAt: null,
|
||||
expiresAt: { gt: new Date() }
|
||||
},
|
||||
data: { revokedAt: new Date() }
|
||||
})
|
||||
}
|
||||
|
||||
export async function createHandoffToken(userId: string, audienceApp: string): Promise<string> {
|
||||
const token = createOpaqueToken()
|
||||
await redis.set(
|
||||
|
||||
+12
-1
@@ -4,7 +4,7 @@ import { z } from 'zod'
|
||||
import { config } from '../config'
|
||||
import { sendEmailVerificationCode } from '../mail'
|
||||
import { verifyPassword } from '../security/password'
|
||||
import { signAccessToken } from '../security/tokens'
|
||||
import { signAccessToken, verifyAccessToken } from '../security/tokens'
|
||||
import type { AuthUser } from '../types'
|
||||
import { authRequired, type AuthVariables } from './middleware'
|
||||
import {
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
markLastLogin,
|
||||
recordFailedLogin,
|
||||
revokeRefreshToken,
|
||||
revokeRefreshTokensForUser,
|
||||
rotateRefreshToken,
|
||||
storeEmailVerificationCode,
|
||||
storePasswordResetCode,
|
||||
@@ -381,6 +382,16 @@ authRoutes.post('/logout', async (c) => {
|
||||
await revokeRefreshToken(refreshToken)
|
||||
}
|
||||
|
||||
const bearerToken = c.req.header('authorization')?.match(/^Bearer\s+(.+)$/i)?.[1]
|
||||
if (bearerToken) {
|
||||
try {
|
||||
const claims = await verifyAccessToken(bearerToken)
|
||||
await revokeRefreshTokensForUser(claims.sub)
|
||||
} catch {
|
||||
// Logout should remain idempotent even when the access token is already invalid.
|
||||
}
|
||||
}
|
||||
|
||||
clearRefreshCookie(c)
|
||||
return c.json({ ok: true })
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user