chore: initial commit of external-app SSO consumer
Next.js sample application that integrates with the mock SSO service: redirects to sso-mock for login, receives the one-time sso_code on its /callback route, and exchanges it for user info to establish a session. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
74
app/callback/page.tsx
Normal file
74
app/callback/page.tsx
Normal file
@@ -0,0 +1,74 @@
|
||||
'use client'
|
||||
|
||||
import { Suspense } from 'react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useSearchParams } from 'next/navigation'
|
||||
import { Stack, Text, Loader } from '@mantine/core'
|
||||
|
||||
function CallbackContent() {
|
||||
const searchParams = useSearchParams()
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const code = searchParams.get('sso_code')
|
||||
if (!code) {
|
||||
setError('缺少 sso_code 参数')
|
||||
return
|
||||
}
|
||||
|
||||
// 用 code 调用本应用的 /api/callback 换取用户信息
|
||||
fetch('/api/callback', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ code }),
|
||||
})
|
||||
.then(async (res) => {
|
||||
if (!res.ok) {
|
||||
const data = await res.json()
|
||||
throw new Error(data.error || '认证失败')
|
||||
}
|
||||
// 成功 -> 跳转到首页
|
||||
window.location.href = '/'
|
||||
})
|
||||
.catch((err) => {
|
||||
setError(err instanceof Error ? err.message : '认证失败')
|
||||
})
|
||||
}, [searchParams])
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<Stack align="center" justify="center" h="100vh" gap="md">
|
||||
<Text size="lg" c="red">认证失败</Text>
|
||||
<Text size="sm" c="dimmed">{error}</Text>
|
||||
<Text
|
||||
size="sm"
|
||||
c="brand"
|
||||
style={{ cursor: 'pointer' }}
|
||||
onClick={() => (window.location.href = '/')}>
|
||||
返回首页重试
|
||||
</Text>
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Stack align="center" justify="center" h="100vh" gap="md">
|
||||
<Loader size="lg" />
|
||||
<Text size="sm" c="dimmed">正在验证 SSO 登录...</Text>
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
|
||||
export default function CallbackPage() {
|
||||
return (
|
||||
<Suspense
|
||||
fallback={
|
||||
<Stack align="center" justify="center" h="100vh" gap="md">
|
||||
<Loader size="lg" />
|
||||
<Text size="sm" c="dimmed">正在验证 SSO 登录...</Text>
|
||||
</Stack>
|
||||
}>
|
||||
<CallbackContent />
|
||||
</Suspense>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user