Sign-in and tokens
sblurp.chat is an OAuth 2.0 authorization server. It signs people in and gives the app a token for each community server. Community servers check those tokens with sblurp.chat's public keys. Bots use their own tokens instead.
The flow
-
The app sends the person to sign in once, with Authorization Code and PKCE:
GET https://sblurp.chat/oauth/authorize?response_type=code&client_id=sblurp-web &redirect_uri=...&code_challenge=...&code_challenge_method=S256&state=... -
sblurp.chat redirects back with
code,stateandiss. The app trades the code:POST https://sblurp.chat/oauth/token grant_type=authorization_code&client_id=sblurp-web&code=...&redirect_uri=...&code_verifier=...The reply has a
refresh_tokenand anaccess_tokenfor sblurp.chat itself. -
For each community server, the app asks for a token with that server as the audience:
POST https://sblurp.chat/oauth/token grant_type=refresh_token&client_id=sblurp-web&refresh_token=...&audience=https://chat.example.comThe reply has a new refresh token and an access token for that server only.
-
The app sends the access token to the server as
Authorization: Bearer <token>.
The only client is the web app, sblurp-web, with fixed return addresses. PKCE with S256 is required. A code works once, within 60 seconds.
Token reply
{"access_token": "eyJ...", "token_type": "Bearer", "expires_in": 900, "refresh_token": "..."}
Access tokens
An access token is a JWT signed with EdDSA (Ed25519). It lasts 15 minutes. Its claims:
| Claim | What |
|---|---|
iss |
The issuer, https://sblurp.chat. |
sub |
The account id. Stable. |
aud |
The server's base URL, or the issuer for sblurp.chat's own API. |
exp, iat |
Expiry and issue time, in Unix seconds. |
jti |
A unique id. |
preferred_username |
The username. |
email_verified |
Whether the email address is confirmed. |
age_over_13, age_over_18 |
What the person declared at sign-up. |
The token carries no profile. The app sends the profile in Join, after the person agrees.
Refresh tokens
- Opaque, and good for 60 days.
- Rotated on every use: each refresh returns a new one.
- Sending the same token again within 10 seconds is treated as a retry and gets the same new token.
- A used token that comes back later means someone else has a copy. sblurp.chat then revokes every token in that chain.
Revoke a refresh token, and its whole chain, with RFC 7009:
POST https://sblurp.chat/oauth/revoke
token=...
It always answers 200.
How a server checks a token
- The
issmust be in the server's trusted issuers. It checks this before fetching any keys. - It fetches
{iss}/.well-known/jwks.jsonand caches the keys for an hour. An unknownkidmakes it fetch again, at most every 30 seconds. - It checks the signature (only
alg: EdDSA), thenaudagainst its own base URL, thenexp,iatandnbf, with 60 seconds of clock skew.
A token for one community is useless at another. A server knows a person by (iss, sub), never by sub alone.
sblurp.chat endpoints
| Endpoint | What |
|---|---|
GET /oauth/authorize |
Starts sign-in. |
POST /oauth/token |
authorization_code and refresh_token grants. |
POST /oauth/revoke |
Revokes a refresh token chain. |
GET /api/me |
The account behind an access token for sblurp.chat: id, username, email_verified, display_name, avatar_url, bio, pronouns. |
GET /.well-known/jwks.json |
Public signing keys. |
GET /.well-known/openid-configuration |
Discovery document. |
Errors are JSON, such as {"error": "invalid_grant", "error_description": "code expired"}. The codes are invalid_client, invalid_grant, invalid_target (a bad audience), unsupported_grant_type and invalid_token.
Bot tokens
A bot token starts with sbb_. An admin makes it with BotService.CreateBot, and it is shown once. The server stores only its SHA-256 hash. It works only on the server that made it. See Bot tokens.
Tools and agents can read every call and type from schema.json.
View this page as Markdown · Glossary · API schema.json · openapi.json