OAuth 2.0 Quickstart
Use the authorization code flow with PKCE (RFC 7636) when your app acts on behalf of a real user — for example, a desktop client, a personal CRM plugin, or a mobile app.
Not building a user-facing app? If you need server-to-server automation (no human in the loop), use service accounts instead.
Prerequisites
- Register your OAuth application with EUnifyer. For a single-org integration, a platform admin does this in Back-office → Service Accounts. Note your
client_id. - Identify the redirect URI your app will handle after the user authorizes.
- Decide which scopes your app needs.
Step 1 — Build the authorization URL
Redirect the user to Keycloak’s authorization endpoint:
GET https://auth.eunifyer.com/realms/eunify/protocol/openid-connect/auth
?response_type=code
&client_id=YOUR_CLIENT_ID
&redirect_uri=https://yourapp.example.com/callback
&scope=openid drive:read drive:write
&code_challenge=BASE64URL(SHA256(code_verifier))
&code_challenge_method=S256
&state=RANDOM_STATE_VALUEGenerate the PKCE code verifier and challenge before redirecting:
function generateCodeVerifier(): string {
const bytes = crypto.getRandomValues(new Uint8Array(32));
return btoa(String.fromCharCode(...bytes))
.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
}
async function generateCodeChallenge(verifier: string): Promise<string> {
const data = new TextEncoder().encode(verifier);
const hash = await crypto.subtle.digest("SHA-256", data);
return btoa(String.fromCharCode(...new Uint8Array(hash)))
.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
}Store code_verifier in session storage — you need it in step 3.
Step 2 — User authorises
The user sees EUnifyer’s consent screen and grants the requested scopes. Keycloak redirects back to your redirect_uri:
https://yourapp.example.com/callback
?code=AUTHORIZATION_CODE
&state=RANDOM_STATE_VALUEVerify state matches what you sent to prevent CSRF.
Step 3 — Exchange code for tokens
curl -X POST https://auth.eunifyer.com/realms/eunify/protocol/openid-connect/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code" \
-d "client_id=YOUR_CLIENT_ID" \
-d "redirect_uri=https://yourapp.example.com/callback" \
-d "code=AUTHORIZATION_CODE" \
-d "code_verifier=YOUR_CODE_VERIFIER"const tokenRes = await fetch(
"https://auth.eunifyer.com/realms/eunify/protocol/openid-connect/token",
{
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "authorization_code",
client_id: CLIENT_ID,
redirect_uri: REDIRECT_URI,
code,
code_verifier: codeVerifier,
}),
},
);
const tokens = await tokenRes.json();
// { access_token, refresh_token, expires_in, ... }import httpx
res = httpx.post(
"https://auth.eunifyer.com/realms/eunify/protocol/openid-connect/token",
data={
"grant_type": "authorization_code",
"client_id": CLIENT_ID,
"redirect_uri": REDIRECT_URI,
"code": code,
"code_verifier": code_verifier,
},
)
res.raise_for_status()
tokens = res.json()Response:
{
"access_token": "eyJ...",
"refresh_token": "eyJ...",
"expires_in": 900,
"refresh_expires_in": 1800,
"token_type": "Bearer",
"scope": "openid drive:read drive:write"
}The access_token is a short-lived RS256 JWT (15 minutes by default). Store refresh_token securely to get new access tokens without re-prompting the user.
Step 4 — Call the API
Pass the access token in the Authorization header:
curl https://api.eunifyer.com/api/v1/drive/browse \
-H "Authorization: Bearer eyJ..."const response = await fetch("https://api.eunifyer.com/api/v1/drive/browse", {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
const data = await response.json();Step 5 — Refresh the token
Refresh before the access token expires:
curl -X POST https://auth.eunifyer.com/realms/eunify/protocol/openid-connect/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=refresh_token" \
-d "client_id=YOUR_CLIENT_ID" \
-d "refresh_token=YOUR_REFRESH_TOKEN"If the refresh token has expired (or the user has revoked access), restart the authorization flow from step 1.
Revoking access
To revoke a token:
curl -X POST https://auth.eunifyer.com/realms/eunify/protocol/openid-connect/revoke \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id=YOUR_CLIENT_ID" \
-d "token=ACCESS_OR_REFRESH_TOKEN"OpenID Connect discovery
Keycloak publishes its full configuration at:
https://auth.eunifyer.com/realms/eunify/.well-known/openid-configurationUse this for endpoint discovery — do not hardcode token or JWKS URLs.
Security checklist
- Always use PKCE — do not use the implicit flow.
- Never store
client_secretin browser JS or a mobile app binary. - Validate
stateon every callback. - Use short-lived access tokens; refresh silently in the background.
- Request only the scopes your app actually needs.