Documentation
Sign in with Presence+
One account across every application on the Network. Your users sign in with Presence+; your application receives who they are and which organizations they belong to, as claims. The issuer is https://api.presence.plus.
Endpoints
- Discovery:
/.well-known/openid-configuration - Authorization:
/api/oauth/authorize - Token:
/api/oauth/token(client_secret_basic,client_secret_post, ornonewith PKCE for public clients) - Userinfo:
/api/oauth/userinfo - Keys:
/.well-known/jwks.json
Scopes
openid profile email offline_access are standard. The Network scopes are memberships:read, community:member, courses:read, pathways:walk, home:read, devotions:pray and clinical:client; request only what the application uses, and the consent screen shows the person exactly that.
Claims
sub, name, email, email_verified, picture, and memberships: the organizations the person belongs to and their role in each. Roles live on the relationship, not on the person, so the same account can be a leader in one community and a member in another.
A confidential client in a dozen lines
// Discovery
const ISSUER = "https://api.presence.plus";
const d = await fetch(`${ISSUER}/.well-known/openid-configuration`).then((r) => r.json());
// Authorization request (code + PKCE)
const url = new URL(d.authorization_endpoint);
url.searchParams.set("client_id", process.env.PRESENCE_CLIENT_ID!);
url.searchParams.set("redirect_uri", "https://yourapp.example/auth/callback");
url.searchParams.set("response_type", "code");
url.searchParams.set("scope", "openid profile email memberships:read");
url.searchParams.set("code_challenge", challenge);
url.searchParams.set("code_challenge_method", "S256");
url.searchParams.set("state", state);
// Token exchange (client_secret_post) and userinfo
const token = await fetch(d.token_endpoint, {
method: "POST",
headers: { "content-type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({ grant_type: "authorization_code", code, redirect_uri, code_verifier: verifier,
client_id: process.env.PRESENCE_CLIENT_ID!, client_secret: process.env.PRESENCE_CLIENT_SECRET! }),
}).then((r) => r.json());
const me = await fetch(d.userinfo_endpoint, { headers: { Authorization: `Bearer ${token.access_token}` } }).then((r) => r.json());
// { sub, name, email, email_verified, picture, memberships: [{ org, role, ... }] } Registering a client
Your sandbox comes with a client. For production, register the application's redirect URI from the developer console in the shell; you receive a client_id and client_secret. Public clients (native and single-page apps) use PKCE without a secret.
Placeholder: the developer console is being built; until it ships, production clients are registered by the Presence+ team on request.