100 lines
2.4 KiB
TypeScript
100 lines
2.4 KiB
TypeScript
import { getPublicAppOrigin } from '@/lib/public-app-url'
|
|
import { normalizeInternalRedirectPath } from '@/lib/safe-redirect'
|
|
|
|
const FALLBACK_DINGTALK_CLIENT_ID = 'ding1bs1gxwrcfu16x0f'
|
|
export const DINGTALK_DIRECT_LOGIN_SOURCE = 'work_notice_direct'
|
|
|
|
export function getDingtalkOAuthClientId() {
|
|
return (
|
|
process.env.DINGTALK_CLIENT_ID ||
|
|
process.env.DINGTALK_APP_KEY ||
|
|
process.env.NEXT_PUBLIC_DINGTALK_CLIENT_ID ||
|
|
FALLBACK_DINGTALK_CLIENT_ID
|
|
)
|
|
}
|
|
|
|
export function encodeDingtalkOAuthState(
|
|
redirectPath: unknown,
|
|
origin = getPublicAppOrigin(),
|
|
options?: { source?: string },
|
|
) {
|
|
const payload: Record<string, string> = {
|
|
origin,
|
|
redirect: normalizeInternalRedirectPath(redirectPath),
|
|
}
|
|
|
|
if (options?.source) {
|
|
payload.source = options.source
|
|
}
|
|
|
|
return JSON.stringify(payload)
|
|
}
|
|
|
|
export function parseDingtalkOAuthState(value: unknown) {
|
|
let raw = String(value ?? '').trim()
|
|
if (!raw) {
|
|
return {
|
|
origin: '',
|
|
redirectPath: '/',
|
|
source: '',
|
|
nonce: '',
|
|
handoffId: '',
|
|
}
|
|
}
|
|
|
|
try {
|
|
raw = decodeURIComponent(raw)
|
|
} catch {
|
|
// URLSearchParams 已经会解码,遇到普通 JSON 字符串时这里可以安全忽略。
|
|
}
|
|
|
|
try {
|
|
const data = JSON.parse(raw)
|
|
return {
|
|
origin: typeof data?.origin === 'string' ? data.origin : '',
|
|
redirectPath: normalizeInternalRedirectPath(data?.redirect),
|
|
source: typeof data?.source === 'string' ? data.source : '',
|
|
nonce: typeof data?.nonce === 'string' ? data.nonce : '',
|
|
handoffId: typeof data?.handoffId === 'string' ? data.handoffId : '',
|
|
}
|
|
} catch {
|
|
return {
|
|
origin: '',
|
|
redirectPath: '/',
|
|
source: '',
|
|
nonce: '',
|
|
handoffId: '',
|
|
}
|
|
}
|
|
}
|
|
|
|
export function buildDingtalkOAuthUrl({
|
|
redirectPath,
|
|
callbackPath = '/api/dingtalk/callback',
|
|
clientId,
|
|
prompt,
|
|
source,
|
|
}: {
|
|
redirectPath: unknown
|
|
callbackPath?: string
|
|
clientId?: string
|
|
prompt?: string
|
|
source?: string
|
|
}) {
|
|
const publicOrigin = getPublicAppOrigin()
|
|
const redirectUrl = `${publicOrigin}${callbackPath}`
|
|
const params = new URLSearchParams({
|
|
redirect_uri: redirectUrl,
|
|
client_id: clientId || getDingtalkOAuthClientId(),
|
|
scope: 'openid',
|
|
response_type: 'code',
|
|
state: encodeDingtalkOAuthState(redirectPath, publicOrigin, { source }),
|
|
})
|
|
|
|
if (prompt) {
|
|
params.set('prompt', prompt)
|
|
}
|
|
|
|
return `https://login.dingtalk.com/oauth2/auth?${params.toString()}`
|
|
}
|