返回 AiToEarn
auth-callback-state.util.ts
根目录 / project / aitoearn-backend / apps / aitoearn-server / src / core / channels / utils / auth-callback-state.util.ts
1 import type { AuthCallbackInput } from '../platforms/platforms.interface'
2 import { AppException, ResponseCode } from '@yikart/common'
3 import { z } from 'zod'
4
5 const AuthCallbackPayloadSchema = z.object({
6 code: z.string().min(1).optional(),
7 state: z.string().min(1).optional(),
8 })
9
10 const AuthCallbackStateSchema = z.object({
11 state: z.string().min(1).optional(),
12 })
13
14 const AuthCodeVerifierSchema = z.object({
15 codeVerifier: z.string().min(1),
16 })
17
18 export interface OAuthCallback {
19 code: string
20 state: string
21 }
22
23 export function getAuthCallbackState(input: Pick<AuthCallbackInput, 'query' | 'body'>): string | undefined {
24 const result = AuthCallbackStateSchema.safeParse({
25 ...(input.body ?? {}),
26 ...(input.query ?? {}),
27 })
28
29 return result.success ? result.data.state : undefined
30 }
31
32 export function parseOAuthCallback(input: AuthCallbackInput): OAuthCallback {
33 const result = AuthCallbackPayloadSchema.safeParse({
34 ...(input.body ?? {}),
35 ...(input.query ?? {}),
36 })
37
38 if (!result.success) {
39 const hasInvalidState = result.error.issues.some(issue => issue.path[0] === 'state')
40 throw new AppException(hasInvalidState ? ResponseCode.ChannelAuthCsrfInvalid : ResponseCode.ChannelAuthCodeMissing)
41 }
42
43 assertParsedCallbackState(result.data.state, input.session.id)
44
45 if (!result.data.code) {
46 throw new AppException(ResponseCode.ChannelAuthCodeMissing)
47 }
48
49 return {
50 code: result.data.code,
51 state: result.data.state,
52 }
53 }
54
55 export function parseAuthCodeVerifier(input: AuthCallbackInput): string {
56 const result = AuthCodeVerifierSchema.safeParse(input.session.authExtras ?? {})
57 if (!result.success) {
58 throw new AppException(ResponseCode.ChannelAuthSessionInvalid)
59 }
60
61 return result.data.codeVerifier
62 }
63
64 export function assertAuthCallbackState(input: AuthCallbackInput): void {
65 assertParsedCallbackState(getAuthCallbackState(input), input.session.id)
66 }
67
68 export function assertParsedCallbackState(callbackState: string | undefined, sessionId: string): asserts callbackState is string {
69 if (callbackState !== sessionId) {
70 throw new AppException(ResponseCode.ChannelAuthCsrfInvalid)
71 }
72 }
73
73 lines TYPESCRIPT