The shortest path from nothing to a signed-in user your own server can trust.
Two halves that never mix. Your users sign in with Google or Apple in their browser and come away with an access token. Your server holds an API key, and uses it to ask whether a token it was handed is still good.
GET /capabilities.Select a tenant, then issue a key from the API keys screen with a name that says what it is for. The secret is shown once, at the moment it is created, and never again. A key is pinned to the tenant it was issued for, so a service handling several tenants needs one per tenant.
Every endpoint is a POST that takes JSON and returns JSON. Ask for the URL to send the browser to, then exchange what comes back.
curl -X POST https://api.albos.org/auth.v1.OAuthService/GetAuthorizationURL \
-H "Content-Type: application/json" \
-d '{"provider":1,"redirectUri":"https://your.app/callback"}'
# {"authorizationUrl":"https://accounts.google.com/o/oauth2/v2/auth?...","state":"9f2c..."}
# provider: 1 = Google, 3 = AppleSend the browser there. Google or Apple sends it back to your redirect URI with a code and a state; check the state matches, then exchange the code.
curl -X POST https://api.albos.org/auth.v1.OAuthService/HandleCallback \
-H "Content-Type: application/json" \
-d '{"provider":1,"code":"...","state":"9f2c...","redirectUri":"https://your.app/callback"}'
{
"user": { "id": "...", "email": "[email protected]", "role": "" },
"tokens": { "accessToken": "eyJhbGci...", "refreshToken": "...", "expiresIn": "900" },
"isNewUser": false
}When your server is handed an access token, check it with your API key. This goes further than the signature and the expiry: it also answers whether the session has ended and whether the account is still usable.
curl -X POST https://api.albos.org/auth.v1.AuthService/VerifyToken \
-H "Content-Type: application/json" \
-H "X-API-Key: ca_..." \
-d '{"accessToken":"eyJhbGci..."}'
{
"active": true,
"user": { "id": "...", "email": "[email protected]", "role": "" },
"sessionId": "...",
"role": "",
"expiresAt": "1789000000"
}An access token lasts fifteen minutes. Renew it with the refresh token. From a browser the cookie is sent automatically, so the body can be empty; a mobile app sends the value it kept.
curl -X POST https://api.albos.org/auth.v1.AuthService/RefreshToken \
-H "Content-Type: application/json" \
-d '{"refreshToken":"..."}'Calls that reach personal data, and calls that cannot be undone, come back with 412 until the account has entered an authenticator code. The response carries an X-Step-Up-Reason header saying which of three situations it is; ask for a code, send it to StepUp, then retry the original call.
# the refusal
HTTP/1.1 412 Precondition Failed
X-Step-Up-Reason: step_up_required
# then
curl -X POST https://api.albos.org/auth.v1.UserService/StepUp \
-H "Content-Type: application/json" \
-d '{"code":"123456"}'To authenticate by cookie, your origin has to be on the allowlist: the cookie is SameSite=Lax and the Origin header is checked as well. Ask whoever operates the service to add it. You also need credentials: "include" for the cookie to be sent at all.
const res = await fetch("https://api.albos.org/auth.v1.AuthService/GetCurrentUser", {
method: "POST",
headers: { "Content-Type": "application/json" },
credentials: "include",
body: "{}",
});