feat: added disable auth mode
This commit is contained in:
@@ -8,6 +8,7 @@
|
|||||||
## Запуск
|
## Запуск
|
||||||
|
|
||||||
1. Скопируй `server/.env.example` в `server/.env` и заполни OIDC параметры.
|
1. Скопируй `server/.env.example` в `server/.env` и заполни OIDC параметры.
|
||||||
|
- Если нужно запустить без авторизации, поставь `DISABLE_AUTH=true` (OIDC параметры тогда не требуются).
|
||||||
2. В одном терминале:
|
2. В одном терминале:
|
||||||
- `cd server`
|
- `cd server`
|
||||||
- `npm run dev`
|
- `npm run dev`
|
||||||
|
|||||||
@@ -20,6 +20,32 @@
|
|||||||
color: #5a6475;
|
color: #5a6475;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.settings {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding: 12px;
|
||||||
|
border: 1px solid #e6eaf2;
|
||||||
|
border-radius: 10px;
|
||||||
|
background: #f8fafc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
color: #0f172a;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle input {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hint {
|
||||||
|
margin: 10px 0 0;
|
||||||
|
color: #5a6475;
|
||||||
|
}
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
|||||||
@@ -7,14 +7,30 @@ type UserProfile = {
|
|||||||
email?: string
|
email?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const NO_AUTH_STORAGE_KEY = 'furumiNodePlayer.runWithoutAuth'
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [loading, setLoading] = useState(true)
|
const [loading, setLoading] = useState(true)
|
||||||
const [user, setUser] = useState<UserProfile | null>(null)
|
const [user, setUser] = useState<UserProfile | null>(null)
|
||||||
const [error, setError] = useState<string | null>(null)
|
const [error, setError] = useState<string | null>(null)
|
||||||
|
const [runWithoutAuth, setRunWithoutAuth] = useState(() => {
|
||||||
|
try {
|
||||||
|
return window.localStorage.getItem(NO_AUTH_STORAGE_KEY) === '1'
|
||||||
|
} catch {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const apiBase = useMemo(() => import.meta.env.VITE_API_BASE_URL ?? '', [])
|
const apiBase = useMemo(() => import.meta.env.VITE_API_BASE_URL ?? '', [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (runWithoutAuth) {
|
||||||
|
setError(null)
|
||||||
|
setUser({ sub: 'noauth', name: 'No Auth' })
|
||||||
|
setLoading(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const loadMe = async () => {
|
const loadMe = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${apiBase}/api/me`, {
|
const response = await fetch(`${apiBase}/api/me`, {
|
||||||
@@ -40,7 +56,7 @@ function App() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void loadMe()
|
void loadMe()
|
||||||
}, [apiBase])
|
}, [apiBase, runWithoutAuth])
|
||||||
|
|
||||||
const loginUrl = `${apiBase}/api/login`
|
const loginUrl = `${apiBase}/api/login`
|
||||||
const logoutUrl = `${apiBase}/api/logout`
|
const logoutUrl = `${apiBase}/api/logout`
|
||||||
@@ -51,9 +67,37 @@ function App() {
|
|||||||
<h1>OIDC Login</h1>
|
<h1>OIDC Login</h1>
|
||||||
<p className="subtitle">Авторизация обрабатывается на Express сервере.</p>
|
<p className="subtitle">Авторизация обрабатывается на Express сервере.</p>
|
||||||
|
|
||||||
|
<div className="settings">
|
||||||
|
<label className="toggle">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={runWithoutAuth}
|
||||||
|
onChange={(e) => {
|
||||||
|
const next = e.target.checked
|
||||||
|
setRunWithoutAuth(next)
|
||||||
|
try {
|
||||||
|
if (next) window.localStorage.setItem(NO_AUTH_STORAGE_KEY, '1')
|
||||||
|
else window.localStorage.removeItem(NO_AUTH_STORAGE_KEY)
|
||||||
|
} catch {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
setLoading(true)
|
||||||
|
setUser(null)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<span>Запускать без авторизации</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
{loading && <p>Проверяю сессию...</p>}
|
{loading && <p>Проверяю сессию...</p>}
|
||||||
{error && <p className="error">Ошибка: {error}</p>}
|
{error && <p className="error">Ошибка: {error}</p>}
|
||||||
|
|
||||||
|
{!loading && runWithoutAuth && (
|
||||||
|
<p className="hint">
|
||||||
|
Режим без авторизации включён. Для входа отключи настройку выше.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
|
||||||
{!loading && !user && (
|
{!loading && !user && (
|
||||||
<a className="btn" href={loginUrl}>
|
<a className="btn" href={loginUrl}>
|
||||||
Войти через OIDC
|
Войти через OIDC
|
||||||
@@ -75,9 +119,11 @@ function App() {
|
|||||||
<strong>Email:</strong> {user.email}
|
<strong>Email:</strong> {user.email}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
{!runWithoutAuth && (
|
||||||
<a className="btn ghost" href={logoutUrl}>
|
<a className="btn ghost" href={logoutUrl}>
|
||||||
Выйти
|
Выйти
|
||||||
</a>
|
</a>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ BASE_URL=http://localhost:3001
|
|||||||
FRONTEND_ORIGIN=http://localhost:5173
|
FRONTEND_ORIGIN=http://localhost:5173
|
||||||
SESSION_SECRET=super-long-random-secret
|
SESSION_SECRET=super-long-random-secret
|
||||||
|
|
||||||
|
# Если true/1/on/yes — сервер стартует без OIDC и не требует авторизации.
|
||||||
|
DISABLE_AUTH=false
|
||||||
|
|
||||||
OIDC_ISSUER_BASE_URL=https://your-issuer.example.com
|
OIDC_ISSUER_BASE_URL=https://your-issuer.example.com
|
||||||
OIDC_CLIENT_ID=your-client-id
|
OIDC_CLIENT_ID=your-client-id
|
||||||
OIDC_CLIENT_SECRET=your-client-secret
|
OIDC_CLIENT_SECRET=your-client-secret
|
||||||
|
|||||||
@@ -9,6 +9,10 @@ const app = express();
|
|||||||
const port = Number(process.env.PORT ?? 3001);
|
const port = Number(process.env.PORT ?? 3001);
|
||||||
const frontendOrigin = process.env.FRONTEND_ORIGIN ?? 'http://localhost:5173';
|
const frontendOrigin = process.env.FRONTEND_ORIGIN ?? 'http://localhost:5173';
|
||||||
|
|
||||||
|
const disableAuth = ['1', 'true', 'yes', 'on'].includes(
|
||||||
|
String(process.env.DISABLE_AUTH ?? '').trim().toLowerCase(),
|
||||||
|
);
|
||||||
|
|
||||||
const oidcConfig = {
|
const oidcConfig = {
|
||||||
authRequired: false,
|
authRequired: false,
|
||||||
auth0Logout: false,
|
auth0Logout: false,
|
||||||
@@ -23,10 +27,10 @@ const oidcConfig = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!oidcConfig.clientID || !oidcConfig.issuerBaseURL || !oidcConfig.clientSecret) {
|
if (!disableAuth && (!oidcConfig.clientID || !oidcConfig.issuerBaseURL || !oidcConfig.clientSecret)) {
|
||||||
// Keep a clear startup failure if OIDC is not configured.
|
// Keep a clear startup failure if OIDC is not configured.
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'OIDC config is missing. Set OIDC_ISSUER_BASE_URL, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET in server/.env',
|
'OIDC config is missing. Set OIDC_ISSUER_BASE_URL, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET in server/.env (or set DISABLE_AUTH=true)',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,13 +42,27 @@ app.use(
|
|||||||
);
|
);
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
|
|
||||||
app.use(auth(oidcConfig));
|
if (!disableAuth) {
|
||||||
|
app.use(auth(oidcConfig));
|
||||||
|
}
|
||||||
|
|
||||||
app.get('/api/health', (_req, res) => {
|
app.get('/api/health', (_req, res) => {
|
||||||
res.json({ ok: true });
|
res.json({ ok: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get('/api/me', (req, res) => {
|
app.get('/api/me', (req, res) => {
|
||||||
|
if (disableAuth) {
|
||||||
|
res.json({
|
||||||
|
authenticated: false,
|
||||||
|
bypassAuth: true,
|
||||||
|
user: {
|
||||||
|
sub: 'noauth',
|
||||||
|
name: 'No Auth',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!req.oidc.isAuthenticated()) {
|
if (!req.oidc.isAuthenticated()) {
|
||||||
res.status(401).json({ authenticated: false });
|
res.status(401).json({ authenticated: false });
|
||||||
return;
|
return;
|
||||||
@@ -57,17 +75,29 @@ app.get('/api/me', (req, res) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
app.get('/api/login', (req, res) => {
|
app.get('/api/login', (req, res) => {
|
||||||
|
if (disableAuth) {
|
||||||
|
res.status(204).end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
res.oidc.login({
|
res.oidc.login({
|
||||||
returnTo: frontendOrigin,
|
returnTo: frontendOrigin,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get('/api/logout', (req, res) => {
|
app.get('/api/logout', (req, res) => {
|
||||||
|
if (disableAuth) {
|
||||||
|
res.status(204).end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
res.oidc.logout({
|
res.oidc.logout({
|
||||||
returnTo: frontendOrigin,
|
returnTo: frontendOrigin,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
app.listen(port, () => {
|
app.listen(port, () => {
|
||||||
console.log(`OIDC auth server listening on http://localhost:${port}`);
|
console.log(
|
||||||
|
`${disableAuth ? 'NO-AUTH' : 'OIDC auth'} server listening on http://localhost:${port}`,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user