Skip to Content

Advanced OAuth 2.0 API

Auris supports several advanced OAuth 2.0 and OpenID Connect extensions beyond the standard Authorization Code + PKCE flow. These capabilities are designed for enterprise and specialized authentication scenarios:

  • Device Authorization (RFC 8628) — for input-constrained devices like CLIs, smart TVs, and IoT
  • Token Exchange (RFC 8693) — for impersonation and delegation between services
  • DPoP (RFC 9449) — for sender-constrained tokens that are resistant to token theft
  • CIBA (Client-Initiated Backchannel Authentication) — for authentication initiated by a backend service without browser interaction
  • Adaptive MFA / Risk Assessment — for dynamic step-up authentication based on risk scoring

All advanced OAuth features must be enabled per-application in the Auris Console under Applications > [App] > Advanced OAuth.

Device Authorization (RFC 8628)

The Device Authorization Grant allows devices that cannot display a browser (CLIs, smart TVs, IoT devices, kiosks) to authenticate users. The device displays a short user code and a verification URL; the user visits the URL on a separate device (phone, laptop) and enters the code to approve.

POST/api/oauth/device

Request a device code and user code pair. The device displays the user_code and verification_uri to the user, then polls the token endpoint until the user approves or the code expires.

Request body

{ "client_id": "cli-app-client-id", "scope": "openid profile email" }
FieldRequiredDescription
client_idYesThe application’s Client ID. The application must have Device Flow enabled.
scopeNoSpace-separated list of requested scopes.

Success response

{ "ok": true, "data": { "device_code": "GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS", "user_code": "WDJB-MJHT", "verification_uri": "https://api.altovar.net/hosted/device", "verification_uri_complete": "https://api.altovar.net/hosted/device?user_code=WDJB-MJHT", "expires_in": 1800, "interval": 5 } }
FieldDescription
device_codeA long, opaque code used by the device to poll the token endpoint. Never shown to the user. Stored as a SHA-256 hash on the server.
user_codeA short, human-readable 8-character code (format: XXXX-XXXX) displayed to the user.
verification_uriThe URL the user visits on a separate device to enter the code.
verification_uri_completeThe URL with the code pre-filled. Useful for QR codes.
expires_inSeconds until the device code expires (default: 30 minutes).
intervalMinimum seconds between polling requests.

Error codes

CodeHTTPDescription
DEVICE_FLOW_DISABLED400Device Authorization is not enabled for this application
VALIDATION_ERROR400Missing client_id

Polling for Tokens

After displaying the user code, the device polls the token endpoint at the specified interval:

Request body

{ "grant_type": "urn:ietf:params:oauth:grant-type:device_code", "device_code": "GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS", "client_id": "cli-app-client-id" }

Pending response (user has not yet approved)

{ "ok": false, "error": { "code": "AUTHORIZATION_PENDING", "message": "The user has not yet approved the request. Continue polling." } }

Slow down response (polling too frequently)

{ "ok": false, "error": { "code": "SLOW_DOWN", "message": "Polling too frequently. Increase interval by 5 seconds." } }

Success response (user approved)

{ "ok": true, "data": { "accessToken": "eyJhbGciOiJSUzI1NiJ9...", "refreshToken": "rt_...", "expiresIn": 900, "tokenType": "Bearer" } }

Error codes during polling

CodeHTTPDescription
AUTHORIZATION_PENDING400User has not yet approved — continue polling
SLOW_DOWN400Polling too fast — increase interval
EXPIRED_TOKEN400The device code has expired. Start a new flow.
ACCESS_DENIED400The user denied the authorization request

The public verification page at verification_uri shows the hosted login form pre-scoped to device approval. The user enters the code, authenticates (password, SSO, magic link, etc.), and approves. The device’s next poll returns tokens.

User Verification Page

Auris provides a hosted verification page at /hosted/device where the user:

  1. Enters the 8-character user code (or arrives via verification_uri_complete with the code pre-filled)
  2. Authenticates using any configured method (password, SSO, magic link, 2FA)
  3. Approves the device authorization request
  4. Sees a confirmation screen and can close the tab

Token Exchange (RFC 8693)

Token Exchange allows a service to exchange one token for another — either to impersonate a user (act as them) or to delegate access (act on behalf of them while retaining the original subject). This is essential for microservice architectures where an API gateway needs to call downstream services with different token scopes.

POST/api/auth/tokenRequires: impersonate:users or delegate:tokens

Exchange a token using the Token Exchange grant type. The caller must present a valid subject token and specify the exchange type.

Request body — Impersonation

Impersonation replaces the subject entirely. The resulting token has the target user as the sub claim. Use this when an admin needs to act as a user for debugging.

{ "grant_type": "urn:ietf:params:oauth:grant-type:token-exchange", "subject_token": "eyJhbGciOiJSUzI1NiJ9...", "subject_token_type": "urn:ietf:params:oauth:token-type:access_token", "requested_token_type": "urn:ietf:params:oauth:token-type:access_token", "exchange_type": "impersonation", "target_user_id": "usr_target123" }

Request body — Delegation

Delegation preserves the original subject and adds an act (actor) claim to the resulting token. The downstream service can see both who the token is for and who is acting on their behalf.

{ "grant_type": "urn:ietf:params:oauth:grant-type:token-exchange", "subject_token": "eyJhbGciOiJSUzI1NiJ9...", "subject_token_type": "urn:ietf:params:oauth:token-type:access_token", "requested_token_type": "urn:ietf:params:oauth:token-type:access_token", "exchange_type": "delegation" }
FieldRequiredDescription
grant_typeYesMust be urn:ietf:params:oauth:grant-type:token-exchange
subject_tokenYesThe existing access token to exchange
subject_token_typeYesMust be urn:ietf:params:oauth:token-type:access_token
requested_token_typeYesThe desired output token type. Typically urn:ietf:params:oauth:token-type:access_token
exchange_typeYesEither impersonation or delegation
target_user_idYes*Required for impersonation — the user ID to impersonate
scopeNoSpace-separated scopes for the new token. Must be a subset of the original.

Success response — Impersonation

{ "ok": true, "data": { "accessToken": "eyJhbGciOiJSUzI1NiJ9...", "issuedTokenType": "urn:ietf:params:oauth:token-type:access_token", "tokenType": "Bearer", "expiresIn": 900 } }

The impersonated token’s JWT payload will have "sub": "usr_target123" with the impersonating user’s original identity recorded in audit logs.

Success response — Delegation

{ "ok": true, "data": { "accessToken": "eyJhbGciOiJSUzI1NiJ9...", "issuedTokenType": "urn:ietf:params:oauth:token-type:access_token", "tokenType": "Bearer", "expiresIn": 900 } }

The delegated token’s JWT payload includes an act claim:

{ "sub": "usr_original_user", "act": { "sub": "usr_acting_service" }, "scope": "read:users" }

Error codes

CodeHTTPDescription
TOKEN_EXCHANGE_DISABLED400Token Exchange is not enabled for this application
INVALID_SUBJECT_TOKEN400The subject token is invalid or expired
TARGET_USER_NOT_FOUND404The target_user_id does not exist
PERMISSION_DENIED403Caller lacks impersonate:users or delegate:tokens permission
SCOPE_EXCEEDS_ORIGINAL400Requested scope is not a subset of the original token’s scope

Impersonation is a highly privileged operation. The impersonate:users permission should be restricted to admin roles and M2M service accounts that require it. All impersonation events are recorded in the audit log with both the actor and the impersonated user.

DPoP — Sender-Constrained Tokens (RFC 9449)

Demonstration of Proof-of-Possession (DPoP) binds tokens to a specific client by requiring a cryptographic proof with each request. Even if a DPoP-bound token is stolen, it cannot be used without the corresponding private key.

How DPoP Works

  1. The client generates a key pair (typically EC P-256 or RSA) and keeps the private key secure.
  2. On each request, the client creates a signed DPoP proof JWT containing the HTTP method, URL, and a unique jti.
  3. The server validates the proof, extracts the JWK thumbprint, and binds the issued token to that key.
  4. Subsequent API calls must include both the DPoP token and a fresh DPoP proof signed with the same key.

Requesting DPoP-Bound Tokens

Include a DPoP header with any token request (login, refresh, authorization code exchange):

POST /api/auth/token HTTP/1.1 Content-Type: application/json DPoP: eyJhbGciOiJFUzI1NiIsInR5cCI6ImRwb3Arand0IiwiandrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiLi4uIiwieSI6Ii4uLiJ9fQ.eyJodG0iOiJQT1NUIiwiaHR1IjoiaHR0cHM6Ly95b3VyLWF1cmlzLWRvbWFpbi5jb20vYXBpL2F1dGgvdG9rZW4iLCJpYXQiOjE3MDg1MjEyMDAsImp0aSI6InVuaXF1ZS1pZC0xMjMifQ.signature

DPoP proof JWT structure

Header:

{ "alg": "ES256", "typ": "dpop+jwt", "jwk": { "kty": "EC", "crv": "P-256", "x": "...", "y": "..." } }

Payload:

{ "htm": "POST", "htu": "https://api.altovar.net/api/auth/token", "iat": 1708521200, "jti": "unique-id-123", "nonce": "server-provided-nonce" }
FieldDescription
htmThe HTTP method of the request (POST, GET, etc.)
htuThe HTTP URI of the request (without query parameters)
iatIssued at timestamp. Must be within a short window (typically 60 seconds).
jtiA unique identifier to prevent replay attacks
nonceServer-provided nonce (included if the server returned a DPoP-Nonce header)

Token response with DPoP

When a DPoP proof is included, the response token is DPoP-bound:

{ "ok": true, "data": { "accessToken": "eyJhbGciOiJSUzI1NiJ9...", "tokenType": "DPoP", "expiresIn": 900 } }

Note that tokenType is "DPoP" instead of "Bearer". The access token’s JWT contains a cnf (confirmation) claim with the JWK thumbprint:

{ "sub": "usr_abc123", "cnf": { "jkt": "JWK_THUMBPRINT_BASE64URL" } }

Nonce Management

The server may require a nonce for replay protection. When it does, the response includes:

DPoP-Nonce: eyJhbGciOiJIUzI1NiJ9...

Include this nonce in subsequent DPoP proofs. If a request arrives without a required nonce, the server returns 400 with a use_dpop_nonce error and a fresh nonce in the header.

Error codes

CodeHTTPDescription
INVALID_DPOP_PROOF400DPoP proof JWT is malformed, expired, or has an invalid signature
DPOP_NONCE_REQUIRED400Server requires a nonce — retry with the nonce from the DPoP-Nonce response header
DPOP_JKT_MISMATCH401The DPoP proof was signed with a different key than the one the token is bound to
DPOP_DISABLED400DPoP is not enabled for this application

CIBA — Client-Initiated Backchannel Authentication

CIBA allows a backend service to initiate authentication for a user without requiring the user to interact with a browser redirect. Instead, the user receives a notification (push, SMS, or email) and approves the request on their device.

This is useful for scenarios like call center authentication (“I’m calling from your bank, please approve the login on your phone”) or point-of-sale transactions.

POST/api/oauth/backchannel/authorizeRequires: manage:ciba_config

Initiate a CIBA authentication request. The server sends a notification to the identified user. The calling service then polls (or receives a callback) to obtain tokens once the user approves.

Request body

{ "client_id": "backend-service-id", "client_secret": "backend-service-secret", "scope": "openid profile", "login_hint": "[email protected]", "binding_message": "Approve login for Order #12345", "requested_expiry": 300 }
FieldRequiredDescription
client_idYesThe M2M application’s Client ID
client_secretYesThe M2M application’s Client Secret
scopeNoRequested scopes
login_hintYesEmail address or user ID identifying the user to authenticate
binding_messageNoHuman-readable message displayed to the user in the notification (max 256 chars)
requested_expiryNoSeconds until the request expires (default: 300, max: 600)

Success response

{ "ok": true, "data": { "auth_req_id": "ciba_req_abc123def456", "expires_in": 300, "interval": 5 } }

Notification Modes

CIBA supports three notification delivery modes, configured per-application:

ModeDescription
pollThe service polls the token endpoint using auth_req_id. Default mode.
pingAuris sends an HTTP callback to the application’s registered notification_endpoint when the user responds. The service then calls the token endpoint.
pushAuris sends the tokens directly to the notification_endpoint in the callback payload. No polling needed.

Polling for CIBA Tokens

For poll mode, the service polls the token endpoint:

{ "grant_type": "urn:openid:params:grant-type:ciba", "auth_req_id": "ciba_req_abc123def456", "client_id": "backend-service-id", "client_secret": "backend-service-secret" }

The polling responses follow the same pattern as Device Authorization:

  • AUTHORIZATION_PENDING while waiting for user approval
  • SLOW_DOWN if polling too frequently
  • EXPIRED_TOKEN if the request timed out
  • ACCESS_DENIED if the user rejected the request
  • Full token response on approval

Error codes

CodeHTTPDescription
CIBA_DISABLED400CIBA is not enabled for this application
USER_NOT_FOUND404login_hint does not match any user
NOTIFICATION_FAILED500Failed to deliver the authentication notification to the user
BINDING_MESSAGE_TOO_LONG400binding_message exceeds 256 characters

Risk Assessment and Adaptive MFA

Auris performs automatic risk assessment on every authentication attempt. The risk score is computed from five weighted factors and determines whether additional authentication steps (step-up MFA) are required.

Risk Scoring Factors

FactorWeightDescription
IP Reputation20%Known malicious IPs, VPNs, proxies, data centers
Device Trust20%Whether the device fingerprint has been seen before
Geo Anomaly20%Impossible travel detection (login from a distant location too quickly)
Behavior20%Unusual login patterns (time of day, frequency)
Action Sensitivity20%How sensitive the requested action is

Risk levels: LOW (0-30), MEDIUM (31-60), HIGH (61-80), CRITICAL (81-100).

When the risk score exceeds configured thresholds, Auris automatically challenges the user with step-up MFA before issuing tokens. The acr (Authentication Context Class Reference) and amr (Authentication Methods References) claims in the JWT reflect the actual authentication level achieved.

GET/api/auth/risk/assessmentsRequires: view:security

List recent risk assessments across the tenant. Useful for monitoring suspicious login patterns and reviewing risk scoring decisions.

Query parameters

ParameterTypeDescription
pageintegerPage number (default: 1)
limitintegerItems per page (default: 20)
userIdstringFilter by user ID
levelLOW | MEDIUM | HIGH | CRITICALFilter by risk level
dateFromISO 8601Start of date range
dateToISO 8601End of date range

Success response

{ "ok": true, "data": { "data": [ { "id": "risk_abc123", "userId": "usr_def456", "score": 72, "level": "HIGH", "factors": { "ipReputation": { "score": 85, "isVpn": true, "isProxy": false }, "deviceTrust": { "score": 50, "isNewDevice": true }, "geoAnomaly": { "score": 90, "distance": 5200, "timeSinceLastLogin": 1800 }, "behavior": { "score": 60, "unusualTime": true }, "actionSensitivity": { "score": 75 } }, "actionTaken": "step_up_mfa", "ipAddress": "203.0.113.42", "country": "CN", "city": "Beijing", "createdAt": "2025-02-18T03:45:00Z" } ], "pagination": { "page": 1, "limit": 20, "total": 156, "totalPages": 8 } } }
GET/api/auth/risk/rulesRequires: view:security

List all custom risk rules configured for the tenant. Risk rules allow overriding or augmenting the default scoring for specific conditions.

Success response

{ "ok": true, "data": [ { "id": "rule_abc123", "name": "Block known VPN ranges", "condition": { "field": "ipReputation.isVpn", "operator": "equals", "value": true }, "action": "block", "scoreModifier": 40, "isActive": true, "createdAt": "2025-01-15T10:00:00Z" } ] }
POST/api/auth/risk/rulesRequires: manage:security

Create a custom risk rule. Rules are evaluated during login and can modify the risk score, require additional authentication, or block the login entirely.

Request body

{ "name": "Require MFA for new countries", "condition": { "field": "geoAnomaly.isNewCountry", "operator": "equals", "value": true }, "action": "step_up_mfa", "scoreModifier": 30, "isActive": true }
FieldRequiredDescription
nameYesHuman-readable name for the rule
conditionYesCondition object with field, operator, and value
actionYesOne of: allow, step_up_mfa, block, log
scoreModifierNoPoints to add to the risk score when the condition matches (0-100)
isActiveNoWhether the rule is active (default: true)

Available condition fields: ipReputation.isVpn, ipReputation.isProxy, ipReputation.isDatacenter, deviceTrust.isNewDevice, geoAnomaly.isNewCountry, geoAnomaly.distance, behavior.unusualTime, behavior.failedAttempts.

Available operators: equals, not_equals, greater_than, less_than, contains, in.

Success response

{ "ok": true, "data": { "id": "rule_def456", "name": "Require MFA for new countries", "condition": { "field": "geoAnomaly.isNewCountry", "operator": "equals", "value": true }, "action": "step_up_mfa", "scoreModifier": 30, "isActive": true, "createdAt": "2025-02-18T10:00:00Z" } }
PUT/api/auth/risk/rules/[id]Requires: manage:risk_rules

Update a risk rule’s name, condition, action, score modifier, or active state.

Request body

{ "name": "Require MFA for new countries (updated)", "scoreModifier": 50, "isActive": true }
DELETE/api/auth/risk/rules/[id]Requires: manage:security

Delete a risk rule. Takes effect immediately on the next login attempt.

Success response

{ "ok": true, "data": { "deleted": true } }

ACR and AMR in Tokens

When adaptive MFA triggers step-up authentication, the issued JWT includes acr and amr claims that downstream services can use to verify the authentication strength:

{ "sub": "usr_abc123", "acr": "urn:auris:acr:mfa", "amr": ["pwd", "otp"], "iat": 1708521200, "exp": 1708522100 }
ClaimDescription
acrAuthentication Context Class Reference. Values: urn:auris:acr:pwd (password only), urn:auris:acr:mfa (password + second factor), urn:auris:acr:strong (password + strong second factor like WebAuthn)
amrAuthentication Methods References. Array of methods used: pwd, otp (TOTP), sms, webauthn, social, magic_link, sso

Resource servers can require a minimum acr level for sensitive operations by checking the JWT claims before processing the request.

Permissions Reference

PermissionDescription
manage:device_codesManage Device Authorization Flow configuration
impersonate:usersExchange tokens for impersonation (act as another user)
delegate:tokensExchange tokens for delegation (act on behalf of another user)
manage:dpop_configConfigure DPoP settings for applications
manage:ciba_configConfigure CIBA settings and notification endpoints
view:risk_assessmentsView risk assessment logs and scoring details
manage:risk_rulesCreate, update, and delete custom risk rules
manage:advanced_oauthFull access to all advanced OAuth configuration

All advanced OAuth features require explicit enablement on each application. The application’s settings in the Auris Console include toggle switches for Device Flow, Token Exchange, DPoP, and CIBA. The corresponding enable* flags on the Application model control availability.