Auth / foldkit-auth
Who the client believes it is
The cookie is the browser's, shared by every tab: a sign-in in one tab changes who another tab's next read is answered as. A broadcast between tabs tells the others, but a read already on its way would land the new account's answers in a cache that shows the old one. So the server says whom it answered as, and the client repeats it:
ts
import { believing } from 'foldkit-auth'
const resolving = { auth: AppAuth, http, principal, key: Policy.key }
serveFetch({
server,
resolvePrincipal: AuthServer.resolver(resolving),
headers: AuthServer.answered(resolving), // `x-foldkit-subject` on each answer
layer: () => AppAuthLive,
})
// The browser: Remote's transport through a fetch that repeats it.
export const belief = believing()
Remote.httpWithLive('/remote', {
headers: () => ({ 'x-effect-auth-csrf': '1' }),
fetch: belief.fetch,
})AuthServer.answeredtakes the resolver's own config, and needs itskey, so the header names the principal as the check reads it:subjectSaid(Option.some(key(user))), orvisitor. The key is URI-encoded, as a header carries Latin-1 only.believing()holds what the last answer said, beside the cookie it describes, never in the Model. Once the cookie means someone else, the next request is refused withSUBJECT_CHANGEDinstead of answered.- Any 401 makes it believe no one, so the reads asked after it learn who is
signed in now. The application's
updateanswers a read refused withSUBJECT_CHANGEDwithRemote.forget, which asks them; a refusal for any other reason (Your session ended.) is shown, since asking again would be refused again. - Believing no one is a gap: after a 401 or
forget(), the next request is answered as whoever the cookie names. An application that keeps what Remote holds through a 401 (a session that ended, its old rows still shown) should forget them before another tab could sign in, or the next read merges the new account's answers into them. An answer to a request sent before the last 401 orforget()is never believed. - A sign-in or sign-out this tab makes calls
belief.forget()in its Command, beforeupdateforgets what Remote holds, so the reads after it are not refused once first.
examples/auth wires all of it.