aYOUne
← Developer Hub

OAuth 2.0 — Step by Step

With the authorization-code flow + PKCE a third-party app accesses the API on behalf of an aYOUne user without knowing their password. The granted access is always limited to scope ∩ the user's rights ∩ package at consent time and frozen into the token — a third-party token never inherits the user's full rights.

1. Register your app

Register your app under Developer → Apps. You get a client_id (oac_...). For local development a public client (PKCE-only, no secret) is easiest. Set your redirect URI, e.g. http://localhost:8410/callback, and the scopes you need.

2. Generate PKCE

Per sign-in attempt, create a random code_verifier and derive the code_challenge (SHA-256, base64url). S256 is mandatory — plain is rejected.

import crypto from 'crypto';
const b64url = (b) => b.toString('base64').replace(/\+/g,'-').replace(/\//g,'_').replace(/=+$/,'');

const verifier  = b64url(crypto.randomBytes(32));                       // keep secret
const challenge = b64url(crypto.createHash('sha256').update(verifier).digest());
const state     = b64url(crypto.randomBytes(16));                       // CSRF protection

3. Authorize URL (browser)

Redirect the user to the consent screen. They sign in and approve the scopes.

https://login.ayoune.app/oauth/authorize
  ?response_type=code
  &client_id=oac_...
  &redirect_uri=http://localhost:8410/callback
  &scope=crm.consumers.view openid
  &state=<state>
  &code_challenge=<challenge>
  &code_challenge_method=S256

4. Exchange the code for a token

The provider calls your redirect URI with ?code=...&state=.... Verify state, then exchange the code (with the code_verifier) for a token.

curl -X POST https://auth.ayoune.app/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d grant_type=authorization_code \
  -d code=<code> \
  -d code_verifier=<verifier> \
  -d client_id=oac_... \
  -d redirect_uri=http://localhost:8410/callback
  # confidential clients also:  -d client_secret=<secret>
{
  "access_token": "eyJ...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "eyJ...",
  "scope": "crm.consumers.view openid"
}

5. Scope-limited API call

The access token carries only the granted scope. A call outside it returns 403.

curl -H "Authorization: Bearer <access_token>" \
  https://crm-api.ayoune.app/consumers?limit=3

6. Refresh & revoke

# Refresh (rotation)
curl -X POST https://auth.ayoune.app/oauth/token \
  -d grant_type=refresh_token -d refresh_token=<refresh_token> -d client_id=oac_...

# Revoke
curl -X POST https://auth.ayoune.app/oauth/revoke \
  -d token=<access_or_refresh_token> -d client_id=oac_...

Discovery

All endpoints are self-describing (RFC 8414):

curl https://auth.ayoune.app/.well-known/oauth-authorization-server

Scopes

Scopes are aYOUne rights (module.entity.action, with wildcards crm.consumers.* / crm.*) plus the OIDC standard scopes openid / profile / email (→ /oauth/userinfo).

Reference app

A runnable minimal app (Node/Express) with the complete flow lives in the monorepo under tooling/oauth-reference-app/ — copy it, set your client_id, npm start.