Access for developers

One account for everything sz.ws publishes. Products on *.sz.ws share the session outright; everyone else asks through standard OpenID Connect. This page is the whole public contract.

Products on *.sz.ws

The session lives in a cookie scoped to .sz.ws (__Secure-szws-session — HttpOnly, Secure, SameSite=Lax). A visitor signed in at sz.ws is already signed in on your subdomain; there is no ceremony to run and no redirect to make.

Reading identity. Call GET /api/access/get-session on sz.ws with credentials. Trusted *.sz.ws origins get CORS with credentials, so this works from the browser as well as from your server (forward the cookie). An anonymous visitor gets an empty session, not an error.

Offering sign-in. Copy lib/access/popup.ts from the sz.ws repo into your app unchanged and call openAccessPopup(). It opens https://sz.ws/access?popup=1&from=<origin>, and resolves when Access posts { type: "szws-access", status: "ok" } back to the opener. If the popup is blocked, fall back to a full-page visit to accessPopupUrl() — it carries return= and sends the visitor straight back. Re-check the session when it resolves; the cookie is already yours.

Passkey and OTP ceremonies only ever run on sz.ws itself. Never call the sign-in endpoints from another origin — they will refuse. Successful session reads are recorded per origin and shown to the account owner as connected services. drop.sz.ws is deliberately excluded from all of this trust.

Public profiles. Every handle has a page at https://sz.ws/@<handle> showing only the handle, picture, display name, bio and verification mark. Link to it rather than building a profile view of your own; the picture itself is at /api/access/avatar/<handle>?v=<version>, where the version comes from the session's profile.

Everyone else — OpenID Connect

sz.ws is an OIDC provider. Configuration comes from discovery — treat it as the authority over anything written here:

  • Discovery/api/access/.well-known/openid-configuration
  • Authorization/api/access/oauth2/authorize
  • Token/api/access/oauth2/token
  • Userinfo/api/access/oauth2/userinfo
  • JWKS/api/access/jwks
  • Session (first party)/api/access/get-session

Authorization code with PKCE (S256) is the only flow. Scopes: openid profile email (add offline_access for a refresh token). ID tokens are signed EdDSA — verify against the JWKS, check iss against discovery and aud against your client id. The person sees a consent screen naming your app and what it receives; declining returns access_denied to your redirect URI.

Getting a client. Registration is manual — there is no self-serve signup. Ask the studio (X: @szdotws) with your app name and redirect URIs; you get a client_id and a client_secret that is shown exactly once. Redirect URIs are matched exactly and must be HTTPS.

Sessions and good behavior

Sessions are database-backed, last 30 days, and renew after 15 — revocation on the account page takes effect immediately everywhere.get-sessionis rate limited per IP: read it once per page load and cache the answer, don't poll. Every write endpoint carries its own stricter limit; a 429 means slow down, not retry.