61 lines
2.4 KiB
TypeScript
61 lines
2.4 KiB
TypeScript
/**
|
|
* Where the session token is kept, and how the HTTP client reaches it.
|
|
*
|
|
* A module of its own, holding nothing but the storage key and a reader,
|
|
* because the two files that need it cannot import each other: `session.ts`
|
|
* calls the API to sign in, and `client.ts` needs the token to make that same
|
|
* API call authorised. Anything shared between them has to sit underneath both.
|
|
*
|
|
* It imports nothing, on purpose — that is what keeps it free of the cycle.
|
|
*/
|
|
|
|
/**
|
|
* The single owner of this key.
|
|
*
|
|
* `session.ts` writes the blob and `client.ts` reads one field out of it, and a
|
|
* second copy of the string is how those two quietly stop agreeing after a
|
|
* rename.
|
|
*/
|
|
export const SESSION_STORAGE_KEY = 'nearle.session.v1';
|
|
|
|
/**
|
|
* The signed session on this tab, if there is one.
|
|
*
|
|
* Reads storage on every call rather than caching. Sign-in and sign-out both
|
|
* happen while the app is running, and a cached token would keep authorising
|
|
* requests for a user who has just left — or send nothing for one who has just
|
|
* arrived, until a reload.
|
|
*
|
|
* Returns undefined for every failure, including a throw. `sessionStorage`
|
|
* raises rather than returning null in a browser with site data blocked, and a
|
|
* console that cannot read a token should make an unauthenticated request and
|
|
* be refused by the server, not fail to render.
|
|
*/
|
|
export function readSessionToken(): string | undefined {
|
|
try {
|
|
const raw = sessionStorage.getItem(SESSION_STORAGE_KEY);
|
|
if (!raw) return undefined;
|
|
const parsed: unknown = JSON.parse(raw);
|
|
if (typeof parsed !== 'object' || parsed === null) return undefined;
|
|
const token = (parsed as { token?: unknown }).token;
|
|
return typeof token === 'string' && token !== '' ? token : undefined;
|
|
} catch {
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The `Authorization` header for a request, or nothing at all.
|
|
*
|
|
* Nothing, rather than an empty or `Bearer null` header, when there is no
|
|
* session. A header that is present but meaningless is worse than an absent
|
|
* one: `middleware.WebAuth` refuses a token that does not verify whatever the
|
|
* enforcement flag says, so sending rubbish would turn every anonymous call
|
|
* into a 401 — including the ones that are still meant to work while the
|
|
* rollout is in progress.
|
|
*/
|
|
export function authHeader(): Record<string, string> {
|
|
const token = readSessionToken();
|
|
return token ? { Authorization: `Bearer ${token}` } : {};
|
|
}
|