Identity Service — API Contracts
Status: populated Owner: TBD Last updated: 2026-04-17 Companion: Service Template · 05 API Design · API Path Conventions
Base URL: https://api.ghasi-ehealth.{tld}
Internal base URL: http://identity-service:3000/internal/identity
Auth: Bearer JWT issued by identity-service (RS256 from KMS-backed JWKS) except on anonymous endpoints marked anon.
Error envelope: { error: ERROR_CODE, message, requestId, timestamp } — see ERROR_CODES.md.
1. Authentication (anon / auth-emitting)
| Method | Path | Auth | Summary | Scopes / Gates |
|---|---|---|---|---|
| POST | /api/v1/auth/login | anon | Password login; returns {access, refresh} or {challengeId} for MFA | Rate limit 5/min/IP |
| POST | /api/v1/auth/mfa/verify | challenge-token | Complete MFA during login | — |
| POST | /api/v1/auth/refresh | refresh cookie | Rotate refresh + mint new access | One-time-use |
| POST | /api/v1/auth/logout | JWT | Revoke current session | — |
| POST | /api/v1/auth/logout-all | JWT | Revoke all sessions for caller | — |
| POST | /api/v1/auth/password-reset/request | anon | Trigger reset email | Rate limit 3/h/email |
| POST | /api/v1/auth/password-reset/confirm | reset-token | Confirm new password | Breach-list check |
| POST | /api/v1/auth/register | anon | Public self-registration (patient portal tenants only) | Captcha |
| POST | /api/v1/auth/verify-email | verify-token | Confirm email, activates user | — |
| GET | /.well-known/jwks.json | anon | JWKS for JWT verification | cached 1h |
| GET | /.well-known/openid-configuration | anon | OIDC discovery document | — |
Example request — POST /api/v1/auth/login
{ "email": "dr.ahmed@kch.af", "password": "...", "deviceFingerprint": "sha256:..." }
200 OK — no MFA
{
"accessToken": "eyJhbGc...",
"refreshToken": "eyJhbGc...",
"accessExpiresIn": 900,
"refreshExpiresIn": 28800,
"user": { "id": "usr_01H...", "tenantId": "ten_01H...", "email": "dr.ahmed@kch.af" }
}
401 MFA_CHALLENGE
{ "error": "IDENT_MFA_REQUIRED", "challengeId": "chl_01H...", "factors": ["totp","webauthn"] }
2. Federated identity
| Method | Path | Auth | Summary |
|---|---|---|---|
| GET | /api/v1/auth/oidc/:provider/start | anon | Begin OIDC Authorization Code + PKCE |
| GET | /api/v1/auth/oidc/:provider/callback | anon | OIDC callback; JIT link/provision |
| POST | /api/v1/auth/saml/:provider/login | anon | Initiate SAML redirect |
| POST | /api/v1/auth/saml/:provider/acs | SAML | Assertion Consumer Service endpoint |
| POST | /api/v1/auth/saml/:provider/slo | SAML | Single Logout |
| POST | /api/v1/auth/keycloak/token-exchange | upstream JWT | RFC 8693 token exchange (broker mode) |
Errors: IDENT_FEDERATION_UNAVAILABLE (503), IDENT_EXT_IDENTITY_MISMATCH (409), IDENT_ALLOWLIST_VIOLATION (403).
3. Self profile & access context
| Method | Path | Auth | Summary |
|---|---|---|---|
| GET | /api/v1/me | JWT | Own profile |
| PATCH | /api/v1/me | JWT | Update firstName, lastName, locale, timezone |
| GET | /api/v1/me/access-context | JWT | Aggregated access context {roles, memberships, effectiveModules} |
| GET | /api/v1/me/sessions | JWT | Active sessions |
| DELETE | /api/v1/me/sessions/:id | JWT | Revoke a specific session |
4. User management (admin)
| Method | Path | Scope | Summary |
|---|---|---|---|
| GET | /api/v1/users | iam.users:read | List users in tenant (paginated) |
| POST | /api/v1/users | iam.users:create | Create user |
| GET | /api/v1/users/:id | iam.users:read | Get user |
| PATCH | /api/v1/users/:id | iam.users:update | Update |
| POST | /api/v1/users/:id/suspend | iam.users:suspend | Suspend |
| POST | /api/v1/users/:id/reactivate | iam.users:suspend | Reactivate |
| DELETE | /api/v1/users/:id | iam.users:delete | Soft-delete (deactivated) |
Pagination: ?page=1&pageSize=50 (max 100). Filters: status, search, backend.
5. MFA & devices
| Method | Path | Summary |
|---|---|---|
| POST | /api/v1/me/mfa/totp/enroll | Begin TOTP enrollment, returns provisioning URI |
| POST | /api/v1/me/mfa/totp/confirm | Verify TOTP code, activates factor |
| POST | /api/v1/me/mfa/webauthn/register/options | Create registration options |
| POST | /api/v1/me/mfa/webauthn/register | Finalize WebAuthn registration |
| DELETE | /api/v1/me/mfa/:factorId | Remove factor |
| GET | /api/v1/me/devices | List own devices |
| POST | /api/v1/me/devices | Register new device |
| POST | /api/v1/me/devices/:id/bind-offline | Issue offline binding cert |
| DELETE | /api/v1/me/devices/:id | Revoke device |
6. API keys & service accounts
| Method | Path | Scope | Summary |
|---|---|---|---|
| GET | /api/v1/api-keys | iam.apikeys:read | List API keys (no secrets) |
| POST | /api/v1/api-keys | iam.apikeys:create | Create key (secret returned once) |
| POST | /api/v1/api-keys/:id/rotate | iam.apikeys:rotate | Rotate with overlap window |
| DELETE | /api/v1/api-keys/:id | iam.apikeys:revoke | Revoke |
| GET | /api/v1/service-accounts | SUPER_ADMIN | List |
| POST | /api/v1/service-accounts | SUPER_ADMIN | Create (client credentials) |
| POST | /api/v1/service-accounts/:id/rotate | SUPER_ADMIN | Rotate secret |
| DELETE | /api/v1/service-accounts/:id | SUPER_ADMIN | Revoke |
7. Licensing (module catalogue)
| Method | Path | Scope | Summary |
|---|---|---|---|
| GET | /api/v1/licensing/modules | JWT | Module catalogue |
| POST | /api/v1/licensing/modules | SUPER_ADMIN | Create module |
| PATCH | /api/v1/licensing/modules/:code | SUPER_ADMIN | Update description / dependencies |
8. Licensing (assignments)
| Method | Path | Scope | Summary |
|---|---|---|---|
| POST | /api/v1/licensing/assignments | SUPER_ADMIN | Assign license to node |
| GET | /api/v1/licensing/nodes/:nodeId/assignments | SUPER_ADMIN or TENANT_ADMIN | Direct assignments at node |
| GET | /api/v1/licensing/nodes/:nodeId/effective | JWT | Effective set at node (inheritance walk) |
| PATCH | /api/v1/licensing/assignments/:id/status | SUPER_ADMIN | Change status |
| PATCH | /api/v1/licensing/assignments/:id/constraints | SUPER_ADMIN | Update seats/expiry |
| GET | /api/v1/licensing/assignments/:id/history | SUPER_ADMIN | Change history |
| POST | /api/v1/licensing/bundles/:bundleCode/apply | SUPER_ADMIN | Apply bundle (transactional) |
Error responses: IDENT_LIC_DEPENDENCY_NOT_LICENSED 422, IDENT_LIC_MIN_NODE_TYPE 422, IDENT_LIC_ALWAYS_ON_IMMUTABLE 422, IDENT_LIC_TERMINATED 422, IDENT_LIC_DUPLICATE 409.
9. Internal (cluster-only, no JWT; ip-restricted)
| Method | Path | Summary |
|---|---|---|
| GET | /internal/identity/users/:id | Lightweight user resolve |
| GET | /internal/identity/users/by-external/:issuer/:subject | External identity lookup |
| GET | /internal/identity/providers/:id/identity | Identity package consumed by tenant-service evaluate |
| GET | /internal/identity/service-accounts/:clientId | Active-status probe |
| GET | /internal/identity/licensing/effective | ?tenantId=&providerId=&nodeId= effective set |
| GET | /internal/identity/licensing/nodes/:nodeId | Direct assignments (no walk) |
| POST | /internal/identity/tokens/introspect | OAuth 2.0 introspection for edge |
10. Pagination & idempotency
- Pagination: offset
page/pageSize(max 100) or cursorcursor/limit(assignments history uses cursor). - Idempotency:
Idempotency-Keyheader required on mutating/licensing/assignmentsand/usersPOSTs. 24h window.IDENT_IDEMPOTENCY_MISMATCH409. - Request id:
X-Request-Idechoed in response and all logs. - Correlation:
Traceparentpropagated per OpenTelemetry.
11. Rate limits (Kong)
| Surface | Limit |
|---|---|
/api/v1/auth/* anon | 30/min/IP (login 5/min) |
/api/v1/users POST | 60/min/tenant |
/api/v1/licensing/* mutating | 60/min/tenant |
/api/v1/me/* | 300/min/user |
12. Error code catalogue (identity)
| Code | HTTP | Meaning |
|---|---|---|
IDENT_USER_NOT_FOUND | 404 | User not in tenant |
IDENT_EMAIL_DUPLICATE | 409 | Email exists in tenant |
IDENT_USER_SUSPENDED | 422 | Action invalid for suspended user |
IDENT_USER_DEACTIVATED | 422 | Action invalid for deactivated user |
IDENT_MFA_REQUIRED | 401 | MFA challenge needed |
IDENT_MFA_FAILED | 401 | Invalid TOTP/WebAuthn |
IDENT_REFRESH_REPLAY | 401 | Refresh token reuse detected |
IDENT_INVALID_CREDENTIALS | 401 | Password or OIDC assertion invalid |
IDENT_CROSS_TENANT | 403 | Attempt to touch foreign tenant |
IDENT_FEDERATION_UNAVAILABLE | 503 | Upstream IdP circuit open |
IDENT_DEVICE_NOT_TRUSTED | 422 | Offline bind requires trusted device |
IDENT_LIC_DEPENDENCY_NOT_LICENSED | 422 | License chain incomplete |
IDENT_LIC_ALWAYS_ON_IMMUTABLE | 422 | Cannot terminate always-on module |
IDENT_LIC_TERMINATED | 422 | Terminal state |
IDENT_LIC_CROSS_TENANT | 403 | Node tenant mismatch |
IDENT_IDEMPOTENCY_MISMATCH | 409 | Same key different body |
See ERROR_CODES.md for shared platform errors.