重构鉴权逻辑,新增中间件刷新令牌,授权接口统一后处理无授权跳转
This commit is contained in:
127
src/actions/auth.ts
Normal file
127
src/actions/auth.ts
Normal file
@@ -0,0 +1,127 @@
|
||||
'use server'
|
||||
import {cookies} from 'next/headers'
|
||||
import {ApiResponse, UnauthorizedError} from '@/lib/api'
|
||||
import {User} from '@/lib/models'
|
||||
import {callByDevice, callByUser} from '@/actions/base'
|
||||
|
||||
type TokenResp = {
|
||||
access_token: string
|
||||
refresh_token: string
|
||||
expires_in: number
|
||||
token_type: string
|
||||
scope?: string
|
||||
}
|
||||
|
||||
export async function login(props: {
|
||||
username: string
|
||||
password: string
|
||||
remember: boolean
|
||||
}): Promise<ApiResponse> {
|
||||
|
||||
// 尝试登录
|
||||
const result = await callByDevice<TokenResp>('/api/auth/token', {
|
||||
...props,
|
||||
grant_type: 'password',
|
||||
login_type: 'phone_code',
|
||||
})
|
||||
if (!result.success) {
|
||||
return result
|
||||
}
|
||||
|
||||
// 保存到 cookies
|
||||
const data = result.data
|
||||
const cookieStore = await cookies()
|
||||
cookieStore.set('auth_token', data.access_token, {
|
||||
httpOnly: true,
|
||||
sameSite: 'strict',
|
||||
maxAge: Math.max(data.expires_in, 0),
|
||||
})
|
||||
cookieStore.set('auth_refresh', data.refresh_token, {
|
||||
httpOnly: true,
|
||||
sameSite: 'strict',
|
||||
})
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: undefined,
|
||||
}
|
||||
}
|
||||
|
||||
export async function logout() {
|
||||
const cookieStore = await cookies()
|
||||
|
||||
// 尝试删除后台会话
|
||||
const access_token = cookieStore.get('auth_token')?.value
|
||||
const refresh_token = cookieStore.get('auth_refresh')?.value
|
||||
if (access_token && refresh_token) {
|
||||
await callByDevice('/api/auth/revoke', {
|
||||
access_token,
|
||||
refresh_token,
|
||||
})
|
||||
}
|
||||
|
||||
// 删除 cookies
|
||||
cookieStore.set('auth_token', '', {
|
||||
httpOnly: true,
|
||||
sameSite: 'strict',
|
||||
maxAge: -1,
|
||||
})
|
||||
cookieStore.set('auth_refresh', '', {
|
||||
httpOnly: true,
|
||||
sameSite: 'strict',
|
||||
maxAge: -1,
|
||||
})
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: undefined,
|
||||
}
|
||||
}
|
||||
|
||||
export async function getProfile() {
|
||||
return await callByUser<User>('/api/auth/introspect')
|
||||
}
|
||||
|
||||
export async function refreshAuth() {
|
||||
const cookie = await cookies()
|
||||
|
||||
const userRefresh = cookie.get('auth_refresh')?.value
|
||||
if (!userRefresh) {
|
||||
throw UnauthorizedError
|
||||
}
|
||||
|
||||
// 请求刷新访问令牌
|
||||
const resp = await callByDevice<TokenResp>(`/api/auth/token`, {
|
||||
grant_type: 'refresh_token',
|
||||
refresh_token: userRefresh,
|
||||
})
|
||||
|
||||
// 处理请求
|
||||
if (!resp.success) {
|
||||
cookie.delete('auth_refresh')
|
||||
throw UnauthorizedError
|
||||
}
|
||||
|
||||
// 解析响应
|
||||
const data = resp.data
|
||||
const nextAccessToken = data.access_token
|
||||
const nextRefreshToken = data.refresh_token
|
||||
const expiresIn = data.expires_in
|
||||
|
||||
// 保存令牌到 cookies
|
||||
cookie.set('auth_token', nextAccessToken, {
|
||||
httpOnly: true,
|
||||
sameSite: 'strict',
|
||||
maxAge: Math.max(expiresIn, 0),
|
||||
})
|
||||
cookie.set('auth_refresh', nextRefreshToken, {
|
||||
httpOnly: true,
|
||||
sameSite: 'strict',
|
||||
})
|
||||
|
||||
// 返回新的访问令牌
|
||||
return {
|
||||
access_token: nextAccessToken,
|
||||
refresh_token: nextRefreshToken,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user