@sovereignfs/sdk
v2.1.0
Published
How a JavaScript app's server talks to the Sovereign server it signs people in with: tokens, failure, version support.
Readme
@sovereignfs/sdk
How a JavaScript app's server talks to the Sovereign server it signs people in with. It wraps a client generated from core's
API contract (bundled in, so there is nothing else to install) with what is tedious to do by hand: tokens, failure, and version
support. What it promises is in docs/specs/sdk.md; the
contract it never goes beyond is
contracts/core-api/openapi.yaml.
npm install @sovereignfs/sdkIt runs on Node 22.3 or later, and on any server runtime that has fetch and Web Crypto.
import { createSovereign } from '@sovereignfs/sdk';
// Reads NOTES_URL, NOTES_CORE_URL, NOTES_CORE_CLIENT_ID and NOTES_CORE_CLIENT_SECRET (or its _FILE form).
// Makes no network call, so the app starts while core is down.
export const sv = createSovereign({ prefix: 'NOTES', tokenStore });
const { users } = await sv.as(person).directory.searchUsers({ q: 'ada' }); // as a person
const branding = await sv.instance.getBranding(); // public, so no actor- Actors. Every call names who it is made as:
sv.as(person),sv.app, orsv.instancefor public calls. A method exists only on an actor that can use it, and the SDK never falls back from one to another. - Tokens. An app never handles an access token. Access tokens are cached in memory until 30 seconds before they expire. A
person's refresh token is kept in a token store the app gives (
read, and a conditionalwrite); core rotates it on every use and takes one used twice for theft, so refreshes run one at a time for each person, and an app that runs more than one process gives its store alock. - Failure. Every call can fail, as one of
CoreApiError,CoreUnavailableError,SessionEndedError,NotSupportedErrororConfigurationError. Requests time out (5 seconds), safe ones are retried, and nothing logs a token or a person's details. - Version support. The SDK reads the instance document and raises
NotSupportedErrorfor a capability this core lacks, without sending the call. - Signing in.
sv.authsigns people in through core withopenid-client(PKCES256,state,nonce), keeps the sessions in a session store the app gives, and ends them on sign-out and on core's back-channel logout notice, which it verifies (signature, issuer, audience, age, event,jti, and nononce). The session cookie is host-only (__Host-,Secure,HttpOnly) over https.getSessionmakes no call to core. - Notices without the SDK's sessions. An app whose sessions are its own (it has other ways in, or uses another auth library) verifies
core's logout and account-deleted notices with
verifyLogoutTokenandverifyAccountDeletedToken, which apply every check and end and delete nothing. They need aseenthat records eachjti; see Using the SDK. - Server only. Importing it in a browser throws.
In Next.js
// app/auth/[action]/route.ts: /auth/login, /auth/callback, /auth/logout (POST) and /auth/backchannel-logout (POST)
import { nextAuth } from '@sovereignfs/sdk/next';
export const { GET, POST } = nextAuth(sv).handlers;
// A Server Component:
const session = await nextAuth(sv).requireSession('/notes'); // sends a person to sign in, and backRegister https://notes.sovereign.local/auth/callback as the app's sign-in address, https://notes.sovereign.local/ as its address after
sign-out, and https://notes.sovereign.local/auth/backchannel-logout as its back-channel logout address. next is an optional peer
dependency, and nothing outside /next needs it.
Testing an app
@sovereignfs/sdk/testing is a core that needs no server:
import { createFakeCore } from '@sovereignfs/sdk/testing';
const core = createFakeCore();
core.addPerson({ sub: 'u1', name: 'Ada Lovelace' });
const sv = createSovereign({
appUrl: 'https://notes.sovereign.local',
coreUrl: core.url,
...core.credentials,
tokenStore,
fetch: core.fetch,
});
await tokenStore.write('u1', core.signIn('u1'), undefined);
core.fail({ kind: 'down' }); // or 'timeout', 'lost-answer', or { kind: 'status', status: 503 }
// Signing in: the person agrees at core, and core can send a logout notice, wrong in any way you want to test.
const back = core.authorize((await sv.auth.beginSignIn()).url, 'u1');
const notice = await core.logoutToken({ sub: 'u1' }, { wrong: 'expired' });memorySessionStore() is a session store for tests, and memorySeen() a seen for verifyLogoutToken and verifyAccountDeletedToken.
Core versions
This release supports core >=0.46.0 (sovereign.core in package.json). A core too old to say what it serves is treated as
serving the Directory and the instance document, and nothing else. The guide for an app's developer is
Using the SDK.
Which exports are stable
createSovereign and the error types are stable and change only in a major version; everything else is experimental and may
change in a minor version
(src/stability.ts lists both).
Versions
This SDK starts at 2.0.0. Versions up to 1.25.0 of @sovereignfs/sdk are the legacy SDK, which ran inside the old platform and is
not an earlier release of this one.
Licence
MPL-2.0, so an app under any licence can use it.
