Skip to main content

KukurukuID — OAuth 2.0 Integration

KukurukuID is an OAuth 2.0 / OpenID Connect authentication server. Merchants can add a "Login with KukurukuID" button to their websites, allowing Kukuruku users to authenticate seamlessly.

Prerequisites

Contact the Kukuruku team to receive your OAuth 2.0 credentials:

CredentialDescription
client_idYour OAuth 2.0 client identifier
client_secretYour OAuth 2.0 client secret (for confidential clients)
redirect_uriApproved callback URL(s) for your application

Endpoints

Auth Server URL: https://auth.kukuruku.win

EndpointURLMethod
Authorization/realms/public/protocol/openid-connect/authGET
Token/realms/public/protocol/openid-connect/tokenPOST
UserInfo/realms/public/protocol/openid-connect/userinfoGET
Logout/realms/public/protocol/openid-connect/logoutPOST
JWKS/realms/public/protocol/openid-connect/certsGET
Discovery/realms/public/.well-known/openid-configurationGET

Supported Flows

FlowUse case
Authorization CodeServer-side applications (recommended)
Authorization Code + PKCEClient-side / SPA applications

Authorization Code Flow (Confidential Clients)

For server-side applications that can securely store a client_secret.

Step 1: Redirect to Login

GET {auth_url}/realms/public/protocol/openid-connect/auth

Redirect the user to KukurukuID's login page. After authentication, the user is redirected back to your redirect_uri with an authorization code.

Query Parameters:

ParameterTypeRequiredDescription
client_idstringYesYour client identifier
redirect_uristringYesYour registered callback URL
response_typestringYesMust be code
scopestringNoSpace-separated scopes. Default: openid
statestringRecommendedRandom string for CSRF protection

Example:

https://auth.kukuruku.win/realms/public/protocol/openid-connect/auth
?client_id=your-client-id
&redirect_uri=https://your-site.com/callback
&response_type=code
&scope=openid profile email
&state=random-state-string

After successful authentication, the user is redirected to:

https://your-site.com/callback
?code=c2a6dbb6-0640-4358-a047-8f87bde1d0ab.69eaf4bf-...
&session_state=69eaf4bf-58da-45e7-8ae7-92e6155e1ff8
&iss=https://auth.kukuruku.win/realms/public
&state=random-state-string

Step 2: Exchange Code for Tokens

POST {auth_url}/realms/public/protocol/openid-connect/token

Exchange the authorization code for access and refresh tokens.

FieldTypeRequiredDescription
client_idstringYesYour client identifier
client_secretstringYesYour client secret
grant_typestringYesMust be authorization_code
codestringYesAuthorization code from Step 1
redirect_uristringYesSame redirect_uri as in Step 1

Response 200:

FieldTypeDescription
access_tokenstringJWT access token (5 min TTL)
refresh_tokenstringToken for obtaining new access tokens
token_typestringAlways Bearer
id_tokenstringOpenID Connect ID token with user claims
expires_inintegerAccess token lifetime in seconds
scopestringGranted scopes

Authorization Code Flow with PKCE (Public Clients)

For client-side (SPA) applications where storing a client_secret is not possible.

Step 1: Generate Code Verifier and Challenge

Before redirecting, generate a random string (code verifier) and its SHA-256 hash (code challenge):

// Generate code_verifier (43-128 characters, URL-safe)
const array = new Uint8Array(32);
crypto.getRandomValues(array);
const codeVerifier = btoa(String.fromCharCode(...array))
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');

// Generate code_challenge (SHA-256 hash of verifier)
const digest = await crypto.subtle.digest('SHA-256',
new TextEncoder().encode(codeVerifier));
const codeChallenge = btoa(String.fromCharCode(...new Uint8Array(digest)))
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');

Step 2: Redirect to Login

GET {auth_url}/realms/public/protocol/openid-connect/auth

Query Parameters:

ParameterTypeRequiredDescription
client_idstringYesYour client identifier
redirect_uristringYesYour registered callback URL
response_typestringYesMust be code
code_challenge_methodstringYesMust be S256
code_challengestringYesSHA-256 hash of the code verifier
scopestringNoSpace-separated scopes
statestringRecommendedRandom string for CSRF protection

Step 3: Exchange Code for Tokens

POST {auth_url}/realms/public/protocol/openid-connect/token

FieldTypeRequiredDescription
client_idstringYesYour client identifier
grant_typestringYesMust be authorization_code
codestringYesAuthorization code from Step 2
redirect_uristringYesSame redirect_uri as in Step 2
code_verifierstringYesOriginal random string from Step 1
note

No client_secret is needed for public clients — the code_verifier proves the token request came from the same party that initiated the authorization.

The response format is identical to the Confidential Client response.


Refreshing Tokens

POST {auth_url}/realms/public/protocol/openid-connect/token

Access tokens expire after 5 minutes. Use the refresh token to obtain new tokens without requiring the user to log in again.


User Context API

Get Current User

GET /api/v1/me

Returns profile information about the authenticated user. This endpoint is on the Kukuruku API (https://api.kukuruku.win), not the auth server.

Response 200:

Response Fields

FieldTypeDescription
idstringUser's unique identifier in Kukuruku
first_namestring|nullFirst name
middle_namestring|nullMiddle name
last_namestring|nullLast name
without_middle_namebooleanUser has no middle name
date_of_birthstring|nullDate of birth (YYYY-MM-DD)
place_of_birthstring|nullPlace of birth
default_currencystringUser's default currency (RUB, USD, USDT)

JWT Token Claims

The access token is a signed JWT (RS256) containing these claims:

ClaimTypeDescription
substringUser UUID in Keycloak
emailstringUser's email address
email_verifiedbooleanWhether email is verified
namestringFull name
given_namestringFirst name
family_namestringLast name
user_idstringKukuruku user ID (numeric)
preferred_usernamestringUsername (usually email)
issstringIssuer URL
expintegerToken expiration (Unix timestamp)
JWT Validation

You can validate the JWT signature using the JWKS endpoint: GET https://auth.kukuruku.win/realms/public/protocol/openid-connect/certs


Available Scopes

ScopeClaims included
openidsub, auth_time
profilename, given_name, family_name
emailemail, email_verified
phonephone_number, phone_number_verified
offline_accessEnables long-lived refresh tokens

Example: Node.js Integration

const express = require('express');
const axios = require('axios');

const AUTH_URL = 'https://auth.kukuruku.win/realms/public';
const CLIENT_ID = 'your-client-id';
const CLIENT_SECRET = 'your-client-secret';
const REDIRECT_URI = 'https://your-site.com/callback';

const app = express();

// Step 1: Redirect to login
app.get('/login', (req, res) => {
const state = crypto.randomUUID();
const url = `${AUTH_URL}/protocol/openid-connect/auth` +
`?client_id=${CLIENT_ID}` +
`&redirect_uri=${encodeURIComponent(REDIRECT_URI)}` +
`&response_type=code` +
`&scope=openid+profile+email` +
`&state=${state}`;
res.redirect(url);
});

// Step 2: Exchange code for tokens
app.get('/callback', async (req, res) => {
const { code } = req.query;

const tokenResponse = await axios.post(
`${AUTH_URL}/protocol/openid-connect/token`,
new URLSearchParams({
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
grant_type: 'authorization_code',
code,
redirect_uri: REDIRECT_URI,
}),
{ headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }
);

const { access_token } = tokenResponse.data;

// Step 3: Get user profile
const userResponse = await axios.get(
'https://api.kukuruku.win/api/v1/me',
{ headers: { Authorization: `Bearer ${access_token}` } }
);

const user = userResponse.data.data;
// user.id, user.first_name, user.email, etc.
// Create or find user in your system by user.id
});

Common Errors

ErrorCauseSolution
invalid_grantAuthorization code expired (>60 sec) or already usedRe-initiate the authorization flow
invalid_clientWrong client_secretVerify your credentials
unauthorized_clientClient not configured for this grant typeContact Kukuruku team
invalid_redirect_uriredirect_uri not registeredContact Kukuruku team to add your URI