“Sign in with QAIRU” allows an external platform to obtain data of the information system on behalf of a student. The student signs in on the QAIRU sign-in page and confirms access on the consent screen, after which the application obtains tokens under the OAuth 2.0 (Authorization Code flow with mandatory PKCE) and OpenID Connect protocols.
Sign-in is permitted for student accounts only. Sign-in with any other account is rejected on the sign-in page, and the application receives no response.
The samples in this section use variables obtained at the previous steps: code_verifier, state, nonce, access_token, refresh_token.
5.1. Interaction procedure
- The application generates the PKCE parameters,
stateandnonce, and stores them in the server-side session of the user. - The application redirects the browser of the user to the authorization address.
- The student signs in with a QAIRU account and confirms access on the consent screen.
- The authorization server returns the browser to the redirect address with an authorization code.
- The application server exchanges the authorization code for tokens.
- The application calls
/os/v1/…operations with the access token and refreshes it with therefresh_token.
5.2. Authorization server addresses
The authorization server addresses are published in the OpenID Connect Discovery document. The addresses should be taken from this document rather than set manually in code.
https://auth.bbx.kz/realms/qairu/.well-known/openid-configuration{
"issuer": "https://auth.bbx.kz/realms/qairu",
"authorization_endpoint": "https://auth.bbx.kz/realms/qairu/protocol/openid-connect/auth",
"token_endpoint": "https://auth.bbx.kz/realms/qairu/protocol/openid-connect/token",
"userinfo_endpoint": "https://auth.bbx.kz/realms/qairu/protocol/openid-connect/userinfo",
"end_session_endpoint": "https://auth.bbx.kz/realms/qairu/protocol/openid-connect/logout",
"revocation_endpoint": "https://auth.bbx.kz/realms/qairu/protocol/openid-connect/revoke",
"jwks_uri": "https://auth.bbx.kz/realms/qairu/protocol/openid-connect/certs",
"code_challenge_methods_supported": ["plain", "S256"]
}The response shows an excerpt of the document. The public keys for verifying token signatures are published at the jwks_uri address.
5.3. PKCE, state and nonce parameters
New values are generated for every sign-in:
code_verifieris a random string of 43 to 128 characters from the setA–Z,a–z,0–9,-,.,_,~;code_challengeis the valueBASE64URL(SHA-256(code_verifier))without trailing=characters;stateis a random value that protects against cross-site request forgery; it is verified on return;nonceis a random value that is returned in theid_tokenand verified when it is validated.
# code_verifier: 86 characters of the base64url alphabet
CODE_VERIFIER=$(openssl rand -base64 64 | tr -d '\n=' | tr '+/' '-_' | cut -c1-86)
# code_challenge = BASE64URL(SHA-256(code_verifier))
CODE_CHALLENGE=$(printf '%s' "$CODE_VERIFIER" \
| openssl dgst -sha256 -binary | openssl base64 | tr -d '\n=' | tr '+/' '-_')
STATE=$(openssl rand -hex 16)
NONCE=$(openssl rand -hex 16)The code_verifier, state and nonce values are kept in the server-side session of the user until the code exchange is completed. Only the S256 method is used; the plain method is not permitted.
5.4. Authorization address
The browser of the user is redirected to the authorization_endpoint address with the following parameters:
| PARAMETER | MANDATORY | VALUE |
|---|---|---|
response_type | Yes | code |
client_id | Yes | Client identifier issued at registration, for example ext-campus-planner |
redirect_uri | Yes | Redirect address matching the registered one |
scope | Yes | openid and module scopes separated by spaces, for example openid qairu.scheduling.read |
state | Yes | Random value stored in the session |
nonce | Yes | Random value stored in the session |
code_challenge | Yes | BASE64URL(SHA-256(code_verifier)) |
code_challenge_method | Yes | S256 |
ui_locales | No | Language of the sign-in page and the consent screen: ru, kk or en |
https://auth.bbx.kz/realms/qairu/protocol/openid-connect/auth
?response_type=code
&client_id=ext-campus-planner
&redirect_uri=https%3A%2F%2Fplanner.example.com%2Foauth%2Fcallback
&scope=openid%20qairu.academic.read%20qairu.scheduling.read
&state=af0ifjsldkj
&nonce=n-0S6_WzA2Mj
&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
&code_challenge_method=S256
&ui_locales=ru5.5. Consent screen
After sign-in the student is shown a consent screen with the name of the application and the list of requested modules. The student grants or declines access.
- consent is remembered: on a repeated sign-in with the same scopes the consent screen is not shown;
- when new scopes are requested, the consent screen is shown again;
- scopes of modules not permitted for the application at registration are not granted.
When access is granted, the browser returns to the redirect address with an authorization code:
https://planner.example.com/oauth/callback?state=af0ifjsldkj&session_state=3f0c9a52-6d1e-4b8a-9f3e-2c7b5d8e1a40&iss=https%3A%2F%2Fauth.bbx.kz%2Frealms%2Fqairu&code=7b2e1f0a-5c3d-4e8f-a1b2-c3d4e5f6a7b8.3f0c9a52-6d1e-4b8a-9f3e-2c7b5d8e1a40.ext-campus-plannerIf the student declines, the redirect address contains the error code access_denied:
https://planner.example.com/oauth/callback?error=access_denied&error_description=User+denied+consent&state=af0ifjsldkjstate value from the redirect address is compared with the value stored in the session; if they do not match, the request is rejected. The authorization code is valid for no longer than one minute and may be used once.5.6. Exchanging the code for tokens
The application server sends a POST request to the token_endpoint address in the application/x-www-form-urlencoded format. The client authenticates with its identifier and secret under the HTTP Basic scheme; the client_id and client_secret parameters may also be sent in the request body.
curl -X POST https://auth.bbx.kz/realms/qairu/protocol/openid-connect/token \
-u "ext-campus-planner:YOUR_CLIENT_SECRET" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d grant_type=authorization_code \
-d code=AUTHORIZATION_CODE \
--data-urlencode redirect_uri=https://planner.example.com/oauth/callback \
-d code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires_in": 3600,
"refresh_expires_in": 2592000,
"refresh_token": "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"not-before-policy": 0,
"session_state": "3f0c9a52-6d1e-4b8a-9f3e-2c7b5d8e1a40",
"scope": "openid profile email qairu-student qairu.academic.read qairu.scheduling.read"
}Response fields:
access_tokenis the access token for calling the API; it is valid for 1 hour (expires_in);refresh_tokenis the refresh token; the session is valid for up to 30 days (refresh_expires_in);id_tokenis the OpenID Connect token with information about the student;scopecontains the scopes actually granted; the list may differ from the requested one.
5.7. Calling the API on behalf of a student
The access token is passed in the Authorization: Bearer <access token> header. The operation paths are the same as for API token authorization; GET operations of the modules whose scopes have been granted are available, and for the ALEM module the operations listed in the “ALEM module” section.
curl https://api.bbx.kz/os/v1/me \
-H "Authorization: Bearer ACCESS_TOKEN"
curl https://api.bbx.kz/os/v1/academic/catalog/disciplines \
-H "Authorization: Bearer ACCESS_TOKEN"The information system applies the rights of the student: the response contains only the data available to the student in the student portal. The GET /os/v1/me request with an access token returns information about the application, the student and the granted modules:
{
"app": {
"clientId": "ext-campus-planner",
"name": "Campus Planner"
},
"user": {
"sub": "8b6f1c2e-3a4d-4f5e-9a7b-1c2d3e4f5a6b",
"name": "Aigerim Nurlanova",
"email": "a.nurlanova@qairu.edu.kz",
"studentId": "250142"
},
"modules": ["ACADEMIC", "SCHEDULING"],
"expiresAt": "2026-10-28T09:00:00Z"
}5.8. Refreshing the access token
When the access token expires, an API request is rejected with the code 401 invalid_token. A new access token is requested with the refresh_token without the student’s participation:
curl -X POST https://auth.bbx.kz/realms/qairu/protocol/openid-connect/token \
-u "ext-campus-planner:YOUR_CLIENT_SECRET" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d grant_type=refresh_token \
-d refresh_token=REFRESH_TOKENThe response has the same composition as for the code exchange. The refresh_token received is stored instead of the previous one.
400 response with the code invalid_grant means that the session has ended: the 30-day period has expired, the student has revoked access, or the session has been terminated by the university administration. In this case the student is asked to sign in again.5.9. id_token and user information
Before an id_token is used, its signature is verified against the jwks_uri keys, together with the issuer iss, the audience aud (equal to client_id), the expiry exp and the nonce value. Contents of an id_token:
{
"exp": 1793178000,
"iat": 1793174400,
"auth_time": 1793174388,
"iss": "https://auth.bbx.kz/realms/qairu",
"aud": "ext-campus-planner",
"sub": "8b6f1c2e-3a4d-4f5e-9a7b-1c2d3e4f5a6b",
"typ": "ID",
"azp": "ext-campus-planner",
"nonce": "n-0S6_WzA2Mj",
"sid": "3f0c9a52-6d1e-4b8a-9f3e-2c7b5d8e1a40",
"name": "Aigerim Nurlanova",
"given_name": "Aigerim",
"family_name": "Nurlanova",
"preferred_username": "a.nurlanova",
"email": "a.nurlanova@qairu.edu.kz",
"email_verified": true,
"student_id": "250142"
}| CLAIM | VALUE |
|---|---|
sub | Permanent identifier of the student; used to link the account in the external platform |
name, given_name, family_name | Full name, given name and family name of the student |
preferred_username | Username in the information system |
email, email_verified | Email address and the sign of its verification |
student_id | Student number; present if it is specified in the account |
The same information is returned by a request to the userinfo_endpoint address with an access token:
curl https://auth.bbx.kz/realms/qairu/protocol/openid-connect/userinfo \
-H "Authorization: Bearer ACCESS_TOKEN"{
"sub": "8b6f1c2e-3a4d-4f5e-9a7b-1c2d3e4f5a6b",
"name": "Aigerim Nurlanova",
"given_name": "Aigerim",
"family_name": "Nurlanova",
"preferred_username": "a.nurlanova",
"email": "a.nurlanova@qairu.edu.kz",
"email_verified": true,
"student_id": "250142"
}The email address and the username may change. Only sub is used to link accounts.
5.10. Sign-out
To sign out, the application deletes the stored tokens and revokes the refresh token:
curl -X POST https://auth.bbx.kz/realms/qairu/protocol/openid-connect/revoke \
-u "ext-campus-planner:YOUR_CLIENT_SECRET" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d token=REFRESH_TOKEN \
-d token_type_hint=refresh_tokenTo end the QAIRU session in the browser, the user is redirected to the end_session_endpoint address. The post_logout_redirect_uri parameter is permitted only for an address specified at the registration of the application.
https://auth.bbx.kz/realms/qairu/protocol/openid-connect/logout
?client_id=ext-campus-planner
&id_token_hint=ID_TOKEN
&post_logout_redirect_uri=https%3A%2F%2Fplanner.example.com%2F5.11. Revocation of access by the student
The student disconnects the application in the student portal: the “Profile” section, the “Connected applications” card, the “Disconnect” action. After disconnection:
- the access and refresh tokens issued to the application cease to be valid immediately;
- API requests with the previous access token are rejected with the code
401token_revoked; - a token refresh fails with the error
invalid_grant; - at the next sign-in the student is shown the consent screen again.
{
"error": {
"type": "authentication_error",
"code": "token_revoked",
"message": "The student has revoked access for this application.",
"request_id": "0d8e4c1a-7f2b-4a9e-b3c5-6e1d2f8a9b70"
}
}The university administration may disable an application completely. In this case requests are rejected with the code 403 app_disabled, and sign-in through the application becomes impossible.
5.12. Validity periods
| OBJECT | PERIOD |
|---|---|
| Authorization code | No longer than 1 minute, single use |
Access token access_token | 1 hour |
Session and refresh token refresh_token | 30 days from sign-in; a new sign-in is required after this period |
| Consent of the student | Until revoked by the student or until the application is disabled |
5.13. OAuth errors
Authorization server errors are returned under the OAuth 2.0 protocol: at authorization, in the error and error_description parameters of the redirect address; at token issue, in the response body:
{
"error": "invalid_grant",
"error_description": "Code not valid"
}| CODE | RETURNED IN | MEANING | RECOMMENDED ACTION |
|---|---|---|---|
access_denied | Redirect address | The student declined access on the consent screen. | Inform the user that sign-in was not completed and offer to sign in again. |
invalid_request | Redirect address; token issue, HTTP 400 | A mandatory parameter is missing or an invalid parameter is specified, including a missing code_challenge. | Verify the request parameters. |
invalid_scope | Redirect address | An invalid scope has been requested. | Specify openid and the scopes of the modules permitted for the application. |
unauthorized_client | Redirect address; token issue, HTTP 400 | The application is not permitted to obtain a code or a token in the specified way. | Verify the response_type and grant_type values. |
temporarily_unavailable | Redirect address | The authorization server is temporarily unavailable. | Repeat sign-in later. |
server_error | Redirect address; token issue | An internal error of the authorization server. | Repeat the request later. |
invalid_client | Token issue, HTTP 401 | The client identifier or secret is invalid, or the application is disabled. | Verify client_id and client_secret; after the secret has been reissued, use the new one. |
invalid_grant | Token issue, HTTP 400 | The authorization code is invalid, expired or already used; code_verifier or redirect_uri do not match the values sent at authorization; the refresh token has expired or been revoked. | Repeat sign-in; when refreshing a token, ask the student to sign in again. |
unsupported_grant_type | Token issue, HTTP 400 | An unsupported grant_type is specified. | Use authorization_code or refresh_token. |
API error codes related to the student access token are listed in the “Error codes” section.
5.14. Scopes
Default scopes are granted to the application at every sign-in. Module scopes are specified in the scope parameter and are granted only for the modules permitted for the application at registration.
| SCOPE | TYPE | PURPOSE |
|---|---|---|
openid | Default | Mandatory. Confirms sign-in under OpenID Connect and the issue of an id_token. |
profile | Default | Full name, given name, family name and username of the student. |
email | Default | Email address and the sign of its verification. |
qairu-student | Default | Student number student_id. |
qairu.alem.read | Module ALEM | Language models, embeddings, reranking and speech recognition of the Alem.Cloud platform |
qairu.users.read | Module USERS | User accounts and roles |
qairu.files.read | Module FILES | Files of the information system |
qairu.learning.read | Module LEARNING | Learning: courses, assignments, grades and competencies |
qairu.academic.read | Module ACADEMIC | Educational programs, curricula and the course catalog |
qairu.scheduling.read | Module SCHEDULING | Class schedule |
qairu.skillmatch.read | Module SKILLMATCH | Matching of skills and professional requirements |
qairu.clubs.read | Module CLUBS | Student clubs and their events |
qairu.projects.read | Module PROJECTS | Student and research projects |
qairu.ideas.read | Module IDEAS | Student ideas and complaints |