Sign in with someones.computer
This platform is an OpenID Connect (OIDC) identity provider. Your site sends a person here to sign in, and gets back a signed token that says who they are. They never make an account with you, and you never hold a password.
This is the one page here that isn't about the /cli contract. It needs no
API token. An identity-provider client is a separate credential, and the flow
runs in the person's browser rather than on your server.
What you get
| Claim | Always | What you get |
|---|---|---|
sub |
Yes | The account's permanent identifier, 128 bits. Key your own records on this |
email |
Yes | The address the account signs in with |
email_verified |
Yes | Whether the account has opened a confirmation link here. Read it. It reports the account's real state, not a constant |
nonce |
When you send one | Exactly what you sent |
sub never changes, and this platform never reuses one. An address can
change. Match on sub.
An ordinary client learns nothing else. It doesn't learn whether the person administers this platform, or which organizations they belong to. Those claims exist, and they're for relying parties this platform runs itself. See the provider notes in the main repo.
Getting a client
There is no dynamic registration. Ask an operator to register you at
/admin/oidc-clients, and give them:
| A name | The consent screen shows it to the person |
| Your redirect URIs | One per line, http:// or https:// |
You get a client_id and a client_secret back. The page shows the secret
once and keeps only a hash of it. Nobody can read it back to you later, so a
lost secret means registering again.
Ask for the "Any approved account" kind. The other kind admits only administrators of this platform, which is almost certainly not what you want.
Redirect URIs match exactly
Both /oidc/authorize and /oidc/token compare every redirect URI byte for
byte. There is no prefix match and no wildcard. A trailing slash you didn't
register is a different URI.
Register every callback you use, including the local one you develop with.
The flow
This provider offers the authorization_code grant and no other. Start from
the discovery document rather than writing these paths down:
GET /.well-known/openid-configuration
- Send the person to
/oidc/authorizewithclient_id,redirect_uri,response_type=code,scope=openid email, and astateyou generate. Add anonceif your library uses one. - They sign in here and agree. They arrive back at your
redirect_uriwithcodeand yourstate. Check that thestatematches. - Exchange the code at
/oidc/tokenfrom your server, with yourclient_secret. The code is single-use and lives 60 seconds. - Verify the
id_tokenwith the key at/.well-known/jwks.json.
/oidc/userinfo answers the same identity claims, if your library prefers to
read them there: sub, plus email and email_verified when you asked for
the email scope. Call it once, straight after the exchange, with the
access_token as a bearer token.
scope must contain openid, and may contain email. This provider refuses
anything else with error=invalid_scope, so don't send profile.
The proof key for code exchange, S256, is always welcome, and mandatory if
the operator ticked that box on your registration. Send it. Your library
almost certainly does already.
What it won't do
| Not there | Instead |
|---|---|
| Refresh tokens | The access token lives 5 minutes, for one /oidc/userinfo call. Keep your own session after sign-in, the way you would with any other provider |
| Logout, of any kind | There is no end_session_endpoint and no back-channel logout. Signing out here doesn't sign the person out of your site |
Any grant but authorization_code |
Client credentials included. This provider authenticates people, not machines. Machines use an API token |
| Dynamic client registration | Ask an operator. /oauth/register exists, but it belongs to the Model Context Protocol (MCP) authorization server, which is a different thing |
When it refuses
| You see | It means |
|---|---|
404 from /oidc/authorize |
Unknown client_id, or a redirect_uri that isn't on your registration. Nothing redirects. The URI itself is what this check doubts, so sending a browser to it would be the attack the check stops |
error=invalid_scope |
You asked for a scope beyond openid email, or left out openid |
error=invalid_request |
A code_challenge_method other than S256, or none on a client that requires one |
error=access_denied |
The person pressed deny, or may not use this client. The two share one code on purpose: do the same thing for both, which is to stop and offer your own sign-in |
invalid_client from /oidc/token |
Wrong client_id or client_secret |
invalid_grant from /oidc/token |
The code is unknown, expired, or already spent. Or the redirect_uri doesn't match the one you authorized with |
This provider rate-limits repeated refusals per source, across
/oidc/authorize, /oidc/token and /oidc/userinfo together. A working
client never touches that budget. Only a rejection spends it.
One key, no rotation
The issuer signs with one key under one kid. Read it from
/.well-known/jwks.json rather than pinning it, and honour the kid in the
token header, so a future rotation doesn't break you.