fenghuo/apps/web/app/test-oidc/page.tsx

249 lines
8.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client';
import { useState } from 'react';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@repo/ui/components/card';
import { Button } from '@repo/ui/components/button';
import { Badge } from '@repo/ui/components/badge';
import { Alert, AlertDescription } from '@repo/ui/components/alert';
import { CheckCircle, XCircle, Loader2, ArrowRight, Key, User, Shield } from 'lucide-react';
export default function TestOidcPage() {
const [testResults, setTestResults] = useState<{
discovery: 'idle' | 'loading' | 'success' | 'error';
discoveryData?: any;
discoveryError?: string;
}>({
discovery: 'idle',
});
const testDiscoveryEndpoint = async () => {
setTestResults((prev) => ({ ...prev, discovery: 'loading' }));
try {
const response = await fetch('http://localhost:3000/oidc/.well-known/openid_configuration');
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
setTestResults((prev) => ({
...prev,
discovery: 'success',
discoveryData: data,
}));
} catch (error) {
setTestResults((prev) => ({
...prev,
discovery: 'error',
discoveryError: error instanceof Error ? error.message : '未知错误',
}));
}
};
const startOidcFlow = () => {
const params = new URLSearchParams({
response_type: 'code',
client_id: 'demo-client',
redirect_uri: 'http://localhost:3001/auth/callback',
scope: 'openid profile email',
state: `test-${Date.now()}`,
});
window.location.href = `http://localhost:3000/oidc/auth?${params.toString()}`;
};
const getStatusIcon = (status: string) => {
switch (status) {
case 'loading':
return <Loader2 className="h-5 w-5 animate-spin text-blue-500" />;
case 'success':
return <CheckCircle className="h-5 w-5 text-green-500" />;
case 'error':
return <XCircle className="h-5 w-5 text-red-500" />;
default:
return <div className="h-5 w-5 rounded-full border-2 border-gray-300" />;
}
};
return (
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100 dark:from-gray-900 dark:to-gray-800 p-8">
<div className="max-w-4xl mx-auto space-y-8">
{/* 页面标题 */}
<div className="text-center">
<h1 className="text-3xl font-bold text-gray-900 dark:text-white mb-4">OIDC </h1>
<p className="text-lg text-gray-600 dark:text-gray-300"> OpenID Connect </p>
</div>
{/* OIDC 流程步骤 */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Shield className="h-5 w-5" />
OIDC
</CardTitle>
<CardDescription> OIDC OIDC Provider </CardDescription>
</CardHeader>
<CardContent className="space-y-6">
{/* 流程步骤图示 */}
<div className="grid grid-cols-1 md:grid-cols-5 gap-4 text-center">
<div className="flex flex-col items-center space-y-2">
<div className="w-12 h-12 bg-blue-100 dark:bg-blue-900 rounded-full flex items-center justify-center">
<User className="h-6 w-6 text-blue-600 dark:text-blue-300" />
</div>
<p className="text-sm font-medium"></p>
</div>
<div className="flex items-center justify-center">
<ArrowRight className="h-5 w-5 text-gray-400" />
</div>
<div className="flex flex-col items-center space-y-2">
<div className="w-12 h-12 bg-green-100 dark:bg-green-900 rounded-full flex items-center justify-center">
<Shield className="h-6 w-6 text-green-600 dark:text-green-300" />
</div>
<p className="text-sm font-medium"> Provider</p>
</div>
<div className="flex items-center justify-center">
<ArrowRight className="h-5 w-5 text-gray-400" />
</div>
<div className="flex flex-col items-center space-y-2">
<div className="w-12 h-12 bg-purple-100 dark:bg-purple-900 rounded-full flex items-center justify-center">
<Key className="h-6 w-6 text-purple-600 dark:text-purple-300" />
</div>
<p className="text-sm font-medium"></p>
</div>
</div>
{/* 测试按钮 */}
<div className="flex justify-center">
<Button onClick={startOidcFlow} size="lg" className="px-8">
OIDC
</Button>
</div>
{/* 提示信息 */}
<Alert>
<Shield className="h-4 w-4" />
<AlertDescription>
OIDC Provider
<br />
<strong>:</strong> demouser / demo123
</AlertDescription>
</Alert>
</CardContent>
</Card>
{/* Discovery 端点测试 */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
{getStatusIcon(testResults.discovery)}
Discovery
</CardTitle>
<CardDescription> OIDC Provider </CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<Button onClick={testDiscoveryEndpoint} disabled={testResults.discovery === 'loading'}>
{testResults.discovery === 'loading' ? '测试中...' : '测试 Discovery 端点'}
</Button>
{testResults.discovery === 'error' && (
<Alert variant="destructive">
<XCircle className="h-4 w-4" />
<AlertDescription>
<strong>:</strong> {testResults.discoveryError}
</AlertDescription>
</Alert>
)}
{testResults.discovery === 'success' && testResults.discoveryData && (
<div className="space-y-4">
<Alert>
<CheckCircle className="h-4 w-4" />
<AlertDescription>
<strong>!</strong> OIDC Provider
</AlertDescription>
</Alert>
<div className="bg-gray-50 dark:bg-gray-800 p-4 rounded-lg">
<h4 className="font-semibold mb-3">Provider </h4>
<div className="space-y-2 text-sm">
<p>
<strong>Issuer:</strong>{' '}
<code className="bg-gray-200 dark:bg-gray-700 px-1 rounded">
{testResults.discoveryData.issuer}
</code>
</p>
<p>
<strong>:</strong>{' '}
<code className="bg-gray-200 dark:bg-gray-700 px-1 rounded">
{testResults.discoveryData.authorization_endpoint}
</code>
</p>
<p>
<strong>:</strong>{' '}
<code className="bg-gray-200 dark:bg-gray-700 px-1 rounded">
{testResults.discoveryData.token_endpoint}
</code>
</p>
</div>
</div>
<div>
<h4 className="font-semibold mb-3"></h4>
<div className="flex flex-wrap gap-2">
{testResults.discoveryData.response_types_supported?.map((type: string) => (
<Badge key={type} variant="outline">
{type}
</Badge>
))}
</div>
<div className="flex flex-wrap gap-2 mt-2">
{testResults.discoveryData.scopes_supported?.map((scope: string) => (
<Badge key={scope} variant="secondary">
{scope}
</Badge>
))}
</div>
</div>
</div>
)}
</CardContent>
</Card>
{/* 架构说明 */}
<Card>
<CardHeader>
<CardTitle> OIDC </CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<h4 className="font-semibold text-green-600 mb-2"> </h4>
<ul className="space-y-1 text-sm">
<li> OIDC Provider </li>
<li> </li>
<li> PKCE </li>
<li> </li>
<li> </li>
</ul>
</div>
<div>
<h4 className="font-semibold text-red-600 mb-2"> </h4>
<ul className="space-y-1 text-sm">
<li> </li>
<li> </li>
<li> </li>
<li> </li>
</ul>
</div>
</div>
</CardContent>
</Card>
</div>
</div>
);
}