Files
web/src/actions/auth.ts

135 lines
2.9 KiB
TypeScript

'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 type LoginMode = 'phone_code' | 'password'
export async function login(props: {
username: string
password: string
remember: boolean
mode: LoginMode
}): Promise<ApiResponse> {
// 尝试登录
const result = await callByDevice<TokenResp>('/api/auth/token', {
...props,
grant_type: 'password',
login_type: props.mode,
})
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',
maxAge: Number.MAX_SAFE_INTEGER,
})
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 callByUser('/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) {
if (resp.status === 401) {
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',
maxAge: Number.MAX_SAFE_INTEGER,
})
// 返回新的访问令牌
return {
access_token: nextAccessToken,
refresh_token: nextRefreshToken,
}
}