Plain HTTP

The same flow as raw requests, for a language with no library available.

Do this when there is no ready library for your language. If there is one, use it: verifying a token signature by hand is the last thing worth writing yourself.

1. Send the user

https://auth.my/auth
  ?client_id=YOUR_CLIENT_ID
  &response_type=code
  &redirect_uri=https%3A%2F%2Fexample.com%2Fcallback
  &scope=openid+profile+email
  &state=RANDOM_STRING
  &nonce=RANDOM_STRING
  &code_challenge=BASE64URL(SHA256(verifier))
  &code_challenge_method=S256

verifier is a random string of 43–128 characters. Keep it, along with state and nonce, until the user comes back.

2. Handle the return

The user returns to redirect_uri?code=…&state=….

Compare state with what you sent. If it differs, stop — this is not your flow.

3. Exchange the code

curl -u "CLIENT_ID:CLIENT_SECRET" https://auth.my/token \
  -d grant_type=authorization_code \
  -d code=RECEIVED_CODE \
  -d redirect_uri=https://example.com/callback \
  -d code_verifier=ORIGINAL_VERIFIER
{
  "access_token": "…",
  "id_token": "…",
  "refresh_token": "…",
  "token_type": "Bearer",
  "expires_in": 3600
}

Public clients have no secret — instead of -u, send client_id in the request body.

4. Verify the id_token

This is not a formality. An unverified id_token is a claim about someone's identity submitted by anyone at all.

Check every one of these:

WhatHow
signaturewith the key from https://auth.my/jwks matching kid in the header
issexactly https://auth.my
audyour client_id
nonceequals the one you sent
expnot expired

Use a vetted crypto library for your language. A hand-written signature check is a source of vulnerabilities, not of savings.

Important

In particular: never trust the alg header blindly and never accept alg: none. You decide the expected algorithm, not the token.

5. Afterwards

curl -H "Authorization: Bearer ACCESS_TOKEN" https://auth.my/me

Refresh, sign-out and revocation are covered in Tokens and Sign-out.

Did this page help?