GOCOSYS Account Docs

Authorization flow

Authorization code with PKCE, from redirect to tokens.

The round trip

  1. Generate a PKCE verifier and challenge; keep the verifier server-side.
  2. Redirect the user to the authorization endpoint.
  3. They sign in and approve on the consent screen.
  4. They come back to your redirect URI with a code.
  5. You exchange the code — plus the verifier — for tokens.

Step 0 — PKCE

PKCE is required. Create a random code_verifier, hash it with SHA-256, and base64url-encode the digest to get the code_challenge.

# bash
verifier=$(openssl rand -base64 60 | tr -d '\n=+/' | cut -c1-64)
challenge=$(printf '%s' "$verifier" \
  | openssl dgst -binary -sha256 \
  | openssl base64 | tr '+/' '-_' | tr -d '=')

Store the verifier in the user's server-side session, keyed alongside the state. It must never travel in the URL or sit in browser storage — that would defeat the point.

Step 1 — Send the user to authorize

GET https://account.gocosys.com/oauth-authorize.php
  ?response_type=code
  &client_id=YOUR_CLIENT_ID
  &redirect_uri=https%3A%2F%2Fyourapp.example%2Fcallback
  &scope=openid%20profile%20email
  &state=RANDOM_OPAQUE_VALUE
  &nonce=RANDOM_VALUE
  &code_challenge=THE_CHALLENGE
  &code_challenge_method=S256
ParameterRequiredNotes
response_typeyesAlways code.
client_idyesFrom registration.
redirect_uriyesMust match a registered URI exactly.
scoperecommendedSpace-separated. Defaults to the client's registered scopes if omitted.
stateyes, in practiceOpaque value echoed back. Your CSRF defence.
noncerecommendedEchoed into the ID token, binding it to this request.
code_challengeyesbase64url(SHA256(verifier)).
code_challenge_methodyesMust be S256.

Step 2 — The user approves

The GOCOSYS consent screen.
What the user sees. Your application name and logo appear here.

If they cancel, they return to your redirect URI with an error instead of a code:

https://yourapp.example/callback?error=access_denied&state=…

Step 3 — Handle the callback

https://yourapp.example/callback?code=THE_CODE&state=YOUR_STATE

Compare state against what you stored, and reject the request if it differs. Without this, an attacker can feed you a code from their own session and have your application link their identity to your user.

Step 4 — Exchange the code

Within 60 seconds. Codes are single use — a replay is rejected. The endpoint accepts form-encoded (below) or JSON.

curl -X POST https://account.gocosys.com/oauth/token \
  -d grant_type=authorization_code \
  -d code=THE_CODE \
  -d redirect_uri=https://yourapp.example/callback \
  -d client_id=YOUR_CLIENT_ID \
  -d client_secret=YOUR_CLIENT_SECRET \
  -d code_verifier=THE_ORIGINAL_VERIFIER
{
  "access_token": "…",
  "token_type": "Bearer",
  "expires_in": 900,
  "refresh_token": "…",
  "id_token": "…",
  "scope": "openid profile email"
}

The redirect_uri must be byte-identical to step 1, and the code_verifier must match the challenge. Either mismatch fails with OAUTH_INVALID_GRANT.

Step 5 — Identify the user

curl https://account.gocosys.com/oauth/userinfo \
  -H "Authorization: Bearer ACCESS_TOKEN"

Create or look up your local user by the sub claim. See Scopes & claims.

Common mistakes

  • Reusing a code. Single use; a retry needs a fresh authorization.
  • Taking too long. Codes expire in 60 seconds — do the exchange in the callback handler, not a background job.
  • A differently-encoded redirect_uri. It must match the registered value character for character.
  • Losing the verifier. If your session is not shared across instances, the exchange fails behind a load balancer.
  • Keying users on email. Emails change. Use sub.