deploy code
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
import { useAuth } from 'react-oidc-context';
|
||||
import { useState } from 'react';
|
||||
import { fetchProtected } from './api/client';
|
||||
import './App.css';
|
||||
|
||||
function App() {
|
||||
const auth = useAuth();
|
||||
const [apiData, setApiData] = useState<unknown>(null);
|
||||
const [apiError, setApiError] = useState<string | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
if (auth.isLoading) {
|
||||
return <div className="app">Loading session…</div>;
|
||||
}
|
||||
|
||||
if (auth.error) {
|
||||
const hint =
|
||||
auth.error.message === 'Failed to fetch'
|
||||
? 'The browser could not reach Authentik. Check that it is running, VITE_AUTHENTIK_URL is correct, and the discovery URL opens in a new tab.'
|
||||
: auth.error.message;
|
||||
return (
|
||||
<div className="app">
|
||||
<p className="error">Auth error: {hint}</p>
|
||||
<button type="button" onClick={() => auth.signinRedirect()}>
|
||||
Try again
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!auth.isAuthenticated || !auth.user) {
|
||||
return (
|
||||
<div className="app">
|
||||
<h1>OIDC Auth Demo</h1>
|
||||
<p>Sign in with Authentik to continue.</p>
|
||||
<button type="button" onClick={() => auth.signinRedirect()}>
|
||||
Sign in
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const idClaims = auth.user.profile;
|
||||
const accessToken = auth.user.access_token;
|
||||
|
||||
async function callApi() {
|
||||
if (!accessToken) {
|
||||
setApiError('No access token in session');
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
setApiError(null);
|
||||
try {
|
||||
const data = await fetchProtected('/api/me', accessToken);
|
||||
setApiData(data);
|
||||
} catch (err) {
|
||||
setApiData(null);
|
||||
setApiError(err instanceof Error ? err.message : 'API request failed');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="app">
|
||||
<header>
|
||||
<h1>OIDC Auth Demo</h1>
|
||||
<button type="button" className="secondary" onClick={() => auth.signoutRedirect()}>
|
||||
Sign out
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<section className="card">
|
||||
<h2>Login (ID Token)</h2>
|
||||
<p className="hint">
|
||||
User identity comes from the ID token claims below.
|
||||
</p>
|
||||
<dl>
|
||||
<dt>Subject</dt>
|
||||
<dd>{idClaims.sub}</dd>
|
||||
<dt>Email</dt>
|
||||
<dd>{String(idClaims.email ?? '—')}</dd>
|
||||
<dt>Name</dt>
|
||||
<dd>{String(idClaims.name ?? idClaims.preferred_username ?? '—')}</dd>
|
||||
</dl>
|
||||
</section>
|
||||
|
||||
<section className="card">
|
||||
<h2>API (Access Token)</h2>
|
||||
<p className="hint">
|
||||
Protected routes use the access token in the Authorization header.
|
||||
</p>
|
||||
<button type="button" onClick={callApi} disabled={loading}>
|
||||
{loading ? 'Calling API…' : 'GET /api/me'}
|
||||
</button>
|
||||
{apiError && <p className="error">{apiError}</p>}
|
||||
{apiData != null && (
|
||||
<pre>{JSON.stringify(apiData, null, 2)}</pre>
|
||||
)}
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
Reference in New Issue
Block a user