OAuth & Sign in with Sporty

Sign in with Sporty

OpenID Connect — the id_token, its claims, /userinfo and verifying a signature.

If all you need is to know who someone is, you need OpenID Connect, which is the authorization code flow with openid in the scope.

Point any OIDC library at https://api.sporty.plus/.well-known/openid-configuration and it will configure itself. Writing the verification by hand is possible but rarely worth it.

The id_token

Returned alongside the access token when openid was granted. An RS256 JWT:

{
  "iss": "https://api.sporty.plus",
  "sub": "9f1c2e40-...",
  "aud": "your-client-id",
  "iat": 1789304059,
  "nbf": 1789304059,
  "exp": 1789307659,
  "auth_time": 1789304050,
  "nonce": "what-you-sent",
  "at_hash": "xQ7f...",
  "name": "Ada Lovelace",
  "given_name": "Ada",
  "family_name": "Lovelace",
  "locale": "hr",
  "email": "ada@example.com",
  "email_verified": true
}
ClaimNotes
subThe member's permanent id. This is the one to store. It never changes; an email address can.
audYour client id. Check it.
noncePresent if you sent one. Check it.
at_hashBinds this id_token to the access token issued with it.
auth_timeWhen the member approved. Not necessarily when they signed in — we do not record that, so do not build max_age behaviour on it.
name, given_name, family_name, localeOnly with profile.
email, email_verifiedOnly with email.

Verifying it

Fetch https://api.sporty.plus/oauth2/jwks, pick the key whose kid matches the token header, verify RS256, then check iss, aud and exp.

curl https://api.sporty.plus/oauth2/jwks

Cache the JWKS — an hour is sensible — but re-fetch when you meet a kid you do not know, rather than failing.

/userinfo

An id_token is a snapshot. For current values:

curl https://api.sporty.plus/oauth2/userinfo \
  -H "Authorization: Bearer $ACCESS_TOKEN"

The same claims, gated the same way by scope, always including sub.

Key rotation

We rotate the signing key rarely, and when we do:

  • the new public key appears in the JWKS, alongside the old one, before anything is signed with it;
  • every existing access token stops working. The signing key is shared with access tokens, so a rotation ends them. Your refresh tokens survive — use them, and be ready to re-authorise if that fails.

If you ever see a valid-looking token suddenly rejected, refresh once before assuming anything is broken.