Ppoppo Docs

Add Sign in with Ppoppo (OAuth2 PKCE)

This guide wires Sign in with Ppoppo end to end. It assumes you have registered a login client (see the Quickstart). For exact endpoint parameters and token fields, follow the links into the Sign-in reference — this page is the recipe, not the field tables.

Every Ppoppo login client uses PKCE S256.

1. Generate a PKCE pair

Before redirecting the user, generate a fresh code_verifier (43–128 characters) and derive its code_challenge (base64url-encoded SHA-256). Generate a new pair for every login attempt.

function generateCodeVerifier() {
  const array = new Uint8Array(32);
  crypto.getRandomValues(array);
  return base64UrlEncode(array);
}

async function generateCodeChallenge(verifier) {
  const data = new TextEncoder().encode(verifier);
  const digest = await crypto.subtle.digest('SHA-256', data);
  return base64UrlEncode(new Uint8Array(digest));
}

function base64UrlEncode(buffer) {
  return btoa(String.fromCharCode(...buffer))
    .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
}

const codeVerifier = generateCodeVerifier();
const codeChallenge = await generateCodeChallenge(codeVerifier);
import secrets, hashlib, base64

def _b64url(data: bytes) -> str:
    return base64.urlsafe_b64encode(data).rstrip(b"=").decode("ascii")

def generate_code_verifier() -> str:
    return _b64url(secrets.token_bytes(32))

def generate_code_challenge(verifier: str) -> str:
    return _b64url(hashlib.sha256(verifier.encode("ascii")).digest())

Keep the code_verifier for the token exchange (e.g. in the user's session).

2. Redirect to the authorization endpoint

Send the user to GET /oauth/authorize with your client_id, redirect_uri, response_type=code, scope, a random state, the code_challenge, and code_challenge_method=S256:

https://accounts.ppoppo.com/oauth/authorize
  ?client_id=yourapp_login_client
  &redirect_uri=https://yourapp.com/auth/callback
  &response_type=code
  &scope=openid%20profile
  &state=RANDOM_STATE
  &code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
  &code_challenge_method=S256

Request only the scopes you need — add email to receive the user's address, messaging / poll to use the External API. The full parameter table is in the authorization endpoint reference.

3. Handle the callback

Ppoppo redirects back to your redirect_uri with code and state. Verify state matches the value you sent, then proceed. If the callback carries error instead, surface error_description — see Errors.

4. Exchange the code for tokens

From your server (never the browser), POST /oauth/token:

grant_type=authorization_code
&code=AUTH_CODE
&redirect_uri=https://yourapp.com/auth/callback
&client_id=yourapp_login_client
&code_verifier=ORIGINAL_CODE_VERIFIER

The code_verifier authenticates the client — there is no client_secret. You receive an access_token (1 hour), a refresh_token (180-day inactivity), and an id_token when openid was requested. See the token fields.

5. Fetch the user

Call GET /oauth/userinfo with the access token to get the user's sub (ppnum_id) and ppnum. Find-or-create a record keyed by sub, and store the encrypted refresh token against the session — see the User mapping guide.

6. Refresh before expiry

The access token lasts one hour. Refresh with grant_type=refresh_token; the same refresh token stays valid (no rotation). If a refresh fails with invalid_grant, the user has revoked access — clear stored tokens and send them back through step 2.

Incomplete accounts

If a user has not finished Ppoppo registration, the callback returns error=account_setup_required. Prompt them to complete their Ppoppo registration, then retry.

Keep tokens on the server, out of reach of browser JavaScript (HttpOnly cookies or a server-side session). Encrypt refresh tokens at rest. More in Security.