@volter/identity
v0.4.2
Published
Volter identity for products: whether an issuer is available, who a token names (ADR-0001), and this machine's one shared sign-in (`volter login`).
Readme
@volter/identity
What a product needs from Volter's identity service (https://id.volter.ai), and nothing it can do without.
import { resolveIdentity, verifyIdentityToken } from "@volter/identity";
// Whether this deployment has an identity service at all (a CDN-only self-host has none).
const identity = await resolveIdentity(process.env.VOLTER_ISSUER);
// Who a token names, verified against the issuer's published keys, with no call per request.
const person = await verifyIdentityToken(token, { issuer: "https://id.volter.ai", audience: "https://your-product.example" });
person.subject; // the permanent Volter id
person.email; // when the email scope was granted
person.githubId; // the linked GitHub account's numeric id, when one is linkedsubject is permanent; everything else may change. githubLogin is the login recorded when the person first signed in with GitHub: a GitHub login can be renamed and re-registered, and an identity that linked GitHub later carries only githubId. Key a GitHub account by githubId, and read its current login from GitHub (GET /user/{id}) wherever the login decides access. Tokens are EdDSA JWTs; access tokens for a registered resource carry it as their audience.
This machine's one sign-in
signIn signs the machine in once, for every Volter tool (volter login runs it); a tool then asks tokenFor(itsOrigin) for a token of its own. In the browser, the sign-in returns to a page served by the machine itself, which stays open until the tool is done: finish may ask the person one question there and says what the page ends on.
await signIn({
prompt: ({ url, opened }) => console.error(opened ? `Finish in your browser: ${url}` : `Open ${url}`),
finish: async ({ who, ask }) => {
// `ask` is absent when there is no tab to ask in (a device code): ask at the terminal instead.
const chosen = ask ? await ask({ title: "Choose your teams", options: [{ id: "t1", label: "Volter", checked: true }], submit: "Continue" }) : ["t1"];
return { ok: true, title: "You're signed in", items: chosen.map((id) => ({ label: id })) };
},
});