CastyrDocs

Sign in with Castyr

The authorization code flow with PKCE, for web, single-page, mobile and desktop apps.

You send the user to Castyr, they approve, and Castyr sends them back with a short-lived code that you exchange for tokens.

Make a PKCE pair

For every sign-in, create a random code_verifier (43–128 characters) and derive the code_challenge from it: base64url, no padding, of its SHA-256. Keep the verifier until the user comes back. Public clients must do this; web apps should too.

pkce.js
// Browser or Node 18+
const b64url = (buf) => btoa(String.fromCharCode(...new Uint8Array(buf)))
  .replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
const verifier = b64url(crypto.getRandomValues(new Uint8Array(32)));
const challenge = b64url(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(verifier)));

Send the user to Castyr

https://sso.castyr.cloud/oauth/authorize
  ?response_type=code
  &client_id=YOUR_CLIENT_ID
  &redirect_uri=https%3A%2F%2Fyourapp.example%2Fcallback
  &scope=openid%20profile
  &state=RANDOM_STATE
  &code_challenge=CHALLENGE
  &code_challenge_method=S256

Prop

Type

If the user isn't signed in, Castyr asks them to sign in first. They then see a consent screen, unless they already approved these scopes for your app. Sensitive scopes always need their explicit approval once.

Handle the redirect back

https://yourapp.example/callback?code=AUTH_CODE&state=RANDOM_STATE&iss=https%3A%2F%2Fsso.castyr.cloud
  • Check that state is the value you sent, and that iss is https://sso.castyr.cloud.
  • On failure you get error and error_description instead of code: for example access_denied when the user clicks Cancel, or login_required / consent_required with prompt=none.

Problems with client_id or redirect_uri are shown to the user and never redirected, so they can't be used to bounce people to other sites.

Exchange the code for tokens

Codes are single-use and expire after 5 minutes. Send the same redirect_uri and your verifier.

curl -X POST https://sso.castyr.cloud/oauth/token \
  -u "YOUR_CLIENT_ID:YOUR_CLIENT_SECRET" \
  -d grant_type=authorization_code \
  -d code=AUTH_CODE \
  -d redirect_uri=https://yourapp.example/callback \
  -d code_verifier=VERIFIER
Response
{
  "access_token": "cat_…",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "openid profile",
  "refresh_token": "crt_…",
  "id_token": "eyJ…"
}

refresh_token is included when your app may refresh; id_token when you asked for openid. Using a code twice fails, and also revokes the tokens that code already produced.

Next: call user info or verify the ID token, and keep the session alive with refresh tokens.

On this page