Skip to content
DEVELOPERS

5. “Sign in with QAIRU”: OAuth 2.0 and OpenID Connect

Student sign-in, the consent screen, code exchange, refresh, sign-out and revocation of access.

“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

  1. The application generates the PKCE parameters, state and nonce, and stores them in the server-side session of the user.
  2. The application redirects the browser of the user to the authorization address.
  3. The student signs in with a QAIRU account and confirms access on the consent screen.
  4. The authorization server returns the browser to the redirect address with an authorization code.
  5. The application server exchanges the authorization code for tokens.
  6. The application calls /os/v1/… operations with the access token and refreshes it with the refresh_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.

URL
https://auth.bbx.kz/realms/qairu/.well-known/openid-configuration
JSON
{
  "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_verifier is a random string of 43 to 128 characters from the set A–Z, a–z, 0–9, -, ., _, ~;
  • code_challenge is the value BASE64URL(SHA-256(code_verifier)) without trailing = characters;
  • state is a random value that protects against cross-site request forgery; it is verified on return;
  • nonce is a random value that is returned in the id_token and 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:

Authorization address parameters
PARAMETERMANDATORYVALUE
response_typeYescode
client_idYesClient identifier issued at registration, for example ext-campus-planner
redirect_uriYesRedirect address matching the registered one
scopeYesopenid and module scopes separated by spaces, for example openid qairu.scheduling.read
stateYesRandom value stored in the session
nonceYesRandom value stored in the session
code_challengeYesBASE64URL(SHA-256(code_verifier))
code_challenge_methodYesS256
ui_localesNoLanguage 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=ru

5.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:

URL
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-planner

If the student declines, the redirect address contains the error code access_denied:

URL
https://planner.example.com/oauth/callback?error=access_denied&error_description=User+denied+consent&state=af0ifjsldkj

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
JSON
{
  "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_token is the access token for calling the API; it is valid for 1 hour (expires_in);
  • refresh_token is the refresh token; the session is valid for up to 30 days (refresh_expires_in);
  • id_token is the OpenID Connect token with information about the student;
  • scope contains 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:

JSON
{
  "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_TOKEN

The response has the same composition as for the code exchange. The refresh_token received is stored instead of the previous one.

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:

JSON
{
  "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"
}
User information
CLAIMVALUE
subPermanent identifier of the student; used to link the account in the external platform
name, given_name, family_nameFull name, given name and family name of the student
preferred_usernameUsername in the information system
email, email_verifiedEmail address and the sign of its verification
student_idStudent 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"
JSON
{
  "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_token

To 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.

URL
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%2F

5.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 401 token_revoked;
  • a token refresh fails with the error invalid_grant;
  • at the next sign-in the student is shown the consent screen again.
JSON
{
  "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

Validity periods
OBJECTPERIOD
Authorization codeNo longer than 1 minute, single use
Access token access_token1 hour
Session and refresh token refresh_token30 days from sign-in; a new sign-in is required after this period
Consent of the studentUntil 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:

JSON
{
  "error": "invalid_grant",
  "error_description": "Code not valid"
}
Authorization server errors
CODERETURNED INMEANINGRECOMMENDED ACTION
access_deniedRedirect addressThe student declined access on the consent screen.Inform the user that sign-in was not completed and offer to sign in again.
invalid_requestRedirect address; token issue, HTTP 400A mandatory parameter is missing or an invalid parameter is specified, including a missing code_challenge.Verify the request parameters.
invalid_scopeRedirect addressAn invalid scope has been requested.Specify openid and the scopes of the modules permitted for the application.
unauthorized_clientRedirect address; token issue, HTTP 400The application is not permitted to obtain a code or a token in the specified way.Verify the response_type and grant_type values.
temporarily_unavailableRedirect addressThe authorization server is temporarily unavailable.Repeat sign-in later.
server_errorRedirect address; token issueAn internal error of the authorization server.Repeat the request later.
invalid_clientToken issue, HTTP 401The 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_grantToken issue, HTTP 400The 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_typeToken issue, HTTP 400An 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.

OAuth scopes
SCOPETYPEPURPOSE
openidDefaultMandatory. Confirms sign-in under OpenID Connect and the issue of an id_token.
profileDefaultFull name, given name, family name and username of the student.
emailDefaultEmail address and the sign of its verification.
qairu-studentDefaultStudent number student_id.
qairu.alem.readModule ALEMLanguage models, embeddings, reranking and speech recognition of the Alem.Cloud platform
qairu.users.readModule USERSUser accounts and roles
qairu.files.readModule FILESFiles of the information system
qairu.learning.readModule LEARNINGLearning: courses, assignments, grades and competencies
qairu.academic.readModule ACADEMICEducational programs, curricula and the course catalog
qairu.scheduling.readModule SCHEDULINGClass schedule
qairu.skillmatch.readModule SKILLMATCHMatching of skills and professional requirements
qairu.clubs.readModule CLUBSStudent clubs and their events
qairu.projects.readModule PROJECTSStudent and research projects
qairu.ideas.readModule IDEASStudent ideas and complaints