Skip to content

KRDPass Integration Guide

Version 1.1 — December 2025


1. Introduction

KRDPass enables secure, passwordless citizen authentication for digital services within the Kurdistan Region. Authentication is performed exclusively through the Central Authentication Service (CAS) of the DIT using OpenID Connect (OIDC).

Your application never handles passwords. CAS handles all authentication through the KRDPass mobile app using a QR-code approval flow. Your backend then receives an OIDC authorization code and exchanges it securely for tokens.

This guide outlines the correct and compliant integration pattern.


2. How KRDPass Login Works (High-Level)

  1. User tries to access a protected page.
  2. Your backend redirects them to CAS via OIDC.
  3. CAS displays:
    • a QR code
    • a numeric approval code
  4. User opens KRDPass mobile app → approves login.
  5. CAS sends the user back to your application with an authorization code.
  6. Your backend exchanges this code for tokens.
  7. Backend creates a secure session via HttpOnly cookies.
  8. User is authenticated inside your system.

3. Required OIDC Configuration

SettingDescriptionExample
AuthorityCAS issuerhttps://account.id.krd
ClientIdApplication IDyour-client-id
ClientSecretApplication secretyour-client-secret
CallbackPathRedirect URI path/auth/callback

Mandatory parameters

response_type=code  
scope=openid profile  
acr_values=idp:urn:krg:identity:citizen

The acr_values ensure CAS uses the KRDPass Citizen Identity Provider.


4. Identity Claims Returned by CAS

Example claims:

{
  "iss": "https://account.id.krd",
  "sub": "77ad7707bb007377cb07377",
  "upn": "5010219103805",
  "upns": ["5010219103805"],
  "did": "8D00D48F-6F97-436D-954B-F14709DCE3AC",
  "dpv": 1
}

Identity Handling Requirements (MANDATORY)

1. sub (primary key)

  • Must be stored
  • Must be used as your internal primary key
  • Must never be displayed in UI, logs, receipts, or communications
  • Used only for identity mapping and session association

2. upn (citizen identifier)

  • Must be stored
  • Must be displayed in all citizen-facing or administrative interfaces
  • Used as the primary searchable citizen reference across all systems

3. upns (historical UPNs)

  • Must be stored
  • Must never be displayed
  • Used for audit, data reconciliation, and recovery scenarios

Identity Summary Table

ClaimStore?Display?Purpose
sub✔ Yes❌ NoInternal PK
upn✔ Yes✔ YesCitizen-facing identifier
upns✔ Yes❌ NoHistorical audit & search

5. Security Architecture Requirements

KRDPass requires a high-security OIDC integration model.

5.1 Tokens Must Never Be Accessible to the Frontend

Prohibited:

  • ID token in JavaScript
  • Access tokens in localStorage/sessionStorage
  • Authorization code in browser memory
  • Tokens in cookies

All tokens must remain backend-only.

5.2 Authorization Code Must Never Reach the Browser

Authorization codes may only be handled in the backend.

5.3 Sessions Must Be HttpOnly

Your session cookies must be:

  • HttpOnly
  • Secure
  • SameSite=Lax
  • Rotated on login

6. Mandatory Architecture: Backend-for-Frontend (BFF)

All new systems integrating with KRDPass must use a BFF pattern.

A BFF:

  • Performs the OIDC flow entirely server-side
  • Exchanges the authorization code for tokens
  • Stores tokens securely server-side
  • Issues only safe cookies to the browser
  • Exposes minimal endpoints:
    • POST /session/login
    • POST /session/logout
    • GET /session/me (basic profile only)

The SPA (React/Vue/Angular/etc.) must never:

  • Communicate with CAS directly
  • Store tokens
  • Receive tokens
  • Perform OIDC itself

7. DIT-Provided Standard BFF (Important)

For services hosted inside DIT infrastructure:

DIT provides an official, hardened BFF component.

It:

  • Implements the OIDC Authorization Code Flow correctly
  • Stores tokens securely
  • Issues safe session cookies
  • Prevents any token exposure
  • Ensures full compliance with KRDPASS authentication policy

How to obtain it:

👉 Contact the Head of DevOps at DIT. You’ll receive:

  • Containerized BFF component (Docker image / Helm chart)
  • Deployment instructions
  • Integration templates
  • Routing and environment configuration support

This is the recommended and compliant method for all DIT-hosted platforms.


8. COMPLIANCE REQUIREMENTS

The following rules are mandatory for any system integrating with KRDPass.

8.1 Authentication Implementation

  • Must use standard OpenID Connect Authorization Code Flow
  • Must include acr_values=idp:urn:krg:identity:citizen
  • Do NOT use the OIDC Implicit Flow, Hybrid Flow, or SPA-only flows
  • Do NOT build your own OAuth/OIDC implementation
  • Must use a standard, battle-tested OIDC/OAuth2 library for your stack
  • Example libraries:
    • .NET: Microsoft.Owin / ASP.NET OIDC Middleware
    • Node.js: openid-client
    • PHP: jumbojett/openid-connect-php
    • Ruby: omniauth-openid-connect
    • Java: Spring Security OAuth2 Client
    • Go: coreos/go-oidc
  • No custom token parsing, signature verification, or OIDC protocol reimplementation

Reinventing OIDC or OAuth2 is explicitly prohibited. This ensures:

  • correct security
  • protocol compliance
  • maintainability
  • interoperability and correctness during audits

8.2 Identity Handling

  • Store sub as internal primary key
  • Store upn as external citizen identifier
  • Store upns for audit
  • Never display sub or upns

8.3 Token and Session Security

  • All tokens processed server-side only
  • No token storage in front-end code
  • No token exposure in logs
  • Session cookies must be HttpOnly, Secure, SameSite=Strict

8.4 Architecture

  • Must use a BFF architecture
  • SPA must communicate only with BFF, not CAS
  • Must use DIT’s official BFF if hosted in DIT infrastructure

8.5 Validation

  • Validate iss=https://account.id.krd
  • Validate aud (client_id)
  • Validate expiry, nonce, and state
  • Reject authentication from unknown or mismatched clients

9. Backend Integration Examples

Below are examples using widely adopted, compliant OIDC libraries.


9.1 .NET (ASP.NET Core)

services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = "oidc";
})
.AddCookie(options =>
{
    options.Cookie.HttpOnly = true;
    options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
    options.Cookie.SameSite = SameSiteMode.Strict;
})
.AddOpenIdConnect("oidc", options =>
{
    options.Authority = "https://account.id.krd";
    options.ClientId = config["KRDPass:ClientId"];
    options.ClientSecret = config["KRDPass:ClientSecret"];
    options.CallbackPath = "/auth/callback";

    options.ResponseType = "code";
    options.Scope.Clear();
    options.Scope.Add("openid");
    options.Scope.Add("profile");

    options.SaveTokens = false;

    options.Events.OnRedirectToIdentityProvider = ctx =>
    {
        ctx.ProtocolMessage.AcrValues = "idp:urn:krg:identity:citizen";
        return Task.CompletedTask;
    };
});

9.2 PHP (openid-connect-php)

$oidc = new OpenIDConnectClient(
    'https://account.id.krd',
    getenv('KRD_CLIENT_ID'),
    getenv('KRD_CLIENT_SECRET')
);

$oidc->setRedirectURL('https://your-app.com/auth/callback');
$oidc->addScope('openid');
$oidc->addScope('profile');

$oidc->addAuthParam('acr_values', 'idp:urn:krg:identity:citizen');

$oidc->authenticate();

$sub = $oidc->requestUserInfo('sub');
$upn = $oidc->requestUserInfo('upn');
$upns = $oidc->requestUserInfo('upns');

store_identity($sub, $upn, $upns);

9.3 Ruby (OmniAuth)

provider :openid_connect, {
  name: :krdpass,
  scope: [:openid, :profile],
  response_type: :code,
  issuer: 'https://account.id.krd',
  discovery: true,
  client_options: {
    identifier: ENV['KRD_CLIENT_ID'],
    secret: ENV['KRD_CLIENT_SECRET'],
    redirect_uri: 'https://your-app.com/auth/krdpass/callback'
  },
  authorize_params: {
    acr_values: 'idp:urn:krg:identity:citizen'
  }
}

9.4 Node.js (openid-client)

const authUrl = client.authorizationUrl({
  scope: 'openid profile',
  state,
  nonce,
  acr_values: 'idp:urn:krg:identity:citizen'
});

Callback:

const tokenSet = await client.callback(redirectUri, params);

const user = await client.userinfo(tokenSet.access_token);

saveIdentity({
  sub: user.sub,
  upn: user.upn,
  upns: user.upns
});

9.5 Java (Spring Security)

spring:
  security:
    oauth2:
      client:
        registration:
          krdpass:
            client-id: ${KRD_CLIENT_ID}
            client-secret: ${KRD_CLIENT_SECRET}
            scope: openid,profile
            authorization-grant-type: authorization_code
            redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
        provider:
          krdpass:
            issuer-uri: https://account.id.krd

Add acr_values:

return OAuth2AuthorizationRequest.from(req)
  .additionalParameters(params -> params.put("acr_values", "idp:urn:krg:identity:citizen"))
  .build();