1
+ import { Request , Response } from 'express' ;
2
+ import { AuthService } from './feishuAuthService.js' ;
3
+ import { Config } from '../utils/config.js' ;
4
+ import { CacheManager } from '../utils/cache.js' ;
5
+ import { renderFeishuAuthResultHtml } from '../utils/document.js' ;
6
+
7
+ // 通用响应码
8
+ const CODE = {
9
+ SUCCESS : 0 ,
10
+ PARAM_ERROR : 400 ,
11
+ CUSTOM : 500 ,
12
+ } ;
13
+
14
+ // 封装响应方法
15
+ function sendSuccess ( res : Response , data : any ) {
16
+ const html = renderFeishuAuthResultHtml ( data ) ;
17
+ res . setHeader ( 'Content-Type' , 'text/html; charset=utf-8' ) ;
18
+ res . status ( 200 ) . send ( html ) ;
19
+ }
20
+ function sendFail ( res : Response , msg : string , code : number = CODE . CUSTOM ) {
21
+ const html = renderFeishuAuthResultHtml ( { error : msg , code } ) ;
22
+ res . setHeader ( 'Content-Type' , 'text/html; charset=utf-8' ) ;
23
+ res . status ( 200 ) . send ( html ) ;
24
+ }
25
+
26
+ const authService = new AuthService ( ) ;
27
+ const config = Config . getInstance ( ) ;
28
+
29
+ export async function callback ( req : Request , res : Response ) {
30
+ const code = req . query . code as string ;
31
+ const state = req . query . state as string ;
32
+ console . log ( `[callback] query:` , req . query ) ;
33
+ if ( ! code ) {
34
+ console . log ( '[callback] 缺少code参数' ) ;
35
+ return sendFail ( res , '缺少code参数' , CODE . PARAM_ERROR ) ;
36
+ }
37
+ // 校验state(clientKey)
38
+ const client_id = config . feishu . appId ;
39
+ const client_secret = config . feishu . appSecret ;
40
+ const expectedClientKey = await CacheManager . getClientKey ( client_id , client_secret ) ;
41
+ if ( state !== expectedClientKey ) {
42
+ console . log ( '[callback] state(clientKey)不匹配' ) ;
43
+ return sendFail ( res , 'state(clientKey)不匹配' , CODE . PARAM_ERROR ) ;
44
+ }
45
+
46
+ const redirect_uri = `http://localhost:${ config . server . port } /callback` ;
47
+ const session = ( req as any ) . session ;
48
+ const code_verifier = session ?. code_verifier || undefined ;
49
+
50
+ try {
51
+ // 获取 user_access_token
52
+ const tokenResp = await authService . getUserTokenByCode ( {
53
+ client_id,
54
+ client_secret,
55
+ code,
56
+ redirect_uri,
57
+ code_verifier
58
+ } ) ;
59
+ const data = ( tokenResp && typeof tokenResp === 'object' ) ? tokenResp : undefined ;
60
+ console . log ( '[callback] feishu response:' , data ) ;
61
+ if ( ! data || data . code !== 0 || ! data . access_token ) {
62
+ return sendFail ( res , `获取 access_token 失败,飞书返回: ${ JSON . stringify ( tokenResp ) } ` , CODE . CUSTOM ) ;
63
+ }
64
+ // 获取用户信息
65
+ const access_token = data . access_token ;
66
+ let userInfo = null ;
67
+ if ( access_token ) {
68
+ userInfo = await authService . getUserInfo ( access_token ) ;
69
+ console . log ( '[callback] feishu userInfo:' , userInfo ) ;
70
+ }
71
+ return sendSuccess ( res , { ...data , userInfo } ) ;
72
+ } catch ( e ) {
73
+ console . error ( '[callback] 请求飞书token或用户信息失败:' , e ) ;
74
+ return sendFail ( res , `请求飞书token或用户信息失败: ${ e } ` , CODE . CUSTOM ) ;
75
+ }
76
+ }
77
+
78
+ export async function getTokenByParams ( { client_id, client_secret, token_type } : { client_id : string , client_secret : string , token_type ?: string } ) {
79
+ const authService = new AuthService ( ) ;
80
+ if ( client_id ) authService . config . feishu . appId = client_id ;
81
+ if ( client_secret ) authService . config . feishu . appSecret = client_secret ;
82
+ if ( token_type ) authService . config . feishu . authType = token_type === 'user' ? 'user' : 'tenant' ;
83
+ return await authService . getToken ( ) ;
84
+ }
0 commit comments