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> {
|
export async function createHandoffToken(userId: string, audienceApp: string): Promise<string> {
|
||||||
const token = createOpaqueToken()
|
const token = createOpaqueToken()
|
||||||
await redis.set(
|
await redis.set(
|
||||||
|
|||||||
+12
-1
@@ -4,7 +4,7 @@ import { z } from 'zod'
|
|||||||
import { config } from '../config'
|
import { config } from '../config'
|
||||||
import { sendEmailVerificationCode } from '../mail'
|
import { sendEmailVerificationCode } from '../mail'
|
||||||
import { verifyPassword } from '../security/password'
|
import { verifyPassword } from '../security/password'
|
||||||
import { signAccessToken } from '../security/tokens'
|
import { signAccessToken, verifyAccessToken } from '../security/tokens'
|
||||||
import type { AuthUser } from '../types'
|
import type { AuthUser } from '../types'
|
||||||
import { authRequired, type AuthVariables } from './middleware'
|
import { authRequired, type AuthVariables } from './middleware'
|
||||||
import {
|
import {
|
||||||
@@ -25,6 +25,7 @@ import {
|
|||||||
markLastLogin,
|
markLastLogin,
|
||||||
recordFailedLogin,
|
recordFailedLogin,
|
||||||
revokeRefreshToken,
|
revokeRefreshToken,
|
||||||
|
revokeRefreshTokensForUser,
|
||||||
rotateRefreshToken,
|
rotateRefreshToken,
|
||||||
storeEmailVerificationCode,
|
storeEmailVerificationCode,
|
||||||
storePasswordResetCode,
|
storePasswordResetCode,
|
||||||
@@ -381,6 +382,16 @@ authRoutes.post('/logout', async (c) => {
|
|||||||
await revokeRefreshToken(refreshToken)
|
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)
|
clearRefreshCookie(c)
|
||||||
return c.json({ ok: true })
|
return c.json({ ok: true })
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user