/** * Signing in and out. The token itself never reaches this code: the server keeps the session and * hands the browser a cookie, so all that travels here is the name behind it. */ /** `admin` may manage the accounts and open every project (#180, #179); anything else is a `user`. */ export type Role = 'admin' | 'user'; /** Who is signed in, and what they may do – the server decides, this only says what to offer. */ export interface SignedIn { user: string; role: Role; } export interface AuthStore { /** The signed-in user, or null while nobody is. */ session(): Promise; /** Who signed in on success; a rejected promise when the pair is wrong. */ logIn(username: string, password: string): Promise; logOut(): Promise; /** * Replaces the password of the signed-in account. The current one has to come along even though * there is a session – and the server ends the account's other sessions, not this one. * Rejects with `WrongCredentialsError` when the current password does not match. */ changePassword(currentPassword: string, newPassword: string): Promise; } /** Anything the server did not call `admin` is a `user`: the weaker reading is the safe one. */ export function roleOf(value: unknown): Role { return value === 'admin' ? 'admin' : 'user'; } /** Thrown for a wrong user name or password, as opposed to a server that is not there. */ export class WrongCredentialsError extends Error { constructor(message = 'Wrong user name or password') { super(message); this.name = 'WrongCredentialsError'; } }