mirror of
https://github.com/DorkSquadRR/DorkNet.git
synced 2026-10-26 07:28:45 -07:00
fix(auth): show only known sign-in error codes
Sign-in errors arrive in the query string, so unknown codes get a fixed message instead of being echoed into the banner.
This commit is contained in:
@@ -54,7 +54,7 @@ export function Login() {
|
|||||||
const errorText = (e: unknown, codes: Record<string, React.ReactNode>): React.ReactNode => {
|
const errorText = (e: unknown, codes: Record<string, React.ReactNode>): React.ReactNode => {
|
||||||
if (e instanceof ApiError && e.body && typeof e.body === 'object' && 'error' in e.body) {
|
if (e instanceof ApiError && e.body && typeof e.body === 'object' && 'error' in e.body) {
|
||||||
const code = (e.body as { error: string }).error;
|
const code = (e.body as { error: string }).error;
|
||||||
return codes[code] ?? e.message;
|
return Object.hasOwn(codes, code) ? codes[code] : e.message;
|
||||||
}
|
}
|
||||||
return e instanceof Error ? e.message : String(e);
|
return e instanceof Error ? e.message : String(e);
|
||||||
};
|
};
|
||||||
@@ -75,7 +75,8 @@ export function Login() {
|
|||||||
ssoHandled.current = true;
|
ssoHandled.current = true;
|
||||||
setErr(signinError === 'not_linked'
|
setErr(signinError === 'not_linked'
|
||||||
? <>This sign-in isn't linked to a DorkNet account yet. Link it on the <a className="underline" href={siteUrl('/api/auth/external/login?returnUrl=%2Faccount')}>website</a> first, then sign in here again.</>
|
? <>This sign-in isn't linked to a DorkNet account yet. Link it on the <a className="underline" href={siteUrl('/api/auth/external/login?returnUrl=%2Faccount')}>website</a> first, then sign in here again.</>
|
||||||
: SSO_ERRORS[signinError] ?? `Single sign-on failed (${signinError}).`);
|
// Only known codes are shown: the query string is attacker-controllable.
|
||||||
|
: Object.hasOwn(SSO_ERRORS, signinError) ? SSO_ERRORS[signinError] : 'Single sign-on failed. Try again.');
|
||||||
setParams({}, { replace: true });
|
setParams({}, { replace: true });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { NavLink, Link, useLocation, useNavigate, useSearchParams } from 'react-
|
|||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import { BrandMark } from './BrandMark';
|
import { BrandMark } from './BrandMark';
|
||||||
import { getDownloads, SignInRequiredError } from '../lib/downloads';
|
import { getDownloads, SignInRequiredError } from '../lib/downloads';
|
||||||
import { getMe, signInUrl, signOut, SIGNIN_ERRORS, type AccountMe } from '../lib/account';
|
import { errorCopy, getMe, signInUrl, signOut, SIGNIN_ERRORS, type AccountMe } from '../lib/account';
|
||||||
|
|
||||||
const NAV = [
|
const NAV = [
|
||||||
{ to: '/', label: 'Home', exact: true },
|
{ to: '/', label: 'Home', exact: true },
|
||||||
@@ -154,7 +154,7 @@ export function Header() {
|
|||||||
{signinError && (
|
{signinError && (
|
||||||
<div className="border-t border-danger/30 bg-danger/10">
|
<div className="border-t border-danger/30 bg-danger/10">
|
||||||
<div className="mx-auto max-w-6xl px-4 sm:px-6 py-2 flex items-center justify-between gap-3 text-sm text-danger">
|
<div className="mx-auto max-w-6xl px-4 sm:px-6 py-2 flex items-center justify-between gap-3 text-sm text-danger">
|
||||||
<span>Sign-in didn't work: {SIGNIN_ERRORS[signinError] ?? signinError}</span>
|
<span>Sign-in didn't work: {errorCopy(SIGNIN_ERRORS, signinError, 'something went wrong. Try again.')}</span>
|
||||||
<button type="button" className="btn-ghost !px-2 !py-1 text-xs" onClick={() => { params.delete('signin_error'); setParams(params, { replace: true }); }}>Dismiss</button>
|
<button type="button" className="btn-ghost !px-2 !py-1 text-xs" onClick={() => { params.delete('signin_error'); setParams(params, { replace: true }); }}>Dismiss</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -46,6 +46,12 @@ export const LINK_ERRORS: Record<string, string> = {
|
|||||||
no_pending_identity: 'Your sign-in expired. Start again with "Sign in".',
|
no_pending_identity: 'Your sign-in expired. Start again with "Sign in".',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** Friendly copy for a known error code, else `fallback`. For codes read from
|
||||||
|
* the query string, pass a fixed fallback: anyone can put text in a link. */
|
||||||
|
export function errorCopy(map: Record<string, string>, code: string | null | undefined, fallback: string): string {
|
||||||
|
return code && Object.hasOwn(map, code) ? map[code] : fallback;
|
||||||
|
}
|
||||||
|
|
||||||
export const SIGNIN_ERRORS: Record<string, string> = {
|
export const SIGNIN_ERRORS: Record<string, string> = {
|
||||||
account_unavailable: 'The account linked to this sign-in is unavailable (deleted or banned).',
|
account_unavailable: 'The account linked to this sign-in is unavailable (deleted or banned).',
|
||||||
no_identity: "The sign-in provider didn't return an identity. Try again.",
|
no_identity: "The sign-in provider didn't return an identity. Try again.",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { Link as RouterLink, useNavigate, useSearchParams } from 'react-router';
|
import { Link as RouterLink, useNavigate, useSearchParams } from 'react-router';
|
||||||
import { getMe, link, LINK_ERRORS, localPath, signInUrl, type AccountMe } from '../lib/account';
|
import { errorCopy, getMe, link, LINK_ERRORS, localPath, signInUrl, type AccountMe } from '../lib/account';
|
||||||
|
|
||||||
// After a first sign-in with the provider: prove which DorkNet account this
|
// After a first sign-in with the provider: prove which DorkNet account this
|
||||||
// identity belongs to, once, with that account's password.
|
// identity belongs to, once, with that account's password.
|
||||||
@@ -35,7 +35,7 @@ export function Link() {
|
|||||||
window.location.assign(returnUrl); // the header re-reads who is signed in
|
window.location.assign(returnUrl); // the header re-reads who is signed in
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const key = (e as Error).message;
|
const key = (e as Error).message;
|
||||||
setErr(LINK_ERRORS[key] ?? key);
|
setErr(errorCopy(LINK_ERRORS, key, key));
|
||||||
} finally {
|
} finally {
|
||||||
setBusy(false);
|
setBusy(false);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user