import { z } from 'zod' import { getClientById, verifyClientSecret, createToken, createClientCredentialsToken, createTokenWithoutRefresh, getAuthorizationCode, useAuthorizationCode, validateRedirectUri, getTokenByRefreshToken, revokeToken } from '../../modules/oauth' import { getUserByUsername, verifyPassword } from '../../modules/auth/user' const tokenSchema = z.object({ grant_type: z.enum(['authorization_code', 'password', 'client_credentials', 'refresh_token']), client_id: z.string().min(1), client_secret: z.string().optional() }) const authCodeSchema = z.object({ code: z.string().min(1), redirect_uri: z.string().min(1) }) const passwordSchema = z.object({ username: z.string().min(1), password: z.string().min(1), scope: z.string().optional() }) const clientCredsSchema = z.object({ scope: z.string().optional() }) const refreshSchema = z.object({ refresh_token: z.string().min(1) }) export default defineEventHandler(async (event) => { const body = await readBody(event) const baseResult = tokenSchema.safeParse(body) if (!baseResult.success) { throw createError({ statusCode: 400, message: baseResult.error.errors[0].message }) } const { grant_type, client_id, client_secret } = baseResult.data const client = getClientById(client_id) if (!client) { throw createError({ statusCode: 400, message: '无效的客户端' }) } if (client_secret !== undefined && !verifyClientSecret(client, client_secret)) { throw createError({ statusCode: 401, message: '客户端密钥错误' }) } switch (grant_type) { case 'authorization_code': { if (!client.grant_types.includes('authorization_code')) { throw createError({ statusCode: 400, message: '客户端不支持授权码模式' }) } const codeResult = authCodeSchema.safeParse(body) if (!codeResult.success) { throw createError({ statusCode: 400, message: codeResult.error.errors[0].message }) } const { code, redirect_uri } = codeResult.data const authCode = getAuthorizationCode(code) if (!authCode) { throw createError({ statusCode: 400, message: '无效或已过期的授权码' }) } if (authCode.client_id !== client_id) { throw createError({ statusCode: 400, message: '授权码与客户端不匹配' }) } if (!validateRedirectUri(client, redirect_uri)) { throw createError({ statusCode: 400, message: '重定向地址不匹配' }) } if (authCode.code_challenge) { throw createError({ statusCode: 400, message: '需要 PKCE 验证' }) } useAuthorizationCode(code) const { accessToken, refreshToken, expiresAt } = createToken( authCode.user_id, client_id, authCode.scope || undefined ) return { success: true, data: { access_token: accessToken, token_type: 'Bearer', expires_in: Math.floor((expiresAt.getTime() - Date.now()) / 1000), refresh_token: refreshToken, scope: authCode.scope } } } case 'password': { if (!client.grant_types.includes('password')) { throw createError({ statusCode: 400, message: '客户端不支持密码模式' }) } const pwdResult = passwordSchema.safeParse(body) if (!pwdResult.success) { throw createError({ statusCode: 400, message: pwdResult.error.errors[0].message }) } const { username, password, scope } = pwdResult.data const user = getUserByUsername(username) if (!user || !verifyPassword(password, user.password_hash!)) { throw createError({ statusCode: 401, message: '用户名或密码错误' }) } if (user.status !== 'active') { throw createError({ statusCode: 403, message: '账户已被禁用' }) } const { accessToken, refreshToken, expiresAt } = createToken(user.id, client_id, scope) return { success: true, data: { access_token: accessToken, token_type: 'Bearer', expires_in: Math.floor((expiresAt.getTime() - Date.now()) / 1000), refresh_token: refreshToken, scope } } } case 'client_credentials': { if (!client.grant_types.includes('client_credentials')) { throw createError({ statusCode: 400, message: '客户端不支持客户端凭据模式' }) } const credsResult = clientCredsSchema.safeParse(body) if (!credsResult.success) { throw createError({ statusCode: 400, message: credsResult.error.errors[0].message }) } const { accessToken, expiresAt } = createClientCredentialsToken(client_id, credsResult.data.scope) return { success: true, data: { access_token: accessToken, token_type: 'Bearer', expires_in: Math.floor((expiresAt.getTime() - Date.now()) / 1000), scope: credsResult.data.scope } } } case 'refresh_token': { if (!client.grant_types.includes('refresh_token')) { throw createError({ statusCode: 400, message: '客户端不支持刷新令牌模式' }) } const refreshResult = refreshSchema.safeParse(body) if (!refreshResult.success) { throw createError({ statusCode: 400, message: refreshResult.error.errors[0].message }) } const { refresh_token } = refreshResult.data const oldToken = getTokenByRefreshToken(refresh_token) if (!oldToken || oldToken.client_id !== client_id) { throw createError({ statusCode: 400, message: '无效的刷新令牌' }) } revokeToken(refresh_token, 'refresh') const { accessToken, refreshToken: newRefreshToken, expiresAt } = createToken( oldToken.user_id!, client_id, oldToken.scope || undefined ) return { success: true, data: { access_token: accessToken, token_type: 'Bearer', expires_in: Math.floor((expiresAt.getTime() - Date.now()) / 1000), refresh_token: newRefreshToken, scope: oldToken.scope } } } default: throw createError({ statusCode: 400, message: '不支持的授权类型' }) } })