Other HTTP clients
Attach the access token with axios, ky, or any other HTTP client, using the client's credentials calls.
createAuthFetch and the
effetch plugin are built on three
calls, and any other HTTP client can use the same ones:
- Before a request,
auth.credentials.get()resolves the credentials to send, waiting for a restore or a refresh that is under way. It resolvesnullwhen nobody is signed in. - After a 401,
auth.credentials.renew(credentials)takes the credentials that request was sent with, and resolves new ones. However many requests failed, they share one refresh. It resolvesnullwhen the user was signed out instead. - After a second 401,
auth.credentials.reject(credentials)ends the session, unless a newer one has replaced it.
With axios, that's two interceptors:
import type { } from "@monarcode/session-kit";
import , { type } from "axios";
import { } from "./auth.js";
import { } from "./backend.js";
type = & { ?: boolean };
// One instance for one API, so the token goes nowhere else.
export const = .({ : });
/** The credentials each request was sent with. */
const = new <, >();
...(async () => {
const = await ..();
// Signed out, the request goes without a token, as a public one would.
if () {
..("Authorization", `Bearer ${.}`);
.(, );
}
return ;
});
...(, async (: unknown) => {
if (!.() || .?. !== 401) throw ;
const : | undefined = .;
const = && .();
if (! || !) throw ;
if (.) {
// The new token was refused too: end the session.
await ..();
throw ;
}
const = await ..();
// As with authFetch, only a GET or HEAD is sent twice. For anything else
// the caller decides.
if (! || !["get", "head"].(. ?? "")) throw ;
const : = { ..., : true };
return .();
});With ky, two hooks do the same. The
request's context carries the credentials from one hook to the other:
import type { } from "@monarcode/session-kit";
import from "ky";
import { } from "./auth.js";
import { } from "./backend.js";
export const = .({
: `${}/`,
: {
: [
async ({ , }) => {
const = await ..();
if (!) return;
..credentials = ;
..(
"Authorization",
`Bearer ${.}`,
);
},
],
: [
async ({ , , }) => {
const = ..credentials as | undefined;
if (. !== 401 || !) return;
if (..replayed) {
// The new token was refused too: end the session.
await ..();
return;
}
const = await ..();
// As with authFetch, only a GET or HEAD is sent twice.
if (! || !["GET", "HEAD"].(.)) return;
..credentials = ;
..replayed = true;
const = new (.);
.("Authorization", `Bearer ${.}`);
return .({ : new (, { }) });
},
],
},
});Three things createAuthFetch does for you are yours to keep here:
- Keep the token on one origin. Give the client one base URL, and don't pass it absolute URLs to other hosts.
- Don't read the token early. Ask for credentials in the request hook, never once at startup: a token saved in a variable goes stale at the next refresh.
- Check the account after slow work.
auth.isCurrent(credentials)tells whether the session that sent a request is still the signed-in one, before you show its answer.