mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-23 23:31:47 +00:00
Merge branch 'pr-117' into dev
This commit is contained in:
@@ -496,7 +496,7 @@ export default function LoginPage(): React.ReactElement {
|
|||||||
{error}
|
{error}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<a href="/api/auth/oidc/login"
|
<a href={`/api/auth/oidc/login${inviteToken ? '?invite=' + encodeURIComponent(inviteToken) : ''}`}
|
||||||
style={{
|
style={{
|
||||||
width: '100%', padding: '12px',
|
width: '100%', padding: '12px',
|
||||||
background: '#111827', color: 'white',
|
background: '#111827', color: 'white',
|
||||||
@@ -657,7 +657,7 @@ export default function LoginPage(): React.ReactElement {
|
|||||||
<span style={{ fontSize: 12, color: '#9ca3af' }}>{t('common.or')}</span>
|
<span style={{ fontSize: 12, color: '#9ca3af' }}>{t('common.or')}</span>
|
||||||
<div style={{ flex: 1, height: 1, background: '#e5e7eb' }} />
|
<div style={{ flex: 1, height: 1, background: '#e5e7eb' }} />
|
||||||
</div>
|
</div>
|
||||||
<a href="/api/auth/oidc/login"
|
<a href={`/api/auth/oidc/login${inviteToken ? '?invite=' + encodeURIComponent(inviteToken) : ''}`}
|
||||||
style={{
|
style={{
|
||||||
marginTop: 12, width: '100%', padding: '12px',
|
marginTop: 12, width: '100%', padding: '12px',
|
||||||
background: 'white', color: '#374151',
|
background: 'white', color: '#374151',
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ setInterval(() => {
|
|||||||
}
|
}
|
||||||
}, AUTH_CODE_CLEANUP);
|
}, AUTH_CODE_CLEANUP);
|
||||||
|
|
||||||
const pendingStates = new Map<string, { createdAt: number; redirectUri: string }>();
|
const pendingStates = new Map<string, { createdAt: number; redirectUri: string; inviteToken?: string }>();
|
||||||
|
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
@@ -124,8 +124,9 @@ router.get('/login', async (req: Request, res: Response) => {
|
|||||||
const proto = (req.headers['x-forwarded-proto'] as string) || req.protocol;
|
const proto = (req.headers['x-forwarded-proto'] as string) || req.protocol;
|
||||||
const host = (req.headers['x-forwarded-host'] as string) || req.headers.host;
|
const host = (req.headers['x-forwarded-host'] as string) || req.headers.host;
|
||||||
const redirectUri = `${proto}://${host}/api/auth/oidc/callback`;
|
const redirectUri = `${proto}://${host}/api/auth/oidc/callback`;
|
||||||
|
const inviteToken = req.query.invite as string | undefined;
|
||||||
|
|
||||||
pendingStates.set(state, { createdAt: Date.now(), redirectUri });
|
pendingStates.set(state, { createdAt: Date.now(), redirectUri, inviteToken });
|
||||||
|
|
||||||
const params = new URLSearchParams({
|
const params = new URLSearchParams({
|
||||||
response_type: 'code',
|
response_type: 'code',
|
||||||
@@ -222,7 +223,16 @@ router.get('/callback', async (req: Request, res: Response) => {
|
|||||||
const userCount = (db.prepare('SELECT COUNT(*) as count FROM users').get() as { count: number }).count;
|
const userCount = (db.prepare('SELECT COUNT(*) as count FROM users').get() as { count: number }).count;
|
||||||
const isFirstUser = userCount === 0;
|
const isFirstUser = userCount === 0;
|
||||||
|
|
||||||
if (!isFirstUser) {
|
let validInvite: any = null;
|
||||||
|
if (pending.inviteToken) {
|
||||||
|
validInvite = db.prepare('SELECT * FROM invite_tokens WHERE token = ?').get(pending.inviteToken);
|
||||||
|
if (validInvite) {
|
||||||
|
if (validInvite.max_uses > 0 && validInvite.used_count >= validInvite.max_uses) validInvite = null;
|
||||||
|
if (validInvite?.expires_at && new Date(validInvite.expires_at) < new Date()) validInvite = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isFirstUser && !validInvite) {
|
||||||
const setting = db.prepare("SELECT value FROM app_settings WHERE key = 'allow_registration'").get() as { value: string } | undefined;
|
const setting = db.prepare("SELECT value FROM app_settings WHERE key = 'allow_registration'").get() as { value: string } | undefined;
|
||||||
if (setting?.value === 'false') {
|
if (setting?.value === 'false') {
|
||||||
return res.redirect(frontendUrl('/login?oidc_error=registration_disabled'));
|
return res.redirect(frontendUrl('/login?oidc_error=registration_disabled'));
|
||||||
@@ -242,6 +252,15 @@ router.get('/callback', async (req: Request, res: Response) => {
|
|||||||
'INSERT INTO users (username, email, password_hash, role, oidc_sub, oidc_issuer) VALUES (?, ?, ?, ?, ?, ?)'
|
'INSERT INTO users (username, email, password_hash, role, oidc_sub, oidc_issuer) VALUES (?, ?, ?, ?, ?, ?)'
|
||||||
).run(username, email, hash, role, sub, config.issuer);
|
).run(username, email, hash, role, sub, config.issuer);
|
||||||
|
|
||||||
|
if (validInvite) {
|
||||||
|
const updated = db.prepare(
|
||||||
|
'UPDATE invite_tokens SET used_count = used_count + 1 WHERE id = ? AND (max_uses = 0 OR used_count < max_uses)'
|
||||||
|
).run(validInvite.id);
|
||||||
|
if (updated.changes === 0) {
|
||||||
|
console.warn(`[OIDC] Invite token ${pending.inviteToken?.slice(0, 8)}... exceeded max_uses (race condition)`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
user = { id: Number(result.lastInsertRowid), username, email, role } as User;
|
user = { id: Number(result.lastInsertRowid), username, email, role } as User;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user