From 00632f4c92171da65ad5b5cd2a46e6d2e5374c3d Mon Sep 17 00:00:00 2001 From: nmoskaleva Date: Fri, 10 Apr 2026 11:18:37 +0200 Subject: [PATCH 1/4] Move authorize request parameters to a dedicated reference page This section was previously hidden inside OIDC introduction. This PR improves the information architecture by moving it into a dedicated Reference page, separating the API specifications from "how it works" part. --- src/pages/signatures/graphql/examples.mdx | 6 +- .../verify/e-ids/finnish-trust-network.mdx | 6 +- src/pages/verify/guides/appswitch.mdx | 2 +- .../guides/pushed-authorization-requests.mdx | 6 +- src/pages/verify/how-it-works/oidc-intro.mdx | 178 +------ .../verify/integrations/aspnet-core-v6.mdx | 2 +- src/pages/verify/integrations/javascript.mdx | 4 +- src/pages/verify/integrations/vuejs.mdx | 4 +- .../images/enable-dynamic-scopes.png | Bin 0 -> 36294 bytes .../verify/reference/request-parameters.mdx | 477 ++++++++++++++++++ 10 files changed, 494 insertions(+), 191 deletions(-) create mode 100644 src/pages/verify/reference/images/enable-dynamic-scopes.png create mode 100644 src/pages/verify/reference/request-parameters.mdx diff --git a/src/pages/signatures/graphql/examples.mdx b/src/pages/signatures/graphql/examples.mdx index 276e043b..d91ca58c 100644 --- a/src/pages/signatures/graphql/examples.mdx +++ b/src/pages/signatures/graphql/examples.mdx @@ -28,10 +28,10 @@ It is currently not possible to sign documents that have previously been signed, You can modify Idura Verify evidence provider settings to change the runtime behavior: -- To change authentication methods displayed, add [`acrValues`](/verify/getting-started/oidc-intro#authorize-request-parameters). +- To change authentication methods displayed, add [`acrValues`](/verify/reference/authorize-request-parameters/#acr_values). - To disable popups, set `"alwaysRedirect": true`. -- To specify the user data you need, add the [OpenID Connect `scope`](/verify/getting-started/oidc-intro/#the-scope-parameter). -- To add a message, use [`loginHint`](/verify/getting-started/oidc-intro/#the-loginhint-parameter). +- To specify the user data you need, add [`scope`](/verify/reference/authorize-request-parameters/#scope). +- To add a message, use [`loginHint`](/verify/reference/authorize-request-parameters/#login_hint). To experiment with different settings, see [Authorize URL builder](/verify/guides/authorize-url-builder/). diff --git a/src/pages/verify/e-ids/finnish-trust-network.mdx b/src/pages/verify/e-ids/finnish-trust-network.mdx index c7fa219c..9943bfd2 100644 --- a/src/pages/verify/e-ids/finnish-trust-network.mdx +++ b/src/pages/verify/e-ids/finnish-trust-network.mdx @@ -50,16 +50,16 @@ Please refer to the [Private Key JWT authentication guide](/verify/guides/privat ### Authentication request signing Your application must sign [authorization requests](/verify/reference/glossary/#authorization-request-authorize-url) to Idura and send them as JWT-secured Authorization Requests(JARs). -A JWT-secured Authorization Request is a method where the [request parameters](/verify/getting-started/oidc-intro/#authorize-request-parameters) are encapsulated in a [JSON Web Token (JWT)](/verify/reference/glossary/#json-web-token-jwt) signed by your application’s private key and included in the authorization request via the `request` parameter. +A JWT-secured Authorization Request is a method where the [request parameters](/verify/reference/authorize-request-parameters) are encapsulated in a [JSON Web Token (JWT)](/verify/reference/glossary/#json-web-token-jwt) signed by your application’s private key and included in the authorization request via the `request` parameter. Authentication request signing enhances security by guaranteeing that the request originated from your application and has not been tampered with. To understand the benefits in more detail, see: Why Signed Authorization Requests Elevate Your Security. #### Creating a signed authorization request -To create a signed authorization request, you first create a JWT where the payload contains your [request parameters](/verify/getting-started/oidc-intro/#authorize-request-parameters), plus appropriate `issuer` and `audience`. +To create a signed authorization request, you first create a JWT where the payload contains your request parameters, plus appropriate `issuer` and `audience`. The JWT is then signed using your application's private signing key and added to the authorization request via the `request` parameter. The private signing key used for [private key JWT client authentication](/verify/e-ids/finnish-trust-network/#private-key-jwt-client-authentication) can be used to sign authorization requests. -Your signed authorization request will look something like this: +If your [Idura domain](/verify/how-it-works/core-concepts/#domains) is `acme-corp.idura.broker`, your signed authorization request will look something like this: `https://acme-corp.idura.broker/oauth2/authorize?client_id=your_client_id&request=eyJhbGciOiJNilhcmxzZXYifQ.eyJpc3MiOiJ5b3VyX2Nsa9pZCI...` diff --git a/src/pages/verify/guides/appswitch.mdx b/src/pages/verify/guides/appswitch.mdx index 35d28ea6..1f2bae0d 100644 --- a/src/pages/verify/guides/appswitch.mdx +++ b/src/pages/verify/guides/appswitch.mdx @@ -24,7 +24,7 @@ You must augment the authorize request you send to Idura Verify so it contains o - `login_hint=appswitch:ios` - `login_hint=appswitch:android` -The value must be sent in the `login_hint` query parameter. Further details on this (and other) parameters in an authorize request can be found [here](/verify/getting-started/oidc-intro#authorize-request-parameters). +The value must be sent in the `login_hint` query parameter. For further details on this (and other) parameters in an authorize request, see [authorize request parameters](/verify/reference/authorize-request-parameters). Your app is responsible for sending the appropriate value for the platform it is deployed on. diff --git a/src/pages/verify/guides/pushed-authorization-requests.mdx b/src/pages/verify/guides/pushed-authorization-requests.mdx index 96a2015a..bdaf2728 100644 --- a/src/pages/verify/guides/pushed-authorization-requests.mdx +++ b/src/pages/verify/guides/pushed-authorization-requests.mdx @@ -9,8 +9,8 @@ title: Pushed Authorization Requests (PAR) Pushed Authorization Requests (PAR) is an OAuth 2.0 extension that allows sending [authorization request -parameters](/verify/how-it-works/oidc-intro/#authorize-request-parameters) to the authorization -server via a POST request, rather than in the browser's URL query string. +parameters](verify/reference/authorize-request-parameters) to the authorization server via a POST +request, rather than in the browser's URL query string. PAR enhances security by mitigating risks associated with exposing sensitive data in URLs. It also allows the authorization server to authenticate the client and validate the request early in the process (before any user interaction occurs), @@ -45,7 +45,7 @@ Send a POST request from your backend to the PAR endpoint. The PAR endpoint URL Your POST request should include: - The same parameters you would normally send in an authorization request (e.g. `response_type`, `scope`, `redirect_uri`, `state`). - For the complete list of authorization request parameters, see [Authorize Request Parameters](/verify/how-it-works/oidc-intro/#authorize-request-parameters). + For the complete list of authorization request parameters, see [authorize request parameters](/verify/reference/authorize-request-parameters/). - Client authentication credentials (for [confidential clients](/verify/reference/glossary/#confidential-client)): - either `client_id` and `client_secret` (included in the Basic Authorization header) or - `client_assertion` and `client_assertion_type` (included in the request body), when using [Private Key JWT](/verify/guides/privatekey-jwt/). diff --git a/src/pages/verify/how-it-works/oidc-intro.mdx b/src/pages/verify/how-it-works/oidc-intro.mdx index dbe9aea7..e8495ab3 100644 --- a/src/pages/verify/how-it-works/oidc-intro.mdx +++ b/src/pages/verify/how-it-works/oidc-intro.mdx @@ -73,102 +73,7 @@ GET https://YOUR_SUBDOMAIN.idura.broker/oauth2/authorize? Note that providing `response_type=code` specifies that you want either the traditional back-channel _Authorization Code Flow_ or the _PKCE Flow_. If you specify `response_type=id_token` you indicate that you want the _Implicit Flow_. In the Implicit Flow, you receive the issued token in a URL fragment on the return URL. -If you want to receive the response in another way, you must specify the `response_mode` parameter, see below. - - - -#### Parameters - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Parameter name Description
`response_type` - Denotes the kind of credential that Idura will return (`code` or `id_token`). If you are - integrating a traditional server-based web application (back-channel flow) or a - *PKCE-enabled* client, use `code`. Use `id_token` for legacy single page applications using - a front-channel flow.{' '} -
`client_id` - Your application's Client ID. You can find this value in the Idura Verify UI in the settings - for the actual application.{' '} -
`redirect_uri` - {' '} - The URL to which Idura will redirect the browser after authentication has been completed. - The authorization code and the `id_token` will be available in the `code` and `id_token` URL - parameters for the back-channel flow and on a URL query parameter for the front-channel - flow. This URL must be pre-registered as a valid callback URL in your application settings. -

**Warning:** Per the [OAuth 2.0 - Specification](https://tools.ietf.org/html/rfc6749#section-3.1.2), Idura removes everything - after the hash and does *not* honor any fragments.{' '} -
`scope` - For applications configured with a `static` scope strategy, specify `openid`. This gets you - the information configured in the management dashboard for each kind of eID (where - applicable).

For applications configured with a `dynamic` scope strategy, you - must specify scope tokens for the types of data you want, in addition to the `openid` scope - token. Possible values are described in the individual eID articles.
-
You can read more about this parameter [here](#the-scope-parameter). -
`acr_values` - Identifies which eID identity service you want to use. You can only specify one value, and - it must identify the exact type of identity service, as some countries have, for example, - both a mobile and a web-based service. Possible values can be found in the [authorize - request builder](/verify/guides/authorize-url-builder/#auth-methods--acr-values).{' '} -
`response_mode` - (optional) Specifies how you want your result delivered via the `redirect_uri`: Use `query` - to return the `code`/`id_token` as a query parameter, `fragment` to have it delivered on a - URL fragment, and finally `form_post` to have it posted back to your `redirect_uri`.
{' '} -
Default values are `query` for `response_type=code` and `fragment` for - `response_type=id_token`.{' '} -
`state` - (optional but recommended) An opaque arbitrary alphanumeric string your app adds to the - initial request that Idura includes when redirecting back to your application.{' '} -
`login_hint` - (optional) Various use cases leverage this parameter. You can read more about them - [here](#the-loginhint-parameter). -
`prompt` - (optional) Specifies whether the user will be forced to re-authenticate. Possible values are - `none`, `login`, `consent`, and `consent_revoke`. More information is available in our [SSO - guide](/verify/guides/sso/#per-request-options) and in our [authorize URL - builder](/verify/guides/authorize-url-builder/).{' '} -
+If you want to receive the response in another way, you must specify the `response_mode` parameter. When adding login to your application, you will build the authorization URL (also called the [_authorize URL_](/verify/guides/authorize-url-builder/)) and redirect the user to it. An HTML snippet for your authorize URL might look like this: @@ -202,7 +107,7 @@ After completing the steps specific to your chosen flow, you can proceed to [val A more secure alternative to traditional authorization requests is [Pushed Authorization Requests (PAR)](/verify/guides/pushed-authorization-requests). -PAR lets you initiate the authentication flow by sending [authorization request parameters](/verify/how-it-works/oidc-intro/#authorize-request-parameters) directly to the authorization server via a POST request. +PAR lets you initiate the authentication flow by sending [authorization request parameters](/verify/reference/authorize-request-parameters) directly to the authorization server via a POST request. This approach keeps parameters out of URLs, making requests more secure and protecting their integrity. It also ensures sensitive data remains confidential – e.g. if you are including PII in a `login_hint`. [View the PAR implementation guide →](/verify/guides/pushed-authorization-requests/#implementing-par-with-idura) @@ -510,85 +415,6 @@ You can find an extensive list of libraries on JWT validation guide. -## The 'scope' parameter - -You can use the `scope` query parameter to specify which user data you want on a per-authorize request basis. - - - -You must explicitly enable this feature [per application](https://dashboard.idura.app/applications). -By default, Idura Verify uses the configured settings per eID (thus effectively ignoring any other `scope` values than `openid`). -Activate the "Enable dynamic scopes" toggle on an application to start using this feature. -Once you do that, Idura Verify will switch from using the per eID configured scope options to the ones you specify in the authorize request. - - - -### Anatomy of the scope value - -The `scope` query parameter can contain multiple values. Each value is separated by a single blank character (`' '` / ASCII `32` / Unicode `U+0020` ). - -The `scope` value must always contain the value `openid`, and it may also contain any of the following additional values: - -- `address` -- `email` -- `phone` -- `birthdate` -- `ssn` - -You can see which values are supported for the various eIDs in the [authorize URL builder](/verify/guides/authorize-url-builder/#auth-methods--acr-values) or in the individual eID articles. - - - -The quirks-mode variant of sending the same instructions via the `login_hint` is fully supported. -Consult the [authorize URL builder](/verify/guides/authorize-url-builder/#auth-methods--acr-values) for details. - - - -## The 'login_hint' parameter - -Just as for [the `scope` parameter](#the-scope-parameter), the `login_hint` parameter can contain multiple values. Each value here must also be separated by a single blank character. - -Idura Verify supports controlling the runtime behavior in quite a few aspects by values sent in this parameter. -We chose this approach as it is most often possible to send them through intermediaries if your architecture has such components (other OpenID Providers such as Azure AD or Auth0). - -Also, we use it as a fallback for cases where intermediaries do not let you pass values via otherwise standardized OpenID Connect query parameters (most notably `scope` and `acr_values`). - -Go to these specific use cases where `login_hint` is used to specify behaviour or get around limitations: - -- [App switching](/verify/guides/appswitch/) -- [Prefilled fields](/verify/guides/prefilled-fields/) -- [Quirks mode](/verify/guides/authorize-url-builder) -- ['Dynamic' styling](/verify/guides/custom-styling/#dynamic-style-sheets) -- [DK MitID for business](/verify/e-ids/danish-mitid/#business) -- [Action specifiers](#action-specifiers) - -### Action specifiers - -For DK and the SE another-device flows, you can add the following values to the `login_hint` query parameter to change the default wording used during login/signing. - -- `action:confirm` -- `action:accept` -- `action:approve` -- `action:sign` -- `action:login` (the default, mostly present for completeness) - -Sending, say, `action:approve` will change "Login at ..." worded elements to "Approve at ...". - -[Authorize URL builder example](/verify/guides/authorize-url-builder/?acr_values=urn:grn:authn:dk:mitid:low&action=approve) - -### End-user confirmation texts (message parameter) - -Note that specifically for [Danish MitID](/verify/e-ids/danish-mitid) and [Swedish BankID](/verify/e-ids/swedish-bankid), you may also send a base64-url-encoded message via `message:BASE64URL(...text...)`, which will be shown to the end user in the app. -For example, if you want to show a _Transfer EUR 100 to IBAN DK123456781234_ message to the user, add a `message:VHJhbnNmZXIgRVVSIDEwMCB0byBJQkFOIERLMTIzNDU2NzgxMjM0` value to the `login_hint`. - -[Danish MitID: Authorize URL builder example](/verify/guides/authorize-url-builder/?acr_values=urn:grn:authn:dk:mitid:low&action=approve&message=Transfer%20EUR%20100%20to%20IBAN%20DK123456781234) - -The maximum length of the message for Danish MitID is 130 characters before base64 encoding (according to our tests). - -[Swedish BankID: Authorize URL builder example](/verify/guides/authorize-url-builder/?acr_values=urn:grn:authn:se:bankid:another-device:qr&action=approve&message=Transfer%20EUR%20100%20to%20IBAN%20DK123456781234) - -The maximum length of the message for Swedish BankID is 1500 characters after base64 encoding. - ## Code exchange authorization examples ```php diff --git a/src/pages/verify/integrations/aspnet-core-v6.mdx b/src/pages/verify/integrations/aspnet-core-v6.mdx index 5fa0511b..1135ad17 100644 --- a/src/pages/verify/integrations/aspnet-core-v6.mdx +++ b/src/pages/verify/integrations/aspnet-core-v6.mdx @@ -62,7 +62,7 @@ To add the authentication services, call the `AddAuthentication` method. To enab Next, configure the OIDC authentication handler. Add a call to `AddOpenIdConnect`. Configure the necessary parameters, such as `ClientId`, `ClientSecret`, `ResponseType`, and not least the `Authority`. The latter is used by the middleware to get the metadata describing the relevant endpoints, the signing keys etc. -The OIDC middleware requests both the `openid` and `profile` scopes by default, you may configure additional scopes if your application is [configured with dynamic scopes](/verify/getting-started/oidc-intro/#the-scope-parameter). +The OIDC middleware requests both the `openid` and `profile` scopes by default, you may configure additional scopes if your application is [configured with dynamic scopes](/verify/reference/authorize-request-parameters/#scope-strategies-dynamic-vs-static). ```json // appsettings.json diff --git a/src/pages/verify/integrations/javascript.mdx b/src/pages/verify/integrations/javascript.mdx index d19cc2c0..c4541788 100644 --- a/src/pages/verify/integrations/javascript.mdx +++ b/src/pages/verify/integrations/javascript.mdx @@ -89,7 +89,7 @@ The `CriiptoAuth` constructor takes an object with the following parameters: Use your application's [domain and client ID](#register-your-application-in-criipto-verify) as the values for `domain` and `clientID` respectively. -Additional authorization parameters can be passed in the `CriiptoAuth` constructor as well. For all available options, see [the full list of supported parameters](/verify/getting-started/oidc-intro#authorize-request-parameters). +Additional authorization parameters can be passed in the `CriiptoAuth` constructor as well. For all available options, see [the full list of supported parameters](/verify/reference/authorize-request-parameters). ## Authorization methods @@ -189,7 +189,7 @@ console.log(match.id_token, match.claims); ``` -`prompt: 'login'` will force the user to re-authenticate even if they have already logged in. See [the list of supported parameters](/verify/getting-started/oidc-intro#authorize-request-parameters) for more information. +`prompt: 'login'` will force the user to re-authenticate even if they have already logged in. See [the list of supported parameters](/verify/reference/authorize-request-parameters) for more information. ## Logging user out diff --git a/src/pages/verify/integrations/vuejs.mdx b/src/pages/verify/integrations/vuejs.mdx index 40d936bf..87c6b4bd 100644 --- a/src/pages/verify/integrations/vuejs.mdx +++ b/src/pages/verify/integrations/vuejs.mdx @@ -65,7 +65,7 @@ The `CriiptoAuth` constructor takes an object with the following parameters: Use your application's [domain and client ID](#register-your-application-in-criipto-verify) as the values for `domain` and `clientID` respectively. -Additional authorization parameters can be passed in the `CriiptoAuth` constructor as well. For all available options, see [the full list of supported parameters](/verify/getting-started/oidc-intro#authorize-request-parameters). +Additional authorization parameters can be passed in the `CriiptoAuth` constructor as well. For all available options, see [the full list of supported parameters](/verify/reference/authorize-request-parameters). ## Authorization methods @@ -200,7 +200,7 @@ onMounted(() => { ``` -`prompt: 'login'` will force the user to re-authenticate even if they have already logged in. See [the list of supported parameters](/verify/getting-started/oidc-intro#authorize-request-parameters) for more information. +`prompt: 'login'` will force the user to re-authenticate even if they have already logged in. See [the list of supported parameters](/verify/reference/authorize-request-parameters) for more information. ## Logging user out diff --git a/src/pages/verify/reference/images/enable-dynamic-scopes.png b/src/pages/verify/reference/images/enable-dynamic-scopes.png new file mode 100644 index 0000000000000000000000000000000000000000..d0964ac109642a67b8bd411258717e5b9c61af30 GIT binary patch literal 36294 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 p|NA5V@0 + **Pushed Authorization Requests (PAR)**: If your application uses + [PAR](/verify/guides/pushed-authorization-requests/), these parameters are sent via an HTTP `POST` + request instead. + + +A list of all supported authorize request parameters can be found below. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterRequiredDescription
+ + client_id + + YesYour application's unique identifier.
+ + redirect_uri + + YesThe URL where the user will be redirected after authentication.
+ + response_type + + Yes + The type of credential Idura Verify will return to your application after the user + successfully authenticates (typically, `code`). +
+ + scope + + Yes + Used to request specific user claims. Must always include openid. +
+ + state + + RecommendedAn opaque string used to prevent CSRF attacks.
+ + nonce + + RecommendedAn opaque string used to prevent replay attacks.
+ + acr_values + + NoSpecifies which eID identity service(s) you want to invoke.
+ + response_mode + + NoSpecifies the format of the response Idura Verify returns to your redirect URL.
+ + login_hint + + No + Used to control the runtime behavior and customize the user experience in several ways. +
+ + prompt + + No + Specifies whether the user should be forced to re-authenticate, and allows you to manage + consent. +
+ + code_challenge + + PKCE onlyCode challenge for the PKCE flow.
+ + code_challenge_method + + PKCE onlyHashing method used to create the `code_challenge`, e.g. `S256`.
+ + request_uri + + PAR onlyA unique identifier of the request.
+ + request + + JAR onlyContains the JWT with JSON-encoded request parameters.
+ +## Token endpoint + +To complete the authentication process and obtain an [ID token](/verify/reference/glossary/#id-token), the client application sends +an HTTP `POST` request to the Idura Verify token endpoint (`/oauth2/token`). The required parameters depend on the authorization flow. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterRequiredDescription
+ + grant_type + + YesThe type of authorization flow being executed.
+ + code + + Authorization Code flow or PKCEAuthorization code received from the authorization endpoint.
+ + redirect_uri + + Authorization Code flow or PKCE + The URL where the user will be redirected after authentication. Must match the{' '} + redirect_uri + sent in the initial authorization request. +
+ + client_id + + PKCE onlyYour application's unique identifier.
+ + code_verifier + + PKCE only + A random string used to generate the code_challenge. +
+ + client_assertion + + Private Key JWT onlyA signed JWT used to authenticate your client instead of a client secret.
+ + client_assertion_type + + Private Key JWT only + The format of the assertion, must be set to + urn:ietf:params:oauth:client-assertion-type:jwt-bearer. +
+ + auth_req_id + + CIBA onlyA unique identifier of the request.
+ + headless_req_id + + Headless onlyA unique identifier of the request.
+ +## `client_id` + +The unique identifier for your Idura application. You can find this value under your application's **General** settings in the Idura Dashboard. + +## `redirect_uri` + +The URL where Idura will redirect the user's browser after authentication has been completed. +This exact URL must be pre-registered as a Redirect URL in your application settings in the Idura Dashboard. + + + As per the [OAuth 2.0 Specification](https://tools.ietf.org/html/rfc6749#section-3.1.2), Idura + removes everything after the hash and does not honor any fragments. + + +## `response_type` + +Denotes the kind of credential Idura Verify will return to your `redirect_uri` after the user successfully authenticates. + +- Use `code` if you are integrating a traditional server-based web application (Authorization Code flow) or a public client using PKCE. +- Use `id_token` only for legacy single-page applications using the Implicit flow (or for debugging). + +## `acr_values` + +Specifies the authentication method you want to trigger. For example, sending `urn:grn:authn:dk:mitid:business` will invoke Danish MitID Erhverv. + +You can send multiple values separated by a space to trigger multiple authentication methods in a single request (e.g., `acr_values=urn:grn:authn:se:bankid:same-device urn:grn:authn:dk:mitid:substantial`). + +Some eIDs have multiple `acr_values` to distinguish between a mobile and a web-based service, or to require different [levels of assurance](/verify/reference/glossary/#level-of-assurance-loa). +Supported `acr_values` can be found in the [Authorize URL Builder](/verify/guides/authorize-url-builder/#auth-methods--acr-values) or in the individual eID articles. + +**Default `acr_values` and Method selector** + +If no `acr_values` are sent, Idura Verify will invoke the eID methods configured in the **eID** section of your application settings. + +If multiple eID methods are triggered (either by passing multiple `acr_values` in the request or relying on your dashboard configuration), Idura will present the user with a selection screen listing all available eIDs. + +## `state` + +An opaque arbitrary alphanumeric string generated by your application, which Idura will send back to your `redirect_uri`. +You should verify that the returned state matches the one you sent to mitigate Cross-Site Request Forgery (CSRF) attacks. +Learn more in our [Security best practices guide](/verify/how-it-works/best-security-practices/#the-state-parameter). + +## `nonce` + +An opaque arbitrary alphanumeric string generated by your application, which Idura will include in the [ID Token](/verify/reference/glossary/#id-token). +You should verify that the returned nonce matches the original one you sent to mitigate replay attacks. +Learn more in our [Security best practices guide](/verify/how-it-works/best-security-practices/#the-nonce-parameter). + +## `response_mode` + +Specifies the format in which the result of a successful authentication will be delivered to your `redirect_uri`. + +- Use `query`to return the `code`/`id_token` as a query parameter. +- Use `fragment` to have it delivered on a URL fragment. +- Use `form_post` to have it posted back to your `redirect_uri`. + +The default values are `query` for `response_type=code` and `fragment` for `response_type=id_token`. + +## `scope` + +Defines which user attributes (claims) Idura Verify will include in the `id_token` issued to your application. + +Every authorize request **must** include the `openid` scope value. Multiple values are separated by a whitespace (e.g., `openid ssn address`). + +### Scope strategies (dynamic vs. static): + +How Idura handles requests for specific user data depends on your application's scope strategy. + +- **Dynamic strategy (default):** Provides maximum flexibility. You must explicitly request the specific data you want in every authorize request. For example, sending `scope=openid ssn` will request the user's social security number. +- **Static strategy:** You only send `scope=openid` in your request. Idura will automatically return the data points you pre-configured for each eID via the dashboard. + +You can change your application's scope strategy in the Idura Dashboard under the **General** tab of your application settings. + +![Enable dynamic scopes](./images/enable-dynamic-scopes.png) + +### Supported scope values + +When using dynamic scopes, the scope value must always contain the value `openid`, and it may also contain any of the following values: `address`, `email`, `phone`, `birthdate`, and `ssn` (availability depends on the specific eID provider). + +You can see which values are supported for the various eIDs in the [Authorize URL Builder](/verify/guides/authorize-url-builder/#auth-methods--acr-values) or in the individual eID articles. + + + **Quirks mode:** The quirks-mode variant of sending scope instructions via the `login_hint` + parameter is fully supported. Consult the [Authorize URL + Builder](/verify/guides/authorize-url-builder/#auth-methods--acr-values) for details. + + +## `login_hint` + +The `login_hint` is a standard OAuth 2.0 request parameter Idura uses to customize the runtime behavior in several ways. +We chose this approach as it is often possible to pass `login_hint` if you're integrating Idura Verify via an intermediary (e.g., Microsoft Entra ID or Auth0). +It is also a fallback for cases where intermediaries do not allow you to pass values via standard OpenID Connect query parameters (most notably `scope` and `acr_values`). + +`login_hint` can contain multiple values separated by a space. + +See specific use cases where `login_hint` is used to specify the runtime behaviour or get around limitations: + +- [App switching](/verify/guides/appswitch/) +- [Prefilled fields](/verify/guides/prefilled-fields/) +- [Quirks mode](/verify/guides/authorize-url-builder) +- ['Dynamic' styling](/verify/guides/custom-styling/#dynamic-style-sheets) +- [DK MitID for business](/verify/e-ids/danish-mitid/#business) +- [Action specifiers](#action-specifiers) +- [End-user confirmation texts](#end-user-confirmation-texts-message) + +### Action specifiers + +For Danish MitID and Swedish BankID (another-device flows), you can change the default "Log in" wording in the eID app by passing an action specifier: + +- `action:confirm` +- `action:accept` +- `action:approve` +- `action:sign` +- `action:login` (default) + +### End-user confirmation texts (message) + +For Danish MitID and Swedish BankID, you can display a custom text message directly inside the user's eID mobile app. The message must be base64-url-encoded and passed via `message:BASE64URL(...text...)`. + +- **Danish MitID:** The maximum length is 130 characters _before_ base64 encoding. +- **Swedish BankID:** The maximum length is 1500 characters _after_ base64 encoding. + +## `prompt` + +Used to control the session behavior of your domain, defining whether the user will be forced to re-authenticate. +Also allows you to manage end-user consent. Idura Verify supports the following values: + +- **Omitted (default):** Reuses an existing session if one is available. If no session exists, the user is prompted to authenticate. +- `none`: Requires an existing active session. If the user is not already logged in, authentication fails. +- `login`: Forces the user to re-authenticate, even if they already have an active session. +- `consent`: Allow the user to grant consent. +- `consent_revoke`: Allows the user to revoke previously granted consent, for instance, via a ["forget-me" link](/verify/e-ids/norwegian-bankid/#forced-and-optional-consent). + +More information is available in our [SSO guide](/verify/guides/sso/#per-request-options) and [Authorize URL Builder](/verify/guides/authorize-url-builder/). + +## `code_challenge` + +Required for the [PKCE flow](/verify/reference/authorization-flows/pkce). The `code_challenge` is a base64URL-encoded hash of a random string (the [`code_verifier`](/verify/reference/request-parameters/#code_verifier)) generated by your client application. + +## `code_challenge_method` + +Required for the [PKCE flow](/verify/reference/authorization-flows/pkce). This tells Idura Verify which hashing method you used to create the `code_challenge` (e.g. `S256`). + +## `request_uri` + +Used in [Pushed Authorization Requests (PAR)](/verify/guides/pushed-authorization-requests/) to pass a reference to the authorization request payload, +keeping the actual request parameters out of the browser's query string. + +## `request` + +Used in [JWT-secured Authorization Requests (JAR)](/verify/reference/glossary/#jwt-secured-authorization-request-jar). +This parameter contains a Request Object – a signed JWT whose claims hold the JSON-encoded authorization request parameters. +This acts as a security enhancement to prevent parameter tampering. + +## `grant_type` + +Sent to the token endpoint to specify the [authorization flow](/verify/reference/authorization-flows/) type being executed. +Idura Verify supports the following values: + +- `authorization_code` (for Authorization Code flow and PKCE) +- `urn:ietf:params:oauth:grant-type:ciba` (for CIBA flow) +- `urn:idura:params:grant-type:headless` (for Headless flow) + +## `code` + +The authorization code received from the authorization endpoint after a successful user login. +Sent to the token endpoint to be exchanged for tokens during the Authorization Code and PKCE flows. + +## `code_verifier` + +Required at the token endpoint for the [PKCE flow](/verify/reference/authorization-flows/pkce). +This is the original random string you generated before hashing it into the [`code_challenge`](/verify/reference/request-parameters/#code_challenge). + +## `auth_req_id` + +A unique request identifier returned by Idura Verify that must be included in the token request during the [CIBA flow](/verify/reference/authorization-flows/ciba). + +## `headless_req_id` + +A unique request identifier returned by Idura Verify that must be included in the token request during the [Headless flow](/verify/reference/authorization-flows/headless). + +## `client_assertion` + +The signed JWT used for [Private key JWT client authentication](/verify/guides/privatekey-jwt/) at the token endpoint instead of a static client secret. + +## `client_assertion_type` + +Used for [Private key JWT client authentication](/verify/guides/privatekey-jwt/). When using a client assertion, this parameter must be set to `urn:ietf:params:oauth:client-assertion-type:jwt-bearer`. From 53bbe42c165c91e45c4549e83ccd136a9a8b8f37 Mon Sep 17 00:00:00 2001 From: nmoskaleva Date: Tue, 14 Apr 2026 12:59:04 +0200 Subject: [PATCH 2/4] Separate OpenID Connect flows overview and technical reference The "Using OpenID Connect" page previously contained both high-level description and technical implementation details of OIDC authorization flows. This commit extracts the technical details into dedicated reference pages. The original page in the "How it works" section now serves only as a high-level overview. --- src/pages/verify/e-ids/swedish-bankid.mdx | 7 +- .../getting-started/dashboard-setup.mdx | 6 +- .../verify/guides/caller-authentication.mdx | 4 +- src/pages/verify/guides/oidc-visualizer.mdx | 8 +- .../guides/pushed-authorization-requests.mdx | 10 +- .../how-it-works/best-security-practices.mdx | 26 +- src/pages/verify/how-it-works/oidc-intro.mdx | 447 ++---------------- src/pages/verify/integrations/auth0.mdx | 2 +- src/pages/verify/integrations/firebase.mdx | 2 +- src/pages/verify/integrations/onelogin.mdx | 2 +- .../authorization-code-flow.mdx | 154 ++++++ .../reference/authorization-flows/ciba.mdx | 85 ++++ .../authorization-flows/headless.mdx | 93 ++++ .../authorization-flows/implicit-flow.mdx | 35 ++ .../reference/authorization-flows/index.mdx | 51 ++ .../reference/authorization-flows/pkce.mdx | 113 +++++ src/pages/verify/reference/glossary.mdx | 32 +- .../reference/images/PKCE_flow_diagram.png | Bin 0 -> 145237 bytes src/pages/verify/reference/rate-limiting.mdx | 2 +- src/pages/verify/reference/token-contents.mdx | 2 +- src/snippets/generate-client-secret.mdx | 4 +- src/snippets/use-par.mdx | 10 + src/snippets/validate-jwt.mdx | 8 + src/utils/get-breadcrumb.tsx | 10 +- 24 files changed, 656 insertions(+), 457 deletions(-) create mode 100644 src/pages/verify/reference/authorization-flows/authorization-code-flow.mdx create mode 100644 src/pages/verify/reference/authorization-flows/ciba.mdx create mode 100644 src/pages/verify/reference/authorization-flows/headless.mdx create mode 100644 src/pages/verify/reference/authorization-flows/implicit-flow.mdx create mode 100644 src/pages/verify/reference/authorization-flows/index.mdx create mode 100644 src/pages/verify/reference/authorization-flows/pkce.mdx create mode 100644 src/pages/verify/reference/images/PKCE_flow_diagram.png create mode 100644 src/snippets/use-par.mdx create mode 100644 src/snippets/validate-jwt.mdx diff --git a/src/pages/verify/e-ids/swedish-bankid.mdx b/src/pages/verify/e-ids/swedish-bankid.mdx index 3e2f848f..4010e800 100644 --- a/src/pages/verify/e-ids/swedish-bankid.mdx +++ b/src/pages/verify/e-ids/swedish-bankid.mdx @@ -77,7 +77,10 @@ _Note_ that, as is also described on the website, using test BankID users does r ## BankID Phone Authentication -BankID in telephone calls facilitates user authentication during a phone call. + + BankID in telephone calls + +facilitates user authentication during a phone call. For details and integration instructions, please refer to [Caller Authentication with CIBA](/verify/guides/caller-authentication). @@ -91,7 +94,7 @@ For instance you could render the QR flow in your own website, or trigger app2ap ### The underlying Headless flow -Idura Verify facilitates Swedish BankID headless authentication via the [Headless flow](/verify/getting-started/oidc-intro/#authenticate-headless). +Idura Verify facilitates Swedish BankID headless authentication via the [Headless flow](/verify/reference/authorization-flows/headless). ### Implementing BankID Headless Authentication diff --git a/src/pages/verify/getting-started/dashboard-setup.mdx b/src/pages/verify/getting-started/dashboard-setup.mdx index b220ddd5..e4f8b463 100644 --- a/src/pages/verify/getting-started/dashboard-setup.mdx +++ b/src/pages/verify/getting-started/dashboard-setup.mdx @@ -58,9 +58,9 @@ import GenerateClientSecretSnippet from '../../../snippets/generate-client-secre **Client secrets are for [server-side - applications](/verify/reference/glossary/#confidential-client) only.** If you are building a - Single Page Application or a native mobile app, you cannot securely store a secret. Use - **[PKCE](/verify/how-it-works/oidc-intro/#authenticate-with-pkce)** instead. + applications](/verify/reference/glossary/#confidential-clients) only.** If you are building a + Single-page application or a native mobile app, you cannot securely store a secret. Use + **[PKCE](/verify/reference/authorization-flows/pkce)** instead. If you lost your client secret or need to generate one, you can do so from your Application settings. diff --git a/src/pages/verify/guides/caller-authentication.mdx b/src/pages/verify/guides/caller-authentication.mdx index 6792cb7a..28f3f44e 100644 --- a/src/pages/verify/guides/caller-authentication.mdx +++ b/src/pages/verify/guides/caller-authentication.mdx @@ -15,7 +15,7 @@ This creates a secure, phishing-resistant way to confirm who you are talking to ## The CIBA flow -Caller Authentication is built on the [**Client-Initiated Backchannel Authentication (CIBA) flow**](/verify/how-it-works/oidc-intro/#authenticate-with-ciba). +Caller Authentication is built on the [**Client-Initiated Backchannel Authentication (CIBA) flow**](/verify/reference/authorization-flows/ciba). With CIBA, the client application provides a hint (e.g. the user's SSN) that triggers an authentication request to the user's device. The user can then approve the request and complete the authentication process on their device. @@ -45,7 +45,7 @@ where the end-user doesn’t operate the client application—such as a call cen ### Implementing Caller Authentication with CIBA -The CIBA flow is only available to traditional server-based web applications ([confidential clients](/verify/reference/glossary/#confidential-client)) and requires client validation for authentication requests. +The CIBA flow is only available to traditional server-based web applications ([confidential clients](/verify/reference/glossary/#confidential-clients)) and requires client validation for authentication requests. We'll use [private key JWTs](/verify/guides/privatekey-jwt/) for authentication in the example below. An alternative (_less secure_) approach is to use `client_secret` for authentication. #### 1. Start the authentication request diff --git a/src/pages/verify/guides/oidc-visualizer.mdx b/src/pages/verify/guides/oidc-visualizer.mdx index 4cd8030d..334e81cd 100644 --- a/src/pages/verify/guides/oidc-visualizer.mdx +++ b/src/pages/verify/guides/oidc-visualizer.mdx @@ -3,7 +3,7 @@ product: verify category: Guides & Tools sort: 0 title: OpenID Connect Visualizer -subtitle: An interactive demo lets you run an OpenID Connect Authorization Code Flow and observe the entire process from sending an authentication request to obtaining and validating an ID token. +subtitle: An interactive demo lets you run an OpenID Connect Authorization Code flow and observe the entire process from sending an authentication request to obtaining and validating an ID token. --- import OidcVisualizer from '../../../components/OidcVisualizer/OidcVisualizer'; @@ -20,12 +20,12 @@ If the authentication flow completes successfully, the client application receiv This visualizer lets you run the flow step by step and observe requests and responses exchanged between the **Client application** and **Idura Verify acting as the Authorization Server**, so you can understand what happens at every step. -## Authorization Code Flow +## Authorization Code flow -[Authorization Code Flow](/verify/how-it-works/oidc-intro/#authenticate-with-back-channel-authorization-code-flow) is the most common OIDC flow for web applications. +[Authorization Code flow](/verify/how-it-works/oidc-intro/#authorization-code-flow) is the most common OIDC flow for web applications. The diagram below shows how the Client Application and Authorization Server interact to complete this flow. -![Authorization Code Flow ](./images/authorization_code_flow_diagram.png) +![Authorization Code flow ](./images/authorization_code_flow_diagram.png) ## Try the Interactive Demo diff --git a/src/pages/verify/guides/pushed-authorization-requests.mdx b/src/pages/verify/guides/pushed-authorization-requests.mdx index bdaf2728..47baeb82 100644 --- a/src/pages/verify/guides/pushed-authorization-requests.mdx +++ b/src/pages/verify/guides/pushed-authorization-requests.mdx @@ -20,7 +20,7 @@ Using PAR improves [Telemetry & Observability](/verify/guides/telemetry/#par) fo it helps you trace authorization requests by giving you a `Trace-Id` to follow immediately and directly to your backend. PAR can be used with any [OpenID Connect flow](/verify/how-it-works/oidc-intro/#supported-openid-connect-flows) that involves authorization requests sent via the browser, -including the [Authorization Code Flow](/verify/how-it-works/oidc-intro/#authenticate-with-back-channel-authorization-code-flow) and the [Authorization Code Flow with PKCE](/verify/how-it-works/oidc-intro/#authenticate-with-pkce). +including the [Authorization Code flow](/verify/how-it-works/oidc-intro/#authorization-code-flow) and the [Authorization Code flow with PKCE](/verify/how-it-works/oidc-intro/#pkce-flow). ## How PAR works @@ -46,17 +46,17 @@ Your POST request should include: - The same parameters you would normally send in an authorization request (e.g. `response_type`, `scope`, `redirect_uri`, `state`). For the complete list of authorization request parameters, see [authorize request parameters](/verify/reference/authorize-request-parameters/). -- Client authentication credentials (for [confidential clients](/verify/reference/glossary/#confidential-client)): +- Client authentication credentials (for [confidential clients](/verify/reference/glossary/#confidential-clients)): - either `client_id` and `client_secret` (included in the Basic Authorization header) or - `client_assertion` and `client_assertion_type` (included in the request body), when using [Private Key JWT](/verify/guides/privatekey-jwt/). -- `code_challenge` and `code_challenge_method` (for [public clients](/verify/reference/glossary/#public-clients) or clients implementing [Authorization Code Flow with PKCE](/verify/how-it-works/oidc-intro/#authenticate-with-pkce)). +- `code_challenge` and `code_challenge_method` (for [public clients](/verify/reference/glossary/#public-clients) or clients implementing [Authorization Code flow with PKCE](/verify/how-it-works/oidc-intro/#pkce-flow)). -The [PKCE](/verify/reference/glossary/#pkce-proof-key-for-code-exchange) extension to the Authorization Code Flow is required for public clients and recommended for confidential clients. +The [PKCE](/verify/reference/glossary/#pkce-proof-key-for-code-exchange) extension to the Authorization Code flow is required for public clients and recommended for confidential clients. It offers stronger protection against the misuse or injection of authorization codes, making your implementation more secure. -For more details, see [OpenID Connect best security practices](/verify/how-it-works/best-security-practices/#authorization-code-flow). +For more details, see [OpenID Connect security best practices](/verify/how-it-works/best-security-practices/#authorization-code-flow). diff --git a/src/pages/verify/how-it-works/best-security-practices.mdx b/src/pages/verify/how-it-works/best-security-practices.mdx index 704be5e4..beb852b5 100644 --- a/src/pages/verify/how-it-works/best-security-practices.mdx +++ b/src/pages/verify/how-it-works/best-security-practices.mdx @@ -2,7 +2,7 @@ product: verify category: How it works sort: 3 -title: OpenID Connect best security practices +title: OpenID Connect security best practices subtitle: Learn about key security guidelines for implementing basic OpenID Connect authentication in client applications. --- @@ -13,22 +13,22 @@ In OpenID Connect(OIDC) authentication flows, there are two main roles: Idura Verify is an OpenID Provider, handling most of the process. However, it is important for developers of client applications to also follow security best practices. -## Authorization Code Flow +## Authorization Code flow -The most secure method of performing OIDC authentication is the [Authorization Code Flow](/verify/how-it-works/oidc-intro/#authenticate-with-back-channel-authorization-code-flow), or its extended version — [Authorization Code Flow with PKCE](/verify/how-it-works/oidc-intro/#authenticate-with-pkce). +The most secure method of performing OIDC authentication is the [Authorization Code flow](/verify/reference/authorization-flows/authorization-code-flow), or its extended version — [Authorization Code flow with PKCE](/verify/reference/authorization-flows/pkce). -In the Authorization Code Flow, the client application exchanges an authorization code for tokens at the token endpoint. When using Idura Verify for eID authentication: After the end user successfully logs in with their [eID](/verify/e-ids) and is redirected back to the client application through the redirect URL, the client application retrieves the authorization code from the URL. The client application then uses this code to request an ID token from Idura Verify (see [code for token exchange](/verify/how-it-works/oidc-intro/#exchange-the-code-for-a-token)). +In the Authorization Code flow, the client application exchanges an authorization code for tokens at the token endpoint. When using Idura Verify for eID authentication: After the end user successfully logs in with their [eID](/verify/e-ids) and is redirected back to the client application through the redirect URL, the client application retrieves the authorization code from the URL. The client application then uses this code to request an ID token from Idura Verify (see [code for token exchange](/verify/how-it-works/oidc-intro/#exchange-the-code-for-a-token)). -The Authorization Code Flow enhances security by mitigating replay attempts by attackers (when an attacker tries to reuse a valid intercepted token) and generally reduces the attack surface since ID tokens are not exposed in URLs. +The Authorization Code flow enhances security by mitigating replay attempts by attackers (when an attacker tries to reuse a valid intercepted token) and generally reduces the attack surface since ID tokens are not exposed in URLs. **Best practice:** - Public clients — such as single-page applications and native applications — must use the **PKCE flow** to prevent misuse of authorization codes. - Confidential clients — such as traditional server-based web applications – are encouraged to use the **PKCE flow** as well. This is a recommended approach, since **PKCE** provides strong protection against misuse and injection of authorization codes, making it a more secure option. -- Alternatively, confidential clients can implement the **Authorization Code Flow**. -- Confidential clients implementing the **Authorization Code Flow** (without **PKCE**) should take additional precautions, such as using the `nonce` parameter and the respective claim in the `id_token`, and one-time tokens carried in the `state` parameter. -- For use cases where authentication is initiated on one device but completed on another (e.g., a call center agent starts the authentication process for the end user), the back-channel [CIBA flow](/verify/how-it-works/oidc-intro/#authenticate-with-ciba) is the right choice. -- The Implicit Flow is obsolete and [should not be used in production applications](/verify/how-it-works/best-security-practices/#avoid-implicit-flow). +- Alternatively, confidential clients can implement the **Authorization Code flow**. +- Confidential clients implementing the **Authorization Code flow** (without **PKCE**) should take additional precautions, such as using the `nonce` parameter and the respective claim in the `id_token`, and one-time tokens carried in the `state` parameter. +- For use cases where authentication is initiated on one device but completed on another (e.g., a call center agent starts the authentication process for the end user), the back-channel [CIBA flow](/verify/reference/authorization-flows/ciba) is the right choice. +- The Implicit flow is obsolete and [should not be used in production applications](/verify/how-it-works/best-security-practices/#avoid-implicit-flow). ## Pushed Authorization Requests @@ -57,7 +57,7 @@ The OpenID Provider puts the received `nonce` value into the `id_token` that is - `nonce` serves as a [token validation](/verify/how-it-works/best-security-practices/#always-validating-jwts) parameter. If an attacker injects an authorization code in the authorization response, the `nonce` value in the client session and the `nonce` value in the `id_token` received from the token endpoint will not match, and the attack will be detected. - `nonce` also helps protect against an attacker potentially intercepting a valid [`state` parameter](/verify/how-it-works/best-security-practices/#the-state-parameter), but then providing a `code` value tied to a different session. This could be a concern for public clients, and, in theory, in some confidential client scenarios. -**Best practice:** For confidential clients implementing the Authorization Code Flow, it is strongly recommended to always include a `nonce` parameter in authorization requests to protect against misuse of authorization codes. +**Best practice:** For confidential clients implementing the Authorization Code flow, it is strongly recommended to always include a `nonce` parameter in authorization requests to protect against misuse of authorization codes. The client application should then validate the `nonce` in the received `id_token`, and not use any issued tokens until this check has succeeded. _For public clients using the PKCE flow, the `nonce` parameter can also provide an additional layer of security. However, it should only be used in addition to PKCE, not as a substitute for it._ @@ -92,11 +92,11 @@ When using asymmetric cryptography, authorization servers do not need to store s **Best practice:** Use Private Key JWTs for client authentication to enhance security and simplify key management. -## Avoid Implicit Flow +## Avoid Implicit flow -The Implicit Flow has been deprecated and is considered insecure due to its vulnerability to token leakage and token replay attacks. Client applications should not use the Implicit Flow or any other method that causes the authorization server to issue tokens directly in the authorization response (such as `response_type=id_token`). +The [Implicit flow](/verify/reference/authorization-flows/implicit-flow/) has been deprecated and is considered insecure due to its vulnerability to token leakage and token replay attacks. Client applications should not use the Implicit flow or any other method that causes the authorization server to issue tokens directly in the authorization response (such as `response_type=id_token`). -Tokens in the Implicit Flow are exposed in the URL fragment (`response_mode=fragment`), making them vulnerable to interception and mishandling. While `response_mode=fragment` is convenient and easy to use during development and testing, it is insecure for production applications for several reasons: +Tokens in the Implicit flow are exposed in the URL fragment (`response_mode=fragment`), making them vulnerable to interception and mishandling. While `response_mode=fragment` is convenient and easy to use during development and testing, it is insecure for production applications for several reasons: - Tokens in a URL fragment can be exposed to browser history, referral headers, and intermediaries. - Browser components or extensions may access the fragment, increasing the risk of interception or token leakage. diff --git a/src/pages/verify/how-it-works/oidc-intro.mdx b/src/pages/verify/how-it-works/oidc-intro.mdx index e8495ab3..16183d0c 100644 --- a/src/pages/verify/how-it-works/oidc-intro.mdx +++ b/src/pages/verify/how-it-works/oidc-intro.mdx @@ -3,442 +3,73 @@ product: verify category: How it works sort: 2 title: Using OpenID Connect -subtitle: Learn how Idura supports the OpenID Connect protocol. +subtitle: Learn how Idura Verify implements the OpenID Connect protocol and find the right authentication flow for your tech stack. --- -## Using OpenID Connect to integrate with Idura Verify - -Idura Verify is an OpenID Provider. It is integrated through the authentication API which follows the OpenID Connect and OAuth 2.0 specifications. - -### Supported OpenID Connect Flows - -The OpenID Connect(OIDC) protocol defines different strategies (flows) to authenticate users. Idura Verify supports five different OpenID Connect flows: - -#### [1. The Authorization Code Flow](/verify/how-it-works/oidc-intro/#authenticate-with-back-channel-authorization-code-flow) - -The Authorization Code Flow is used for traditional server-based web applications, also referred to as _confidential clients_. These applications can securely store a client secret and establish back-channel communication with the Idura Verify service. This facilitates a secure [code-for-token exchange](#exchange-the-code-for-a-token) during authentication. - -#### [2. The PKCE Flow](/verify/how-it-works/oidc-intro/#authenticate-with-pkce) - -The PKCE Flow (pronounced _pixy_) can be used by _public clients_ such as single-page applications (SPAs) and native applications that cannot keep a secret. With PKCE, a one-time secret is generated and used for the code exchange. - -#### [3. The Implicit Flow](/verify/how-it-works/oidc-intro/#authenticate-with-implicit-flow) - -The Implicit Flow returns a token directly in the browser. Support for this flow is being discontinued, although it will continue to function on test domains for the foreseeable future for simpler debugging during development. - -#### [4. The CIBA flow](/verify/how-it-works/oidc-intro/#authenticate-with-ciba) - -The CIBA flow is designed for use cases where the client application isn't directly operated by the end-user but can trigger the authentication process on their behalf. A canonical example is a call center agent verifying a caller's identity. - -#### [5. The Headless flow](/verify/how-it-works/oidc-intro/#authenticate-with-headless) - -The Headless flow is a custom protocol by Idura. It allows interacting with eIDs that offer a poll-based workflow, where a user agent redirect is not required. - -**The choice of the OpenID Connect flow depends on the requirements and architecture of the client application implementing the OIDC authentication.** - -The following sections describe the five flows and introduce the parameters to configure the authentication and subsequent user information retrieval. - -Note that you don't need to perform the steps below manually. In most cases, they are handled by configuring an OpenID Connect package on your platform of choice. - - - -## Authenticate the User - -To begin the login flow, you will need to authenticate the user at the [eID Provider](/verify/e-ids) indicated in your request. - -To authenticate the user, your app must send the user to the _OAuth2 authorization endpoint (`/oauth2/authorize`)_ with the appropriate set of parameters. - -You can find the URL for the _OAuth2 authorization endpoint_ in the OpenID Connect Discovery Document exposed on your [Idura Verify Domain](/verify/how-it-works/core-concepts/#domains): - -```http -GET https://YOUR_SUBDOMAIN.idura.broker/.well-known/openid-configuration -``` - -The response from this endpoint is a JSON document with an `authorization_endpoint` property. The corresponding property value is the URL of the _OAuth2 authorization endpoint_. - -### Example authorization URL - -The following initiates the authentication through an OAuth2 authorization request: - -```http -GET https://YOUR_SUBDOMAIN.idura.broker/oauth2/authorize? - response_type=code|id_token& - client_id=CLIENT_ID& - redirect_uri=YOUR_RETURN_URL& - acr_values=CHOSEN_IDENTITY_SERVICE& - scope=openid& - state=YOUR_STATE -``` - -Note that providing `response_type=code` specifies that you want either the traditional back-channel _Authorization Code Flow_ or the _PKCE Flow_. If you specify `response_type=id_token` you indicate that you want the _Implicit Flow_. In the Implicit Flow, you receive the issued token in a URL fragment on the return URL. - -If you want to receive the response in another way, you must specify the `response_mode` parameter. - -When adding login to your application, you will build the authorization URL (also called the [_authorize URL_](/verify/guides/authorize-url-builder/)) and redirect the user to it. An HTML snippet for your authorize URL might look like this: - -```html - - Sign in with Norwegian BankID - -``` - -You can try the above URL right now if you have a [test user for Norwegian BankID](/verify/e-ids/norwegian-bankid/#test-users). - -Your next steps will depend on the [OpenID Connect flow type](/verify/how-it-works/oidc-intro/#supported-openid-connect-flows) you're implementing. See the relevant section below for a description of how each flow unfolds: - -- [Authorization Code Flow](/verify/how-it-works/oidc-intro/#authenticate-with-back-channel-authorization-code-flow) -- [PKCE Flow](/verify/how-it-works/oidc-intro/#authenticate-with-pkce) -- [Implicit Flow](/verify/how-it-works/oidc-intro/#authenticate-with-implicit-flow) -- [CIBA](/verify/how-it-works/oidc-intro/#authenticate-with-ciba) -- [Headless](/verify/how-it-works/oidc-intro/#authenticate-headless) - -After completing the steps specific to your chosen flow, you can proceed to [validating the JWT](/verify/how-it-works/oidc-intro/#validate-the-jwt) received from successful authentication. - - - -A more secure alternative to traditional authorization requests is [Pushed Authorization Requests (PAR)](/verify/guides/pushed-authorization-requests). - -PAR lets you initiate the authentication flow by sending [authorization request parameters](/verify/reference/authorize-request-parameters) directly to the authorization server via a POST request. -This approach keeps parameters out of URLs, making requests more secure and protecting their integrity. It also ensures sensitive data remains confidential – e.g. if you are including PII in a `login_hint`. - -[View the PAR implementation guide →](/verify/guides/pushed-authorization-requests/#implementing-par-with-idura) - + Idura Verify acts as an OpenID Provider ([Authorization + Server](/verify/reference/glossary/#authorization-server)). You integrate with our authentication + API using standard [OpenID Connect (OIDC)](https://openid.net/specs/openid-connect-core-1_0.html) + and [OAuth 2.0](https://datatracker.ietf.org/doc/html/rfc6749) specifications. -## Authenticate with back-channel Authorization Code Flow - -### Example request - -```text -GET https://YOUR_SUBDOMAIN.idura.broker/oauth2/authorize? - response_type=code& - response_mode=query& - client_id=CLIENT_ID& - redirect_uri=YOUR_RETURN_URL& - acr_values=CHOSEN_IDENTITY_SERVICE& - scope=openid& - state=YOUR_STATE -``` - -### Example response - -For the Authorization Code Flow, when you used `response_type=code`, you will receive an `HTTP 302` response which redirects your browser to your specified `redirect_uri` with the authorization code included in the query parameters: - -```text -HTTP/1.1 302 Found -Location: YOUR_RETURN_URL?code=AUTHORIZATION_CODE&state=YOUR_STATE -``` - -#### Error response - -In case the authentication request fails, you will receive an `HTTP 302` response which redirects your browser to your specified `redirect_uri` with an error included in the query parameters: - -```text -HTTP/1.1 302 Found -Location: YOUR_RETURN_URL?error=ERROR_CODE&error_description=...&state=YOUR_STATE -``` - -### Exchange the code for a token - -For the Authorization Code Flow, you will need to exchange the returned code for an actual token. This is done by posting the authorization code received from the previous step to the token endpoint. - -For PKCE-enabled clients, this exchange is based on a one-time secret created by the OIDC library you use to handle the flow, and the exchange will also be handled by the same library. - -For traditional back-channel flows, note that you must use an HTML-form-style HTTP POST here, and preferably send the credentials in the `Authorization` HTTP header. -You must also x-www-form-urlencode the values of the `CLIENT_ID` and `CLIENT_SECRET`, respectively, before constructing the `Authorization` header in `Basic` format. - -```text -HTTP POST https://YOUR_SUBDOMAIN.idura.broker/oauth2/token -Content-Type: application/x-www-form-urlencoded -Authorization: Basic BASE64(xWwwFormUrlEncode(CLIENT_ID):xWwwFormUrlEncode(CLIENT_SECRET)) - -grant_type=authorization_code&code=AUTHORIZATION_CODE&client_id=CLIENT_ID&redirect_uri=YOUR_RETURN_URL -``` - -[See more code exchange authorization examples in various languages.](#code-exchange-authorization-examples) - -**_Note_** _We also support receiving the client credentials in the payload, but this usage is discouraged by the OAuth2 specification, and we strongly recommend that you send the credentials in the `Authorization: Basic ...` HTTP header value as described above._ +## Supported authentication flows -The client id and secret retrieved from the Idura Verify management UI, as well as the `redirect_uri`, must be exactly the same as you used in the authorization request in the previous step. +OpenID Connect defines different strategies (flows) to authenticate users. Idura Verify supports five different flows. +Use the guide below to determine which OIDC flow fits your application architecture. - + -Note that the back-channel exchange of the authorization code requires the use of the client secret, which is basically just a password, and therefore _must always_ be made via a back-channel - server to server - and never from a public client like a browser or native application. Never include the secret in the frontend code. +**You don't need to implement authentication flows from scratch.** +An OpenID Connect client library or one of Idura's SDKs will handle implementation for you. -For PKCE-enabled clients, the secret is generated on the fly, and no special handling of it is required by you. +- **Idura SDKs:** We provide SDKs for popular frameworks like React, Node.js, and ASP.NET. Explore our [SDKs and integration guides](/verify/integrations/). +- **Standard OIDC libraries:** If you can't find an Idura SDK for your stack, you can use any standard OpenID Connect library for your platform. Popular examples include Authlib (Python), Spring Security (Java), and go-oidc (Go). -## Authenticate with PKCE - -PKCE allows you to use one-time secrets to perform code exchange. The format of the secrets is specified in [RFC 7636](https://datatracker.ietf.org/doc/html/rfc7636#section-4). - -[@criipto/auth-js](https://github.com/criipto/criipto-auth.js) and [@criipto/verify-react](https://github.com/criipto/criipto-verify-react) support PKCE. - -### Example request - -```http -GET https://YOUR_SUBDOMAIN.idura.broker/oauth2/authorize? - response_type=code& - response_mode=query& - client_id=CLIENT_ID& - redirect_uri=YOUR_RETURN_URL& - acr_values=CHOSEN_IDENTITY_SERVICE& - scope=openid& - state=YOUR_STATE& - code_challenge=YOUR_CODE_CHALLENGE& - code_challenge_method=S256 -``` - -### Example response - -You will receive an `HTTP 302` response, which redirects your browser to your specified `redirect_uri` with the authorization code included in the query parameters: - -```text -HTTP/1.1 302 Found -Location: YOUR_RETURN_URL?code=AUTHORIZATION_CODE&state=YOUR_STATE -``` - -### Code exchange - -As opposed to the back-channel authorization flow, you should not include a static client secret when authenticating with PCKE. Instead, you include a `code_verifier`, which was generated in the previous step. - -```text -HTTP POST https://YOUR_SUBDOMAIN.idura.broker/oauth2/token -Content-Type: application/x-www-form-urlencoded - -grant_type=authorization_code&code=AUTHORIZATION_CODE&client_id=CLIENT_ID&redirect_uri=YOUR_RETURN_URL&code_verifier=YOUR_CODE_VERIFIER -``` - -## Authenticate with Implicit Flow - -Implicit Flow, which returns an `id_token` directly in the browser via the `#` fragment, is supported, but not recommended. - -[@criipto/auth-js](https://github.com/criipto/criipto-auth.js) supports Implicit Flow. - -### Example request - -```http -GET https://YOUR_SUBDOMAIN.idura.broker/oauth2/authorize? - response_type=id_token& - response_mode=fragment& - client_id=CLIENT_ID& - redirect_uri=YOUR_RETURN_URL& - acr_values=CHOSEN_IDENTITY_SERVICE& - scope=openid& - state=YOUR_STATE -``` - -### Example response - -```text -HTTP/1.1 302 Found -Location: YOUR_RETURN_URL#id_token=eyJ[...].eyJ[...].Sfl[...]&state=[...] -``` - -## Authenticate with CIBA - -Client-Initiated Backchannel Authentication (CIBA) is a new authentication flow where the client application initiates the authentication process on behalf of the end-user. - -The client application provides a hint that triggers an authentication request to the user's authentication device, typically a phone. -The user can then approve the request and complete the authentication process as usual. - -CIBA is an extension to OpenID Connect. However, unlike in other OpenID Connect flows, there is a direct communication between your application and the OpenID Provider (Idura Verify), without redirects through the user's browser. - -CIBA supports use cases not covered by other OpenID Connect flows, such as: - -- Confirming a user's identity to a call center agent during a phone call or via a chat. -- Using a smartphone to authorize a payment at a point of sale terminal. -- Enabling a bank agent to authenticate a customer in a bank branch during a face-to-face interaction. - -Idura Verify supports CIBA for the [Swedish BankID Phone Authentication](/verify/guides/caller-authentication/#caller-authentication-with-swedish-bankid) and the [Norwegian BankID Caller Authentication](/verify/guides/caller-authentication/#caller-authentication-with-norwegian-bankid). - -### Example request - -The client application shall make an `HTTP POST` request to the backchannel authentication endpoint to ask for end-user authentication. -_The CIBA flow is only available for confidential clients, and the example below uses [private key JWTs](/verify/guides/privatekey-jwt/) for client authentication._ - -```text -HTTP POST https://YOUR_SUBDOMAIN.idura.broker/ciba/bc-authorize -Content-Type: application/x-www-form-urlencoded - -scope=openid -&login_hint=sub:ssn:SSN -&acr_values=urn:grn:authn:se:bankid -&binding_message=BINDING_MESSAGE -&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer -&client_assertion=YOUR_JWT_ASSERTION -authorization_details=%5B%7B%22type%22%3A%22phoneAuth%22%2C%22callInitiator%22%3A%22RP%22%7D%5D -``` - -### Example response - -A successful response will contain the request id, `auth_req_id`: - -```text -{ - "auth_req_id" : "3857f8ff-21b9-48ae-a732-a3bd8128a7ae", - "expires_in" : 120 -} -``` - -### Obtaining the token - -Poll the token endpoint (`/oauth2/token`) providing the `auth_req_id`: - -```text -HTTP POST https://YOUR_DOMAIN.idura.broker/oauth2/token -Content-Type: application/x-www-form-urlencoded - -auth_req_id=AUTH_REQ_ID -&grant_type=urn:openid:params:grant-type:ciba -&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer -&client_assertion=$client_assertion -``` - -### Token response - -The token will be issued upon successful user identification: - -```text -{ - token_type: 'Bearer', - expires_in: '120', - id_token: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IjgyN0Q5QTNFOTg2MTY0OTVBQzZGRTE3MUFFNkRBM0IzQ0ExNDE5MjEifQ.eyJpc3MiOiJodHRwczovL25hdGFsaWEtZGV2LXRlc3QuY3JpaXB0by5pbyIsImF1ZCI6InVybjpjaWJhIiwiaWRlbnRpdHlzY2hlbWUiOiJzZWJhbmtpZCIsImF1dGhlbnRpY2F0aW9udHlwZSI6InVybjpncm46YXV0aG46c2U6YmFua2lkIiwiYXV0aGVudGljYXRpb25tZXRob2QiOiJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpTb2Z0d2FyZVBLSSIsImF1dGhlbnRpY2F0aW9uaW5zdGFudCI6IjIwMjQtMDUtMjJUMTA6NDE6NDEuMzYxWiIsIm5hbWVpZGVudGlmaWVyIjoiYmI5YzIzNjRkZmFlNDRmM2JjZmQ5OTkwNTNkOTRmOWUiLCJzdWIiOiJ7YmI5YzIzNjQtZGZhZS00NGYzLWJjZmQtOTk5MDUzZDk0ZjllfSIsInNlc3Npb25pbmRleCI6ImI2NWYxMWI2LWViYTctNDg4Mi05MDBhLTVmMmQ5M2EzOGRiYSIsInNzbiI6IjE5NjgwMjAyMDU3NSIsIm5hbWUiOiJUZXJuZSBQYXVsc2VuIiwiZ2l2ZW5uYW1lIjoiVGVybmUiLCJnaXZlbl9uYW1lIjoiVGVybmUiLCJzdXJuYW1lIjoiUGF1bHNlbiIsImZhbWlseV9uYW1lIjoiUGF1bHNlbiIsImlwYWRkcmVzcyI6IjE4NS4xNTcuMTM0LjEzNCIsImNvdW50cnkiOiJTRSIsImlhdCI6MTcxNjM3NDUwMSwibmJmIjoxNzE2Mzc0NTAxLCJleHAiOjE3MTYzNzU3MDF9.RVQnlukfoH597uXzE1Gays5DElGzAr8xgOmi7ZWppaL3QPGhV4vK2o6qLhxXg_-FKG9xCwHR6gEhnNzWA3W3B6Q2zJeQTYh9okUvTmmhAFIyDL7lEtfWVVKUKvauDisYVZDjAxJQS_1zbgPEi5I-UJ6_kvMGH-wC13MAD2bZGTGR2dR-ZevBUn7plOt0PKXrIZD3vwxDfebTMPQqX_9SNT5F7GLjCcpeVK-T5LOgmUMFcTAbHvNyklqP5ymRHsZLDw_ib4I7ZqODhR-3uISWo1NvG4Y84iBcqv50WRNlmMUm004LfPw1flM5DNsVyUWCqYW8m7eBEwLp5va-6OQG4w', - access_token: 'cf1ce646-7fbe-4740-9c56-fe3f0891f6c6' -} -``` - -## Authenticate with Headless - -Headless flow is a custom protocol provided by Idura that is very similar to CIBA but with a few key differences. - -Where CIBA requires that the user is known in advance so that a request may be sent directly to the authentication device, headless instead returns values to the relying party so that they may trigger necessary UI or interaction elements with the user directly. - -Headless is an extension to OpenID Connect. However, unlike in other OpenID Connect flows, there is a direct communication between your application and the OpenID Provider (Idura Verify), without redirects through the user's browser. - -Headless supports use cases not covered by other OpenID Connect flows, such as: - -- Rendering UI elements in your own webpage instead of via an Idura redirect -- Performing app2app where a browser is not required - -Idura Verify currently supports Headless for Swedish BankID. - -### Example request - -The client application sends an `HTTP POST` request to the backchannel authentication endpoint to ask for end-user authentication. -_The Headless flow is only available for confidential clients, and the example below uses [private key JWTs](/verify/guides/privatekey-jwt/) for client authentication._ - -```text -HTTP POST https://YOUR_SUBDOMAIN.idura.broker/headless/authorize -Content-Type: application/x-www-form-urlencoded - -scope=openid -&acr_values=urn:grn:authn:se:bankid -&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer -&client_assertion=YOUR_JWT_ASSERTION -``` - -### Example response - -A successful response will contain the request id, `headless_req_id`: - -```text -HTTP/1.1 200 OK -Content-Type: application/json -{ - "headless_req_id" : "3857f8ff-21b9-48ae-a732-a3bd8128a7ae", - "expires_in" : 120 - "acr_data": {... e-ID specific metadata ...} -} -``` - -### Polling +### Authorization Code flow -Poll the token endpoint (`/oauth2/token`) providing the `headless_req_id`: +**Intended for:** Traditional server-side web applications (e.g., ASP.NET, Node.js/Express, PHP). -```text -HTTP POST https://YOUR_DOMAIN.idura.broker/oauth2/token -Content-Type: application/x-www-form-urlencoded +**How it works:** Server-side web applications ([confidential clients](/verify/reference/glossary/#confidential-clients)) can securely store a [client secret](/verify/reference/glossary/#client-secret) and establish back-channel communication with Idura Verify. +This facilitates a secure [code-for-token exchange](/verify/reference/glossary/#code-exchange) during authentication. _(Note: While this flow is fully supported, upgrading to PKCE is recommended)._ -headless_req_id=HEADLESS_REQ_ID -&grant_type=urn:grn:params:grant-type:headless -&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer -&client_assertion=$client_assertion -``` +[Authorization Code flow implementation details](/verify/reference/authorization-flows/authorization-code-flow) -#### Pending response +### PKCE flow -While authentication is pending (non-normative): +**Intended for:** Single-page applications, native mobile apps (iOS/Android), **and** traditional server-side web applications. -```text -HTTP/1.1 400 Bad Request -Content-Type: application/json -{ - "error": "authorization_pending", - "error_description": "awaiting first user interaction", - "acr_data": {... e-ID specific metadata ...} -} -``` +**How it works:** PKCE (Proof Key for Code Exchange, pronounced _pixy_) is an extension to the standard [Authorization Code flow](/verify/reference/authorization-flows/authorization-code-flow). +It requires your app to dynamically generate a unique, one-time cryptographic secret for every login request, then use it during the [code exchange](/verify/reference/glossary/#code-exchange). +While originally designed for [public clients](/verify/reference/glossary/#public-clients) that cannot securely store a client secret, +the industry [best practices](/verify/how-it-works/best-security-practices/#authorization-code-flow) now recommend PKCE for _all_ applications due to its enhanced security features. -#### Successful token response +[PKCE flow implementation details](/verify/reference/authorization-flows/pkce) -The token will be issued upon successful user identification: +### CIBA flow -```text -HTTP/1.1 200 OK -Content-Type: application/json -{ - token_type: 'Bearer', - expires_in: '120', - id_token: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IjgyN0Q5QTNFOTg2MTY0OTVBQzZGRTE3MUFFNkRBM0IzQ0ExNDE5MjEifQ.eyJpc3MiOiJodHRwczovL25hdGFsaWEtZGV2LXRlc3QuY3JpaXB0by5pbyIsImF1ZCI6InVybjpjaWJhIiwiaWRlbnRpdHlzY2hlbWUiOiJzZWJhbmtpZCIsImF1dGhlbnRpY2F0aW9udHlwZSI6InVybjpncm46YXV0aG46c2U6YmFua2lkIiwiYXV0aGVudGljYXRpb25tZXRob2QiOiJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpTb2Z0d2FyZVBLSSIsImF1dGhlbnRpY2F0aW9uaW5zdGFudCI6IjIwMjQtMDUtMjJUMTA6NDE6NDEuMzYxWiIsIm5hbWVpZGVudGlmaWVyIjoiYmI5YzIzNjRkZmFlNDRmM2JjZmQ5OTkwNTNkOTRmOWUiLCJzdWIiOiJ7YmI5YzIzNjQtZGZhZS00NGYzLWJjZmQtOTk5MDUzZDk0ZjllfSIsInNlc3Npb25pbmRleCI6ImI2NWYxMWI2LWViYTctNDg4Mi05MDBhLTVmMmQ5M2EzOGRiYSIsInNzbiI6IjE5NjgwMjAyMDU3NSIsIm5hbWUiOiJUZXJuZSBQYXVsc2VuIiwiZ2l2ZW5uYW1lIjoiVGVybmUiLCJnaXZlbl9uYW1lIjoiVGVybmUiLCJzdXJuYW1lIjoiUGF1bHNlbiIsImZhbWlseV9uYW1lIjoiUGF1bHNlbiIsImlwYWRkcmVzcyI6IjE4NS4xNTcuMTM0LjEzNCIsImNvdW50cnkiOiJTRSIsImlhdCI6MTcxNjM3NDUwMSwibmJmIjoxNzE2Mzc0NTAxLCJleHAiOjE3MTYzNzU3MDF9.RVQnlukfoH597uXzE1Gays5DElGzAr8xgOmi7ZWppaL3QPGhV4vK2o6qLhxXg_-FKG9xCwHR6gEhnNzWA3W3B6Q2zJeQTYh9okUvTmmhAFIyDL7lEtfWVVKUKvauDisYVZDjAxJQS_1zbgPEi5I-UJ6_kvMGH-wC13MAD2bZGTGR2dR-ZevBUn7plOt0PKXrIZD3vwxDfebTMPQqX_9SNT5F7GLjCcpeVK-T5LOgmUMFcTAbHvNyklqP5ymRHsZLDw_ib4I7ZqODhR-3uISWo1NvG4Y84iBcqv50WRNlmMUm004LfPw1flM5DNsVyUWCqYW8m7eBEwLp5va-6OQG4w', - access_token: 'cf1ce646-7fbe-4740-9c56-fe3f0891f6c6' -} -``` +**Intended for:** Call centers or point-of-sale terminals. -## Validate the JWT +**How it works:** Client-Initiated Backchannel Authentication (CIBA) is a [decoupled flow](/verify/reference/glossary/#decoupled-authentication-flow), meaning the device initiating the login is different from the device where the user actually authenticates. +An agent initiates the login from their terminal, and Idura Verify sends a push notification directly to the user's smartphone (e.g., via the Swedish BankID or Norwegian BankID app) asking them to approve the request. No browser redirects are involved. -You can now proceed with validating the `JWT` returned in response and access the [end-user information it contains](/verify/reference/token-contents/). -The validation step is required; without it, you cannot trust the contained end-user information. +[CIBA flow implementation details](/verify/reference/authorization-flows/ciba) -We strongly recommend that you find a battle-hardened library for your specific platform to do this heavy lifting. -You can find an extensive list of libraries on jwt.io. Alternatively, Idura provides a list of [integrations](/verify/integrations) that handle most of the authentication process, including JWT validation. +### Headless flow -For more information, see [OpenID Connect security best practices](/verify/how-it-works/best-security-practices/#always-validating-id-tokens) and JWT validation guide. +**Intended for:** Use cases not covered by other OpenID Connect flows, such as rendering UI elements in your own webpage instead of via an Idura redirect, or performing app2app without a browser. -## Code exchange authorization examples +**How it works:** The Headless flow is a custom protocol provided by Idura. It allows interacting with eIDs that offer a poll-based workflow, where a user agent redirect is not required. -```php -# PHP -"Authorization" => "Basic ".base64_encode(urlencode(IDURA_CLIENT_ID).":".urlencode(IDURA_SECRET)) -``` +[Headless flow implementation details](/verify/reference/authorization-flows/headless) -```javascript -// Node.js -'Authorization': "Basic " + Buffer.from(`${encodeURIComponent(IDURA_CLIENT_ID)}:${encodeURIComponent(IDURA_SECRET)}`).toString('base64') +### Implicit flow (legacy) -// Javascript -'Authorization': "Basic " + btoa(`${encodeURIComponent(IDURA_CLIENT_ID)}:${encodeURIComponent(IDURA_SECRET)}`) -``` +**Intended for:** Debugging only. -```csharp -// C# -using System; -using System.Text; -"Basic " + Convert.ToBase64String(Encoding.ASCII.GetBytes(Uri.EscapeDataString(IDURA_CLIENT_ID) + ":" + Uri.EscapeDataString(IDURA_SECRET))) +**How it works:** The Implicit flow returns the ID token directly in the browser's URL fragment. Support for this flow is being discontinued as it does not satisfy modern security standards. +It remains available for use in test domains for simpler debugging during development. -// C# - if you have a dependency on System.Web.dll or you are willing to take it -using System; -using System.Text; -using System.Web.Security.AntiXss; -"Basic " + Convert.ToBase64String(Encoding.ASCII.GetBytes(AntiXssEncoder.UrlEncode(IDURA_CLIENT_ID) + ":" + AntiXssEncoder.UrlEncode(IDURA_SECRET))) -``` +[Implicit flow implementation details](/verify/reference/authorization-flows/implicit-flow) diff --git a/src/pages/verify/integrations/auth0.mdx b/src/pages/verify/integrations/auth0.mdx index ee1e1a52..8b43d031 100644 --- a/src/pages/verify/integrations/auth0.mdx +++ b/src/pages/verify/integrations/auth0.mdx @@ -25,7 +25,7 @@ Once the application is created, you'll need some of its details for configuring - **Client ID** to identify your Auth0 tenant to Idura Verify. We chose `urn:idura:samples:auth0` for this example. - **Domain** on which you will be communicating with Idura Verify. If your company name is Acme Corp, it could be, for example, `acme-corp.idura.broker`. -- **Client secret** is needed if you choose the [back-channel](/verify/getting-started/oidc-intro/#authenticate-with-back-channel-authorization-code-flow) approach. The secret is generated when you [configure OAuth2 Code Flow](/verify/integrations/auth0/#configure-oauth2-code-flow) for your application. +- **Client secret** is needed if you choose the [back-channel](/verify/how-it-works/oidc-intro/#authorization-code-flow) approach. The secret is generated when you [configure OAuth2 Code Flow](/verify/integrations/auth0/#configure-oauth2-code-flow) for your application. ## Configure OAuth2 Code Flow diff --git a/src/pages/verify/integrations/firebase.mdx b/src/pages/verify/integrations/firebase.mdx index f75685dc..21c13511 100644 --- a/src/pages/verify/integrations/firebase.mdx +++ b/src/pages/verify/integrations/firebase.mdx @@ -43,7 +43,7 @@ Alternatively, you can follow Firebase documentation for [web applications](http Click **Add new provider**, then select **OpenID Connect**. ![OIDC custom provider](./firebase/OIDC-custom-provider.png) 3. You will now enable and define a new OIDC provider using the [information from your Idura Dashboard and the Client Secret you previously saved](#firebase-application-and-oidc-provider-setup). -4. Choose the **Grant type** you intend to use. We recommend choosing **Code flow**. _For more information, see [OpenID Connect best security practices](/verify/how-it-works/best-security-practices/#authorization-code-flow)._ +4. Choose the **Grant type** you intend to use. We recommend choosing **Code flow**. _For more information, see [OpenID Connect security best practices](/verify/how-it-works/best-security-practices/#authorization-code-flow)._ 5. Enter a provider name – this is entirely up to you. We used "Idura" in this example. 6. Enter your Idura Application **Client ID** in the **Client ID** field. 7. Enter your Idura domain in the **Issuer (URL)** field (e.g., `https://acme-corp.idura.broker`). diff --git a/src/pages/verify/integrations/onelogin.mdx b/src/pages/verify/integrations/onelogin.mdx index 43e896d1..65199263 100644 --- a/src/pages/verify/integrations/onelogin.mdx +++ b/src/pages/verify/integrations/onelogin.mdx @@ -21,7 +21,7 @@ First, you must create a new [application](/verify/getting-started/basics/#appli Once the application is created, you'll need some of its details for configuring OneLogin to communicate with Idura Verify. Gather the following information from the application settings: - **Client ID** to identify your OneLogin tenant to Idura Verify. We chose `urn:idura:samples:onelogin` for this example. -- **Client secret** is needed if you choose the [back-channel](/verify/getting-started/oidc-intro/#authenticate-with-back-channel-authorization-code-flow) approach. The secret is generated when you [configure OAuth2 Code Flow](/verify/integrations/onelogin/#configure-oauth2-code-flow) for your application. +- **Client secret** is needed if you choose the [back-channel](/verify/how-it-works/oidc-intro/#authorization-code-flow) approach. The secret is generated when you [configure OAuth2 Code Flow](/verify/integrations/onelogin/#configure-oauth2-code-flow) for your application. - **Domain** on which you will be communicating with Idura Verify. If your company name is Acme Corp, it could be, for example, `acme-corp.idura.broker`. ## Configure OAuth2 Code Flow diff --git a/src/pages/verify/reference/authorization-flows/authorization-code-flow.mdx b/src/pages/verify/reference/authorization-flows/authorization-code-flow.mdx new file mode 100644 index 00000000..551e26b1 --- /dev/null +++ b/src/pages/verify/reference/authorization-flows/authorization-code-flow.mdx @@ -0,0 +1,154 @@ +--- +product: verify +subcategory: Authorization flows +title: Authorization Code flow +subtitle: A backchannel flow designed for traditional server-based web applications. +order: 1 +--- + +# Protocol overview + +Authorization Code flow is designed for traditional server-based web applications, also referred to as [confidential clients](/verify/reference/glossary/#confidential-client). +These applications can securely store a client secret and establish back-channel communication with the [Authorization Server](/verify/reference/glossary/#authorization-server) (Idura Verify). +This facilitates a secure [code-for-token exchange](/verify/reference/authorization-flows/authorization-code-flow/#step-2-exchange-the-code-for-token) during authentication. + +![Authorization Code flow diagram](../../guides/images/authorization_code_flow_diagram.png) + +## Step 1: Build authorize URL + +To start the login flow, send the user to the OAuth2 authorization endpoint (`/oauth2/authorize`) with the appropriate set of [query parameters](/verify/reference/request-parameters/#authorization-endpoint). + +### Example request + +```http +GET https://YOUR_SUBDOMAIN.idura.broker/oauth2/authorize? + response_type=code& + response_mode=query& + client_id=CLIENT_ID& + redirect_uri=YOUR_REDIRECT_URL& + acr_values=CHOSEN_IDENTITY_SERVICE& + scope=openid& + state=YOUR_STATE +``` + +- `response_type=code`: Indicates that you are using the Authorization Code flow and that you want to receive an authorization code in the response. +- `response_mode=query`: Indicates that the authorization code should be returned in the query string of your redirect URI. +- `client_id`: The client ID of your [Idura Verify application](/verify/how-it-works/core-concepts/#applications). +- `redirect_uri`: The URL to which the user will be redirected after authentication. This must match one of the redirect URIs registered for your application in your Idura Dashboard. +- `acr_values`: The specific eID method you want to trigger, e.g. `urn:grn:authn:dk:mitid:substantial` for Danish MitID. +- `scope`: The scopes you want to request. Must always include `openid`, and can include additional scopes depending on the user data you want to receive in the token. +- `state`: A random alphanumeric string you generate. This exact value will be returned [in the response](/verify/reference/authorization-flows/authorization-code-flow/#example-response) and can be used to verify that the response corresponds to the request you sent, protecting your app from cross-site request forgery (CSRF) attacks. + + + Use our [Authorize URL Builder](/verify/guides/authorize-url-builder/) to generate authorize URLs + with your chosen parameters. For a full list of supported parameters, see [Authorization request + parameters reference](/verify/reference/request-parameters/#authorization-endpoint). + + +
+ +import UsePARSnippet from '../../../../snippets/use-par.mdx'; + + + +### Example response + +You will receive an HTTP 302 response which redirects your browser to your specified `redirect_uri` with the authorization code included in the query parameters: + +```http +HTTP/1.1 302 Found +Location: YOUR_REDIRECT_URL?code=AUTHORIZATION_CODE&state=YOUR_STATE +``` + +If the authentication request fails, the redirect will include an error code instead: + +```http +HTTP/1.1 302 Found +Location: YOUR_REDIRECT_URL?error=ERROR_CODE&error_description=...&state=YOUR_STATE +``` + +## Step 2: Exchange the code for token + +Next, you must exchange the returned code for an actual token. This is done by posting the authorization code received from the previous step to the [token endpoint](/verify/reference/glossary/#token-endpoint) (`/oauth2/token`). + +You must format this request as an HTML form (`application/x-www-form-urlencoded`) and send the credentials in the `Authorization` HTTP header in `Basic` format. +You must also x-www-form-urlencode the values of the `CLIENT_ID` and `CLIENT_SECRET` before constructing the `Authorization` header. + +```http +HTTP POST https://YOUR_SUBDOMAIN.idura.broker/oauth2/token +Content-Type: application/x-www-form-urlencoded +Authorization: Basic BASE64(xWwwFormUrlEncode(CLIENT_ID):xWwwFormUrlEncode(CLIENT_SECRET)) + +grant_type=authorization_code& +code=AUTHORIZATION_CODE& +redirect_uri=YOUR_REDIRECT_URL +``` + + + +Idura Verify also supports receiving the client credentials in the `POST` request body, but this usage is [discouraged by the OAuth2 specification](https://datatracker.ietf.org/doc/html/rfc6749#section-2.3.1). +We strongly recommend that you send the credentials in the `Authorization: Basic ...` HTTP header value as shown above. + + + +The `client_id`, `client_secret` and `redirect_uri` must be exactly the same as you used in the [authorization request](/verify/reference/authorization-flows/authorization-code-flow/#1-initiate-authentication). + + + +Because exchanging the authorization code requires your static `client_secret` (which acts as a password), this HTTP POST request must always be made from your backend server. +Never include your client secret in the frontend code. + +If you are building a frontend application that cannot safely store a secret, you must use the [PKCE flow](/verify/reference/authorization-flows/pkce) instead. +With PKCE, the secret is generated on the fly for each authorization request, and no special handling is required. + + + +### Code exchange authorization examples + +```php +# PHP +"Authorization" => "Basic ".base64_encode(urlencode(IDURA_CLIENT_ID).":".urlencode(IDURA_SECRET)) +``` + +```javascript +// Node.js +'Authorization': "Basic " + Buffer.from(`${encodeURIComponent(IDURA_CLIENT_ID)}:${encodeURIComponent(IDURA_SECRET)}`).toString('base64') + +// Javascript +'Authorization': "Basic " + btoa(`${encodeURIComponent(IDURA_CLIENT_ID)}:${encodeURIComponent(IDURA_SECRET)}`) +``` + +```csharp +// C# +using System; +using System.Text; +"Basic " + Convert.ToBase64String(Encoding.ASCII.GetBytes(Uri.EscapeDataString(IDURA_CLIENT_ID) + ":" + Uri.EscapeDataString(IDURA_SECRET))) + +// C# - if you have a dependency on System.Web.dll or you are willing to take it +using System; +using System.Text; +using System.Web.Security.AntiXss; +"Basic " + Convert.ToBase64String(Encoding.ASCII.GetBytes(AntiXssEncoder.UrlEncode(IDURA_CLIENT_ID) + ":" + AntiXssEncoder.UrlEncode(IDURA_SECRET))) +``` + +### Example response + +A successful request to the token endpoint will return an `HTTP 200 OK` along with a JSON payload with your tokens. +The `id_token` contains the authenticated user's identity information. + +```http +HTTP/1.1 200 OK +Content-Type: application/json + +{ + "id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IjgyN0Q5QTNFOTg2MTY0OTVBQzZGRTE3MUFFNkRBM0IzQ0ExNDE5MjEifQ.eyJpc3MiOiJodHRwczovL25hdGFsaWEtZGV2LXRlc3QuY3JpaXB0by5pbyIsImF1ZCI6InVybjpjaWJhIiwiaWRlbnRpdHlzY2hlbWUiOiJzZWJhbmtpZCIsImF1dGhlbnRpY2F0aW9udHlwZSI6InVybjpncm46YXV0aG46c2U6YmFua2lkIiwiYXV0aGVudGljYXRpb25tZXRob2QiOiJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpTb2Z0d2FyZVBLSSIsImF1dGhlbnRpY2F0aW9uaW5zdGFudCI6IjIwMjQtMDUtMjJUMTA6NDE6NDEuMzYxWiIsIm5hbWVpZGVudGlmaWVyIjoiYmI5YzIzNjRkZmFlNDRmM2JjZmQ5OTkwNTNkOTRmOWUiLCJzdWIiOiJ7YmI5YzIzNjQtZGZhZS00NGYzLWJjZmQtOTk5MDUzZDk0ZjllfSIsInNlc3Npb25pbmRleCI6ImI2NWYxMWI2LWViYTctNDg4Mi05MDBhLTVmMmQ5M2EzOGRiYSIsInNzbiI6IjE5NjgwMjAyMDU3NSIsIm5hbWUiOiJUZXJuZSBQYXVsc2VuIiwiZ2l2ZW5uYW1lIjoiVGVybmUiLCJnaXZlbl9uYW1lIjoiVGVybmUiLCJzdXJuYW1lIjoiUGF1bHNlbiIsImZhbWlseV9uYW1lIjoiUGF1bHNlbiIsImlwYWRkcmVzcyI6IjE4NS4xNTcuMTM0LjEzNCIsImNvdW50cnkiOiJTRSIsImlhdCI6MTcxNjM3NDUwMSwibmJmIjoxNzE2Mzc0NTAxLCJleHAiOjE3MTYzNzU3MDF9.RVQnlukfoH597uXzE1Gays5DElGzAr8xgOmi7ZWppaL3QPGhV4vK2o6qLhxXg_-FKG9xCwHR6gEhnNzWA3W3B6Q2zJeQTYh9okUvTmmhAFIyDL7lEtfWVVKUKvauDisYVZDjAxJQS_1zbgPEi5I-UJ6_kvMGH-wC13MAD2bZGTGR2dR-ZevBUn7plOt0PKXrIZD3vwxDfebTMPQqX_9SNT5F7GLjCcpeVK-T5LOgmUMFcTAbHvNyklqP5ymRHsZLDw_ib4I7ZqODhR-3uISWo1NvG4Y84iBcqv50WRNlmMUm004LfPw1flM5DNsVyUWCqYW8m7eBEwLp5va-6OQG4w", + "access_token": "cf1ce646-7fbe-4740-9c56-fe3f0891f6c6", + "token_type": "N_A", +} +``` + +## Step 3: Validate the JWT + +import ValidateJWTSnippet from '../../../../snippets/validate-jwt.mdx'; + + diff --git a/src/pages/verify/reference/authorization-flows/ciba.mdx b/src/pages/verify/reference/authorization-flows/ciba.mdx new file mode 100644 index 00000000..d1cb1cfb --- /dev/null +++ b/src/pages/verify/reference/authorization-flows/ciba.mdx @@ -0,0 +1,85 @@ +--- +product: verify +subcategory: Authorization flows +title: CIBA flow +subtitle: A backchannel authentication flow that does not rely on browser redirects, making it suitable for use cases such as caller authentication. +order: 3 +--- + +## Protocol overview + +Client-Initiated Backchannel Authentication (CIBA) is a new authentication flow where the client application initiates the authentication process on behalf of the end-user. + +The client application provides a hint that triggers an authentication request to the user's authentication device, typically a phone. +The user can then approve the request and complete the authentication process as usual. + +CIBA is an extension to OpenID Connect. However, unlike in other OpenID Connect flows, there is a direct communication between your application and the OpenID Provider (Idura Verify), without redirects through the user's browser. + +CIBA supports use cases not covered by other OpenID Connect flows, such as: + +- Confirming a user's identity to a call center agent during a phone call or via a chat. +- Using a smartphone to authorize a payment at a point of sale terminal. +- Enabling a bank agent to authenticate a customer in a bank branch during a face-to-face interaction. + +Idura Verify supports CIBA for the [Swedish BankID Phone Authentication](/verify/guides/caller-authentication/#caller-authentication-with-swedish-bankid), the [Norwegian BankID Caller Authentication](/verify/guides/caller-authentication/#caller-authentication-with-norwegian-bankid) and the [Caller Authentication with FrejaID](/verify/guides/caller-authentication/#caller-authentication-with-frejaid). + +![CIBA flow](../../guides/images/ciba-sequence.png) + +## Initiating authentication + +The client application shall make an `HTTP POST` request to the backchannel authentication endpoint to ask for end-user authentication. + +### Example request + +_The CIBA flow is only available for [confidential clients](/verify/reference/glossary/#confidential-client), and the example below uses [private key JWTs](/verify/guides/privatekey-jwt/) for client authentication._ + +```http +HTTP POST https://YOUR_SUBDOMAIN.idura.broker/ciba/bc-authorize +Content-Type: application/x-www-form-urlencoded + +scope=openid +&login_hint=sub:ssn:SSN +&acr_values=urn:grn:authn:se:bankid +&binding_message=BINDING_MESSAGE +&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer +&client_assertion=YOUR_JWT_ASSERTION +authorization_details=%5B%7B%22type%22%3A%22phoneAuth%22%2C%22callInitiator%22%3A%22RP%22%7D%5D +``` + +### Example response + +A successful response will contain the request id, `auth_req_id`: + +```http +{ + "auth_req_id" : "3857f8ff-21b9-48ae-a732-a3bd8128a7ae", + "expires_in" : 120 +} +``` + +## Obtaining the token + +Poll the token endpoint (`/oauth2/token`) providing the `auth_req_id`: + +```http +HTTP POST https://YOUR_DOMAIN.idura.broker/oauth2/token +Content-Type: application/x-www-form-urlencoded + +auth_req_id=AUTH_REQ_ID +&grant_type=urn:openid:params:grant-type:ciba +&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer +&client_assertion=$client_assertion +``` + +### Token response + +The token will be issued upon successful user identification: + +```http +{ + "token_type": "Bearer", + "expires_in": 120, + "id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IjgyN0Q5QTNFOTg2MTY0OTVBQzZGRTE3MUFFNkRBM0IzQ0ExNDE5MjEifQ.eyJpc3MiOiJodHRwczovL25hdGFsaWEtZGV2LXRlc3QuY3JpaXB0by5pbyIsImF1ZCI6InVybjpjaWJhIiwiaWRlbnRpdHlzY2hlbWUiOiJzZWJhbmtpZCIsImF1dGhlbnRpY2F0aW9udHlwZSI6InVybjpncm46YXV0aG46c2U6YmFua2lkIiwiYXV0aGVudGljYXRpb25tZXRob2QiOiJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpTb2Z0d2FyZVBLSSIsImF1dGhlbnRpY2F0aW9uaW5zdGFudCI6IjIwMjQtMDUtMjJUMTA6NDE6NDEuMzYxWiIsIm5hbWVpZGVudGlmaWVyIjoiYmI5YzIzNjRkZmFlNDRmM2JjZmQ5OTkwNTNkOTRmOWUiLCJzdWIiOiJ7YmI5YzIzNjQtZGZhZS00NGYzLWJjZmQtOTk5MDUzZDk0ZjllfSIsInNlc3Npb25pbmRleCI6ImI2NWYxMWI2LWViYTctNDg4Mi05MDBhLTVmMmQ5M2EzOGRiYSIsInNzbiI6IjE5NjgwMjAyMDU3NSIsIm5hbWUiOiJUZXJuZSBQYXVsc2VuIiwiZ2l2ZW5uYW1lIjoiVGVybmUiLCJnaXZlbl9uYW1lIjoiVGVybmUiLCJzdXJuYW1lIjoiUGF1bHNlbiIsImZhbWlseV9uYW1lIjoiUGF1bHNlbiIsImlwYWRkcmVzcyI6IjE4NS4xNTcuMTM0LjEzNCIsImNvdW50cnkiOiJTRSIsImlhdCI6MTcxNjM3NDUwMSwibmJmIjoxNzE2Mzc0NTAxLCJleHAiOjE3MTYzNzU3MDF9.RVQnlukfoH597uXzE1Gays5DElGzAr8xgOmi7ZWppaL3QPGhV4vK2o6qLhxXg_-FKG9xCwHR6gEhnNzWA3W3B6Q2zJeQTYh9okUvTmmhAFIyDL7lEtfWVVKUKvauDisYVZDjAxJQS_1zbgPEi5I-UJ6_kvMGH-wC13MAD2bZGTGR2dR-ZevBUn7plOt0PKXrIZD3vwxDfebTMPQqX_9SNT5F7GLjCcpeVK-T5LOgmUMFcTAbHvNyklqP5ymRHsZLDw_ib4I7ZqODhR-3uISWo1NvG4Y84iBcqv50WRNlmMUm004LfPw1flM5DNsVyUWCqYW8m7eBEwLp5va-6OQG4w", + "access_token": "cf1ce646-7fbe-4740-9c56-fe3f0891f6c6" +} +``` diff --git a/src/pages/verify/reference/authorization-flows/headless.mdx b/src/pages/verify/reference/authorization-flows/headless.mdx new file mode 100644 index 00000000..b3b97fde --- /dev/null +++ b/src/pages/verify/reference/authorization-flows/headless.mdx @@ -0,0 +1,93 @@ +--- +product: verify +subcategory: Authorization flows +title: Headless flow +subtitle: A custom protocol provided by Idura. It allows interacting with eIDs that offer a poll-based workflow, where a user agent redirect is not required. +order: 4 +--- + +## Protocol overview + +The Headless flow is an extension to OpenID Connect. The flow is very similar to [CIBA](/verify/reference/authorization-flows/ciba/) but with a few key differences. + +Just like in CIBA (and different from other OpenID Connect flows), there is a direct communication between your application and the OpenID Provider (Idura Verify), without redirects through the user's browser. +However, CIBA requires that the user is known in advance so that a request may be sent directly to the authentication device. The Headless flow returns values to the relying party instead, so that they may trigger the necessary UI or interaction elements with the user directly. + +Headless supports use cases not covered by other OpenID Connect flows, such as: + +- Rendering UI elements in your own webpage instead of via an Idura redirect +- Performing app2app where a browser is not required + +Idura Verify currently supports Headless Authentication for the [Swedish BankID](/verify/e-ids/swedish-bankid/#bankid-headless-authentication). + +## Example request + +The client application sends an `HTTP POST` request to the backchannel authentication endpoint to ask for end-user authentication. +_The Headless flow is only available for confidential clients, and the example below uses [private key JWTs](/verify/guides/privatekey-jwt/) for client authentication._ + +```http +HTTP POST https://YOUR_SUBDOMAIN.idura.broker/headless/authorize +Content-Type: application/x-www-form-urlencoded + +scope=openid +&acr_values=urn:grn:authn:se:bankid +&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer +&client_assertion=YOUR_JWT_ASSERTION +``` + +## Example response + +A successful response will contain the request id, `headless_req_id`: + +```http +HTTP/1.1 200 OK +Content-Type: application/json +{ + "headless_req_id" : "3857f8ff-21b9-48ae-a732-a3bd8128a7ae", + "expires_in" : 120, + "acr_data": {... e-ID specific metadata ...} +} +``` + +## Polling + +Poll the token endpoint (`/oauth2/token`) providing the `headless_req_id`: + +```http +HTTP POST https://YOUR_DOMAIN.idura.broker/oauth2/token +Content-Type: application/x-www-form-urlencoded + +headless_req_id=HEADLESS_REQ_ID +&grant_type=urn:grn:params:grant-type:headless +&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer +&client_assertion=$client_assertion +``` + +### Pending response + +While authentication is pending (non-normative): + +```http +HTTP/1.1 400 Bad Request +Content-Type: application/json +{ + "error": "authorization_pending", + "error_description": "awaiting first user interaction", + "acr_data": {... e-ID specific metadata ...} +} +``` + +### Successful token response + +The token will be issued upon successful user identification: + +```http +HTTP/1.1 200 OK +Content-Type: application/json +{ + "token_type": "Bearer", + "expires_in": 120, + "id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IjgyN0Q5QTNFOTg2MTY0OTVBQzZGRTE3MUFFNkRBM0IzQ0ExNDE5MjEifQ.eyJpc3MiOiJodHRwczovL25hdGFsaWEtZGV2LXRlc3QuY3JpaXB0by5pbyIsImF1ZCI6InVybjpjaWJhIiwiaWRlbnRpdHlzY2hlbWUiOiJzZWJhbmtpZCIsImF1dGhlbnRpY2F0aW9udHlwZSI6InVybjpncm46YXV0aG46c2U6YmFua2lkIiwiYXV0aGVudGljYXRpb25tZXRob2QiOiJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpTb2Z0d2FyZVBLSSIsImF1dGhlbnRpY2F0aW9uaW5zdGFudCI6IjIwMjQtMDUtMjJUMTA6NDE6NDEuMzYxWiIsIm5hbWVpZGVudGlmaWVyIjoiYmI5YzIzNjRkZmFlNDRmM2JjZmQ5OTkwNTNkOTRmOWUiLCJzdWIiOiJ7YmI5YzIzNjQtZGZhZS00NGYzLWJjZmQtOTk5MDUzZDk0ZjllfSIsInNlc3Npb25pbmRleCI6ImI2NWYxMWI2LWViYTctNDg4Mi05MDBhLTVmMmQ5M2EzOGRiYSIsInNzbiI6IjE5NjgwMjAyMDU3NSIsIm5hbWUiOiJUZXJuZSBQYXVsc2VuIiwiZ2l2ZW5uYW1lIjoiVGVybmUiLCJnaXZlbl9uYW1lIjoiVGVybmUiLCJzdXJuYW1lIjoiUGF1bHNlbiIsImZhbWlseV9uYW1lIjoiUGF1bHNlbiIsImlwYWRkcmVzcyI6IjE4NS4xNTcuMTM0LjEzNCIsImNvdW50cnkiOiJTRSIsImlhdCI6MTcxNjM3NDUwMSwibmJmIjoxNzE2Mzc0NTAxLCJleHAiOjE3MTYzNzU3MDF9.RVQnlukfoH597uXzE1Gays5DElGzAr8xgOmi7ZWppaL3QPGhV4vK2o6qLhxXg_-FKG9xCwHR6gEhnNzWA3W3B6Q2zJeQTYh9okUvTmmhAFIyDL7lEtfWVVKUKvauDisYVZDjAxJQS_1zbgPEi5I-UJ6_kvMGH-wC13MAD2bZGTGR2dR-ZevBUn7plOt0PKXrIZD3vwxDfebTMPQqX_9SNT5F7GLjCcpeVK-T5LOgmUMFcTAbHvNyklqP5ymRHsZLDw_ib4I7ZqODhR-3uISWo1NvG4Y84iBcqv50WRNlmMUm004LfPw1flM5DNsVyUWCqYW8m7eBEwLp5va-6OQG4w", + "access_token": "cf1ce646-7fbe-4740-9c56-fe3f0891f6c6" +} +``` diff --git a/src/pages/verify/reference/authorization-flows/implicit-flow.mdx b/src/pages/verify/reference/authorization-flows/implicit-flow.mdx new file mode 100644 index 00000000..8163403e --- /dev/null +++ b/src/pages/verify/reference/authorization-flows/implicit-flow.mdx @@ -0,0 +1,35 @@ +--- +product: verify +subcategory: Authorization flows +title: Implicit flow +subtitle: A legacy authorization flow no longer recommended for production use due to security vulnerabilities. +order: 5 +--- + +## Protocol overview + +The Implicit flow returns an [ID token](/verify/reference/glossary/#id-token) directly in the browser via the `#` fragment (so there is no code exchange like in the authorization code flow). +This means that the token is exposed to the user agent and potentially to malicious actors, which can lead to token interception or leakage. For this reason, the Implicit flow +[should not be used in production applications](/verify/how-it-works/best-security-practices/#avoid-implicit-flow) and is considered a legacy flow. It remains available in test environment for debugging and testing. + +[@criipto/auth-js](https://github.com/criipto/criipto-auth.js) supports Implicit flow. + +## Example request + +```http +GET https://YOUR_SUBDOMAIN.idura.broker/oauth2/authorize? + response_type=id_token& + response_mode=fragment& + client_id=CLIENT_ID& + redirect_uri=YOUR_REDIRECT_URL& + acr_values=CHOSEN_IDENTITY_SERVICE& + scope=openid& + state=YOUR_STATE +``` + +## Example response + +```http +HTTP/1.1 302 Found +Location: YOUR_REDIRECT_URL#id_token=eyJ[...].eyJ[...].Sfl[...]&state=[...] +``` diff --git a/src/pages/verify/reference/authorization-flows/index.mdx b/src/pages/verify/reference/authorization-flows/index.mdx new file mode 100644 index 00000000..743e2319 --- /dev/null +++ b/src/pages/verify/reference/authorization-flows/index.mdx @@ -0,0 +1,51 @@ +--- +product: verify +title: Authorization flows +subtitle: Overview of the different OpenID Connect authorization flows supported by Idura Verify. +category: Reference +isSubIndex: true +sort: 0 +--- + +import { isIndexPage } from '../../../../utils'; +import { graphql as gatsbyGraphql, Link } from 'gatsby'; + +export const pageQuery = gatsbyGraphql` + query verifyAuthorizationFlows { + pages: allMdx( + filter: { + frontmatter: { + subcategory: { eq: "Authorization flows" } + } + } + sort: {frontmatter: {order: ASC}} + ) { + edges { + node { + __typename + id + fields { + slug + } + frontmatter { + title + subtitle + } + } + } + } + } +`; + +
    + {props.data.pages.edges + .map(edge => edge.node) + .filter(node => !isIndexPage(node)) + .map(node => ( +
  • + {node.frontmatter.title} +
    + {node.frontmatter.subtitle} +
  • + ))} +
diff --git a/src/pages/verify/reference/authorization-flows/pkce.mdx b/src/pages/verify/reference/authorization-flows/pkce.mdx new file mode 100644 index 00000000..5c35863c --- /dev/null +++ b/src/pages/verify/reference/authorization-flows/pkce.mdx @@ -0,0 +1,113 @@ +--- +product: verify +subcategory: Authorization flows +title: PKCE flow +subtitle: An extension to the Authorization Code flow that offers enhanced security against authorization code interception. Initially designed for public clients, PKCE is now the recommended flow for all application types. +order: 2 +--- + +## Protocol overview + +PKCE (Proof Key for Code Exchange) allows you to securely perform a [code-for-token exchange](/verify/reference/glossary/#code-exchange) without a static client secret. +Instead, the client application dynamically generates a unique, one-time secret (a `code_verifier`) for every authentication request. + +Because it protects against code interception attacks, PKCE is the industry-recommended standard for **all** application types, including traditional server-side web apps, single-page applications (SPAs), and native mobile apps. + +_[@criipto/auth-js](https://github.com/criipto/criipto-auth.js) and [@criipto/verify-react](https://github.com/criipto/criipto-verify-react) support PKCE._ + +![PKCE flow diagram](../images/PKCE_flow_diagram.png) + +## Step 1: Build authorize URL + +Before making the authorization request, your application must generate the secrets in the format specified in [RFC 7636](https://datatracker.ietf.org/doc/html/rfc7636#section-4): + +1. **`code_verifier`**: A cryptographically random string between 43 and 128 characters long. +2. **`code_challenge`**: A base64url-encoded SHA-256 hash of the `code_verifier`. + +Similar to the standard Authorization Code flow, you start by redirecting the user to the OAuth2 authorization endpoint (`/oauth2/authorize`), +including the `code_challenge`, `code_challenge_method=S256`, and the rest of the [parameters](/verify/reference/request-parameters/#authorization-endpoint) you would normally include in an authorization request. + +### Example request + +```http +GET https://YOUR_SUBDOMAIN.idura.broker/oauth2/authorize? + response_type=code& + response_mode=query& + client_id=CLIENT_ID& + redirect_uri=YOUR_REDIRECT_URL& + acr_values=CHOSEN_IDENTITY_SERVICE& + scope=openid& + state=YOUR_STATE& + code_challenge=YOUR_CODE_CHALLENGE& + code_challenge_method=S256 +``` + +- `response_type=code`: Indicates that you are using the Authorization Code flow and that you want to receive an authorization code in the response. +- `response_mode=query`: Indicates that the authorization code should be returned in the query string of your redirect URI. +- `client_id`: The client ID of your [Idura Verify application](/verify/how-it-works/core-concepts/#applications). +- `redirect_uri`: The URL to which the user will be redirected after authentication. This must match one of the redirect URIs registered for your application in your Idura Dashboard. +- `acr_values`: The specific eID method you want to trigger, e.g. `urn:grn:authn:dk:mitid:substantial` for Danish MitID. +- `scope`: The scopes you want to request. Must always include `openid`, and can include additional scopes depending on the user data you want to receive in the token. +- `state`: A random alphanumeric string you generate. This exact value will be returned [in the response](/verify/reference/authorization-flows/authorization-code-flow/#example-response) and can be used to verify that the response corresponds to the request you sent, protecting your app from cross-site request forgery (CSRF) attacks. +- `code_challenge`: A SHA-256 hash of your dynamically generated `code_verifier`. +- `code_challenge_method=S256`: Indicates that you are using the SHA-256 method to generate the `code_challenge`. + +_Use our [Authorize URL Builder](/verify/guides/authorize-url-builder/) to generate authorize URLs with various parameters._ +A full reference of all available parameters can be found in the [authorization request parameters reference](/verify/reference/request-parameters/#authorization-endpoint). + +### Example response + +You will receive an `HTTP 302` response, which redirects your browser to your specified `redirect_uri` with the authorization code included in the query parameters: + +```http +HTTP/1.1 302 Found +Location: YOUR_REDIRECT_URL?code=AUTHORIZATION_CODE&state=YOUR_STATE +``` + +If the authentication request fails, the redirect will include an error code instead: + +```http +HTTP/1.1 302 Found +Location: YOUR_REDIRECT_URL?error=ERROR_CODE&error_description=...&state=YOUR_STATE +``` + +## Step 2: Exchange the code for token + +Next, you must exchange the returned code for an actual token. The same as with the standard [Authorization Code flow](/verify/reference/authorization-flows/authorization-code-flow/#step-2-exchange-the-code-for-token), +this is done by posting the authorization code received from the previous step to the [token endpoint](/verify/reference/glossary/#token-endpoint) (`/oauth2/token`). + +The difference is that the static client secret is not included in this request. +Instead, you include a `code_verifier`, which was generated in the previous step. + +```http +HTTP POST https://YOUR_SUBDOMAIN.idura.broker/oauth2/token +Content-Type: application/x-www-form-urlencoded + +grant_type=authorization_code& +code=AUTHORIZATION_CODE& +client_id=CLIENT_ID& +redirect_uri=YOUR_REDIRECT_URL& +code_verifier=YOUR_CODE_VERIFIER +``` + +### Example response + +A successful request to the token endpoint will return an `HTTP 200 OK` along with a JSON payload with your tokens. +The `id_token` contains the authenticated user's identity information. + +```http +HTTP/1.1 200 OK +Content-Type: application/json + +{ + "id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IjgyN0Q5QTNFOTg2MTY0OTVBQzZGRTE3MUFFNkRBM0IzQ0ExNDE5MjEifQ.eyJpc3MiOiJodHRwczovL25hdGFsaWEtZGV2LXRlc3QuY3JpaXB0by5pbyIsImF1ZCI6InVybjpjaWJhIiwiaWRlbnRpdHlzY2hlbWUiOiJzZWJhbmtpZCIsImF1dGhlbnRpY2F0aW9udHlwZSI6InVybjpncm46YXV0aG46c2U6YmFua2lkIiwiYXV0aGVudGljYXRpb25tZXRob2QiOiJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpTb2Z0d2FyZVBLSSIsImF1dGhlbnRpY2F0aW9uaW5zdGFudCI6IjIwMjQtMDUtMjJUMTA6NDE6NDEuMzYxWiIsIm5hbWVpZGVudGlmaWVyIjoiYmI5YzIzNjRkZmFlNDRmM2JjZmQ5OTkwNTNkOTRmOWUiLCJzdWIiOiJ7YmI5YzIzNjQtZGZhZS00NGYzLWJjZmQtOTk5MDUzZDk0ZjllfSIsInNlc3Npb25pbmRleCI6ImI2NWYxMWI2LWViYTctNDg4Mi05MDBhLTVmMmQ5M2EzOGRiYSIsInNzbiI6IjE5NjgwMjAyMDU3NSIsIm5hbWUiOiJUZXJuZSBQYXVsc2VuIiwiZ2l2ZW5uYW1lIjoiVGVybmUiLCJnaXZlbl9uYW1lIjoiVGVybmUiLCJzdXJuYW1lIjoiUGF1bHNlbiIsImZhbWlseV9uYW1lIjoiUGF1bHNlbiIsImlwYWRkcmVzcyI6IjE4NS4xNTcuMTM0LjEzNCIsImNvdW50cnkiOiJTRSIsImlhdCI6MTcxNjM3NDUwMSwibmJmIjoxNzE2Mzc0NTAxLCJleHAiOjE3MTYzNzU3MDF9.RVQnlukfoH597uXzE1Gays5DElGzAr8xgOmi7ZWppaL3QPGhV4vK2o6qLhxXg_-FKG9xCwHR6gEhnNzWA3W3B6Q2zJeQTYh9okUvTmmhAFIyDL7lEtfWVVKUKvauDisYVZDjAxJQS_1zbgPEi5I-UJ6_kvMGH-wC13MAD2bZGTGR2dR-ZevBUn7plOt0PKXrIZD3vwxDfebTMPQqX_9SNT5F7GLjCcpeVK-T5LOgmUMFcTAbHvNyklqP5ymRHsZLDw_ib4I7ZqODhR-3uISWo1NvG4Y84iBcqv50WRNlmMUm004LfPw1flM5DNsVyUWCqYW8m7eBEwLp5va-6OQG4w", + "access_token": "cf1ce646-7fbe-4740-9c56-fe3f0891f6c6", + "token_type": "N_A", +} +``` + +## Step 3: Validate the JWT + +import ValidateJWTSnippet from '../../../../snippets/validate-jwt.mdx'; + + diff --git a/src/pages/verify/reference/glossary.mdx b/src/pages/verify/reference/glossary.mdx index 49943ead..32277268 100644 --- a/src/pages/verify/reference/glossary.mdx +++ b/src/pages/verify/reference/glossary.mdx @@ -29,8 +29,9 @@ This code is then securely exchanged for tokens (ID Token, Access Token) at the ### Authorization Flow (Authorization Grant) -In OAuth 2.0, an Authorization Flow describes a sequential workflow a client application follows to obtain an Authorization Grant (permission) from an Authorization Server (e.g., Idura Verify) to access protected resources on behalf of a user. -With Idura Verify, client applications can use Authorization Code Flow, PKCE, CIBA or Implicit Flow (for testing only). The choice of the flow depends on the application's requirements and characteristics, such as its ability to securely store a Client Secret or hold a private key (if using Private Key JWT for client authentication). +In OAuth 2.0, an Authorization flow describes a sequential workflow a client application follows to obtain an Authorization Grant (permission) from an Authorization Server (e.g., Idura Verify) to access protected resources on behalf of a user. +With Idura Verify, client applications can use Authorization Code flow, PKCE, CIBA or Implicit flow (for testing only). +The choice of the flow depends on the application's requirements and characteristics, such as its ability to securely store a Client Secret or hold a private key (if using Private Key JWT for client authentication). ### Authorization Request (Authorize URL) @@ -46,7 +47,7 @@ Idura Verify acts as an Authorization Server, handling the connection to eID pro ### CIBA (Client Initiated Backchannel Authentication) -A new OAuth 2.0 Authorization Flow where the client application initiates authentication on behalf of the end-user. +A new OAuth 2.0 Authorization flow where the client application initiates authentication on behalf of the end-user. Unlike other OIDC flows, there are no browser redirects; communication occurs directly between the client and the OpenID Provider (Idura Verify) over a "backchannel." The user then completes authentication on a separate device (e.g., their phone), enabling use cases like confirming identity during a phone call. @@ -54,6 +55,7 @@ The user then completes authentication on a separate device (e.g., their phone), Statements about an end-user or about the authentication event itself (e.g., authentication time) that are included in an ID token issued by the Authorization Server (Idura Verify) following successful authentication. Claims can include name, date of birth, and national identity number (e.g., SSN, CPR), depending on what the eID provider supports and what your application requests via scopes. +For examples of claims included in ID tokens for different eID providers, see [JWT payloads per eID type](/verify/reference/token-contents/). ### Client ID @@ -72,10 +74,10 @@ The process by which a client application exchanges an authorization code (recei This step is crucial for security, as it ensures that the tokens are transmitted directly between the client application and the Authorization Server, without being exposed in the user's browser URL. If the code-for-token exchange is successful, the Authorization Server will respond by providing the ID Token and/or Access Token. -### Confidential Client +### Confidential Clients -According to the OAuth 2.0 protocol, client applications are categorized as either confidential or public, depending on their ability to securely store credentials (e.g., client secrets). -A Confidential Client is an application that can keep its credentials, typically because it runs on a trusted backend server. +According to the OAuth 2.0 protocol, client applications are categorized as either **confidential** or [**public**](/verify/reference/glossary/#public-clients), depending on their ability to securely store credentials (e.g., client secrets). +A confidential client is an application that can keep its credentials, typically because it runs on a trusted backend server. Confidential clients can use authorization flows that require them to authenticate by specifying their Client ID and Client Secret when calling the Authorization Server's Token Endpoint. ### Criipto @@ -84,6 +86,12 @@ The former name of Idura. Our SDK packages (e.g., `@criipto/auth-js`) will conti ## D +### Decoupled Authentication Flow + +An authentication flow that decouples user authentication from the client application, allowing an authentication request to be triggered on one device (e.g., a call center terminal) and completed on another (e.g., the user's smartphone). +Unlike standard redirect-based authentication flows, this happens via direct server-to-server communication, without browser redirects. +[CIBA](/verify/reference/glossary/#ciba-client-initiated-backchannel-authentication) is an example of a decoupled authentication flow. + ### Domain (Idura Domain) Your unique subdomain on the `idura.broker` root domain (e.g., _your-company.idura.broker_) on which you call our service to perform authentication. Your domain hosts the OIDC discovery document, authorization endpoint, token endpoint, etc. You create a domain via the Idura dashboard. @@ -117,7 +125,7 @@ Acts as an intermediary between client applications and multiple Identity Provid ### Implicit Flow -An OAuth 2.0 Authorization Flow where an ID Token is returned directly to the client application via the user's browser redirect. This flow is now considered obsolete and should not be used due to tokens being exposed in the browser's URL fragment. +An OAuth 2.0 Authorization flow where an ID Token is returned directly to the client application via the user's browser redirect. This flow is now considered obsolete and should not be used due to tokens being exposed in the browser's URL fragment. ## J @@ -163,7 +171,7 @@ Any data that could potentially identify a specific individual. Attributes obtai ### PKCE (Proof Key for Code Exchange) -An extension to the OAuth 2.0 Authorization Code Flow designed to prevent authorization code interception attacks. While it was initially developed for public clients (like mobile or single-page apps) that can't securely store a client secret, PKCE is now recommended for all clients as a best security practice. +An extension to the OAuth 2.0 [Authorization Code flow](/verify/reference/glossary/#authorization-code-flow) designed to prevent authorization code interception attacks. While it was initially developed for public clients (like mobile or single-page apps) that can't securely store a client secret, PKCE is now recommended for all clients as a security best practice. ### Private Key JWT Authentication @@ -171,8 +179,8 @@ One of the client authentication methods defined in + +A more secure alternative to traditional authorization requests is [Pushed Authorization Requests (PAR)](/verify/guides/pushed-authorization-requests). + +PAR lets you initiate the authentication flow by sending [authorization request parameters](/verify/reference/authorize-request-parameters) directly to the authorization server via a POST request. +This approach keeps parameters out of URLs, making requests more secure and protecting their integrity. It also ensures sensitive data remains confidential, e.g. if you are including PII in a [`login_hint`](/verify/reference/authorize-request-parameters/#login_hint). + +[View PAR implementation guide](/verify/guides/pushed-authorization-requests/#implementing-par-with-idura). + +
diff --git a/src/snippets/validate-jwt.mdx b/src/snippets/validate-jwt.mdx new file mode 100644 index 00000000..25f6e980 --- /dev/null +++ b/src/snippets/validate-jwt.mdx @@ -0,0 +1,8 @@ +You can now proceed to validating the [ID Token](/verify/reference/glossary/#id-token) Idura Verify returned in response and access the [end-user information it contains](/verify/reference/glossary/#claims). +The validation step is required: without it, you cannot trust the contained end-user information. + +Idura provides a list of [integrations](/verify/integrations) that handle most of the authentication process, including JWT validation. +Alternatively, we strongly recommend that you find a battle-hardened library for your specific platform to do this heavy lifting. +You can find an extensive list of libraries on jwt.io. + +For more information, see [OpenID Connect security best practices](/verify/how-it-works/best-security-practices/#always-validating-id-tokens) and JWT validation guide. diff --git a/src/utils/get-breadcrumb.tsx b/src/utils/get-breadcrumb.tsx index 9ba20efc..c62e48be 100644 --- a/src/utils/get-breadcrumb.tsx +++ b/src/utils/get-breadcrumb.tsx @@ -4,7 +4,7 @@ export interface BreadcrumbItem { } export function getBreadcrumbTrail(pathname: string): BreadcrumbItem[] { - // Breadcrumb logic for nested index pages ("Errors" and "Samples") + // Breadcrumb logic for nested index pages ("Errors", "Samples", "Authorization flows") if ( pathname.startsWith('/verify/reference/errors/danish-mitid') || pathname.startsWith('/verify/reference/errors/general') @@ -18,6 +18,14 @@ export function getBreadcrumbTrail(pathname: string): BreadcrumbItem[] { { label: 'Reference', href: '/verify/reference' }, { label: 'Samples', href: '/verify/reference/samples' }, ]; + } else if ( + pathname.startsWith('/verify/reference/authorization-flows/authorization-code-flow') || + pathname.startsWith('/verify/reference/authorization-flows/implicit-flow') || + pathname.startsWith('/verify/reference/authorization-flows/pkce') || + pathname.startsWith('/verify/reference/authorization-flows/ciba') || + pathname.startsWith('/verify/reference/authorization-flows/headless') + ) { + return [{ label: 'Reference', href: '/verify/reference' }]; } return []; } From 1a97f2b153232114a478e02515b19ebe33c21b90 Mon Sep 17 00:00:00 2001 From: nmoskaleva Date: Wed, 15 Apr 2026 11:21:44 +0200 Subject: [PATCH 3/4] Update Core Concepts page Update and restructure the page so that it acts as a conceptual guide (rather than a How-to with UI instructions). The flow is slightly reordered to introduce the concept of Applications before Domains to help reader build a mental model without unecessary cognitive load. --- .../getting-started/dashboard-setup.mdx | 2 +- .../verify/how-it-works/core-concepts.mdx | 111 +++++++++++------- .../client-secret-secure-alternatives.mdx | 10 ++ 3 files changed, 78 insertions(+), 45 deletions(-) create mode 100644 src/snippets/client-secret-secure-alternatives.mdx diff --git a/src/pages/verify/getting-started/dashboard-setup.mdx b/src/pages/verify/getting-started/dashboard-setup.mdx index e4f8b463..7f6ab798 100644 --- a/src/pages/verify/getting-started/dashboard-setup.mdx +++ b/src/pages/verify/getting-started/dashboard-setup.mdx @@ -14,7 +14,7 @@ This takes about two minutes and will give you the client credentials required t A [tenant](/verify/how-it-works/core-concepts/#tenants) represents your organization's isolated environment at Idura. 1. [Sign up for a free test account](https://dashboard.idura.app/signup) to access the Idura Dashboard. -2. You will be prompted to create your first tenant. +2. You will be prompted to create your first tenant. _You can change the tenant name at any time after creation in the Dashboard **Settings**._ **Risk-free testing**: Your free account includes a sandbox environment with unlimited test diff --git a/src/pages/verify/how-it-works/core-concepts.mdx b/src/pages/verify/how-it-works/core-concepts.mdx index 25056028..287a002d 100644 --- a/src/pages/verify/how-it-works/core-concepts.mdx +++ b/src/pages/verify/how-it-works/core-concepts.mdx @@ -3,90 +3,113 @@ product: verify category: How it works sort: 1 title: Core concepts -subtitle: Learn the basics of Idura and familiarize yourself with the terminology. +subtitle: Get familiar with the fundamental building blocks of Idura Verify. --- -This document explains some of the basic terminology we use at Idura, and we try to relate these terms to concepts you are already familiar with. +This document explains the core concepts we use at Idura: **[Tenants](#tenants)**, **[Applications](#applications)**, **[Domains](#domains)**, and **[eID Providers](#eid-providers)**. -This article introduces the following core concepts of Idura: **[Tenants](/verify/how-it-works/core-concepts/#tenants)**, **[Domains](/verify/how-it-works/core-concepts/#domains)**, **[Applications](/verify/how-it-works/core-concepts/#applications)**, and **[eID Providers](/verify/how-it-works/core-concepts/#eid-providers)**. +To illustrate how everything fits together, we will use a fictitious company called Acme Corporation. +They are building both a web application and a mobile application, and they want their users to be able to log in with Swedish BankID and Danish MitID. - +## Tenants -One important distinction in Idura Verify is the separation of **Test** and **Production**. Inside each tenant, you may toggle the setup between Test and Production to configure each environment separately. + + [Sign up for a free account](https://dashboard.idura.app/signup?utm_source=docs) at Idura if you'd + like to explore the dashboard as you read. For step-by-step instructions, follow our **[Dashboard + setup guide](/verify/getting-started/dashboard-setup/)**. + -_Note: To set up a production domain, you must first upgrade your free subscription to a paid subscription._ +In Idura, a tenant is the **logical isolation unit** that separates your company's data from all other Idura customers. +You create a tenant when you first sign up for an account. It represents your organization's private environment at Idura where all your +[applications](/verify/how-it-works/core-concepts/#applications) and [domains](/verify/how-it-works/core-concepts/#domains) are created and contained. +No other company can access your tenant's data, and vice versa. - + + **Test vs. Production environments** -For illustration purposes, we will use a fictitious company named Secure Insurance that wants to use Idura for authentication. They have both a web and a mobile application, and they want their users to be able to log in with Swedish BankID and Danish MitID. +One important distinction in Idura is the separation of Test and Production environments. +Inside each tenant, you may toggle the setup between Test and Production to configure each environment separately. -## Tenants + -If you haven't already [signed up](https://dashboard.idura.app/signup?utm_source=docs) for an Idura account, feel free - it's free. +**Tenant characteristics** -In Idura, a tenant is the **logical isolation unit** that isolates you and your data from other Idura tenants. -The term describes a software architecture where a single instance of the software serves multiple tenants. -No tenant can access the instance of another tenant, even though the software may be running on the same machine. +- The tenant name must be unique. It serves primarily for your reference and carries no formal significance. +- You can create as many tenants as you need. For instance, if you have multiple teams working on different environments (e.g., Development vs. Production), + you can create separate tenants for each team. To do so, go to the upper-left corner of the Dashboard and click on your current tenant name to display the dropdown menu. Then, click **New tenant**. -**Tenant characteristics:** +## Applications -- The tenant name has to be unique. It is used primarily for your reference and communication purposes and carries no formal significance. -- The tenant name can be changed at any time after creation. -- You can create more than one tenant. Typically you manage both your test and production setup inside a single tenant, but please create multiple tenants as it suits you, for example, one for each environment you have (such as Development, Staging, or Production) if you have separate teams working in each tenant. +An Idura Application represents the actual software that will use our services to perform authentication. +_We use the term **application** as defined in the **client** role in [OAuth 2.0](https://tools.ietf.org/html/rfc6749#page-6)._ -You can create additional tenants at any time. To do so, go to the upper-right corner of the Dashboard and click on your own name/email to display the pulldown menu. Click **Create new...**. +When an application is registered in Idura, it is assigned a unique **client ID** (and, if applicable, a **client secret**). -## Domains +- **client ID:** A unique alphanumeric string used to identify your application when it makes [authentication requests](/verify/reference/glossary/#authorization-request-authorize-url) to Idura. + You can use our default format (e.g., `urn:my:application:identifier:6765`) or change it to your preference. + **Note:** While you can change your client ID later, doing so requires that you simultaneously update this value in your application's source code. + Otherwise, your application's authentication will stop working. -A domain in Idura Verify is the DNS domain on which you call our service to perform authentication. -Typically, your first domain would be something like `secure-insurance-test.idura.broker`. +import ClientSecretAlternativesSnippet from '../../../snippets/client-secret-secure-alternatives.mdx'; - +- **client secret:** If your application uses a backchannel flow (such as the [Authorization Code flow](/verify/how-it-works/oidc-intro/#authorization-code-flow)), it also receives a client secret. + Think of it as your application's password: **it must be kept confidential** at all times. If anyone gains access to it, they can impersonate your application and access protected resources. -Domains are registered for either test or production and determine whether you can use test user identities or real eID accounts. + -As such, **the domain is the key distinction between Test and Production.** +In our example, Acme Corporation has two apps: a web app (running on a server) and a native mobile app. +They will therefore register two separate Idura applications: one for the web app, and one for the mobile app. - + -### Bring your own domain +**Different apps require different authorization flows** -For development and testing it is usually sufficient to just use the preconfigured `idura.broker` top-level domain, but for production you may wish to map a domain of your own, such as `login.secure-insurance.com`, to point to Idura Verify. In some scenarios this is required for technical reasons, but you should also keep in mind the perception of your users. They may feel uncomfortable being taken to a third-party domain to log in. If instead you have mapped your own domain, your users may feel safer as they are being kept in the realm of your company and application. +The two applications use different authorization flows because of their different tech stacks and security requirements. -If you choose to set up your own domain for production you must have access to your company's _DNS setup_ and be able to acquire a so-called _SSL certificate_ which must be uploaded to the Idura Verify service (after setting up your DNS record). +- The web app can use the Authorization Code flow, which requires a client secret and is suitable for server-side applications. +- The mobile app must use the PKCE flow, which does not require a client secret and is designed for public clients. -## Applications +For more information, see [Authorization flows reference](/verify/reference/authorization-flows/). -Once you've set up a tenant including the first test domain, you are ready to register your application to enable its use of our service for authentication. To that end, you must register each application. (We use the term **application** as defined in the _client_ role in [OAuth 2.0](https://tools.ietf.org/html/rfc6749#page-6)). + -When you create an application in the **Applications** section of the management UI, we ask you to fill out a few required fields. +## Domains -Each application is assigned a **Client ID** upon creation. This ID is used as the key to identify authentication requests made from your application. This is an alphanumeric string and it's the unique identifier for your application (such as `urn:my:application:identifier:6765`) in the scope of the domain it is registered for. You can use the default format suggested by our management UI, or change it to fit your preferred format. +Your [application](/verify/how-it-works/core-concepts/#applications) needs a dedicated web address where end users are sent to log in, and where your application communicates with Idura. +**This address is your Idura domain.** - + -Note that although you may change the client ID later, you should be very careful not to do so without careful consideration of the fact that the application(s) using this client ID will have to be re-configured at the same time or they will stop working. +Domains are registered for either Test or Production environment and determine whether you can log in using [test user identities](/verify/getting-started/test-users/) or real eID accounts. +As such, **the domain is the key distinction** between your Test and Production environments. + +_Note: To set up a Production domain, you must [upgrade your free subscription](/verify/getting-started/production/#moving-to-production) to a paid tier._ -If you choose to use the _OAuth2 code flow_ - a flow where sensitive information is exchanged through a back-channel between servers - another important piece of information is the **Client Secret**. Think of it as your application's password which **must be kept confidential at all times**. If anyone gains access to your Client Secret they can impersonate your application and access protected resources. +A test domain registered by Acme Corporation will typically look something like `acme-corp-test.idura.broker`. -In our example, _Secure Insurance_ has two apps: a web app (running on a server) and a mobile app. Hence, they would create two applications: one of type using the code flow, and one using the implicit flow, which requires no client secret. +### Bring your own domain -## eID Providers +For development and testing, it is usually sufficient to use the preconfigured `idura.broker` top-level domain. +For Production, we recommend mapping your own custom domain (e.g., `login.acme-corp.com`) to point to Idura Verify. +It allows you to build trust with your users, as they won't have to leave the realm of your application and interact with a third-party domain to log in. -Now that you have set up your **Applications**, you are ready to configure how your users will log in. +_Setting up your own domain for Production requires access to your company's DNS records and an SSL certificate._ -Idura sits between your app and the eID Providers that authenticate your users (such as Swedish BankID and Danish MitID). Through this abstraction, Idura keeps your app isolated from any changes in the provider's implementation. +## eID Providers -Each eID Provider can be shared among multiple applications. You may use all the available eID Providers for all your applications. +**eID Providers** are the actual identity services that authenticate your users (such as Swedish BankID, Danish MitID, or Norwegian BankID). +Idura Verify acts as an abstraction layer, sitting between your application and eID providers, so you don't have to worry about implementation changes of every specific eID. - +Each eID Provider can be shared across multiple [applications](/verify/how-it-works/core-concepts/#applications). You may use all available eID Providers for all your applications. -eID Providers are already configured for test when you create a tenant. To be able to use an eID Provider in production you will - in most cases - have to enter into some sort of formal agreement with the provider of the particular type of eID. + -In most cases, this is handled through the intermediary of Idura, although in some cases you will also have to set up direct agreements (in countries such as Denmark and the Netherlands). +eID Providers are automatically configured and ready to use on your Test domains. -More detail on the formalities and process can be found in the section about [eIDs](/verify/e-ids/). +To use an eID Provider in Production, you must enter into a formal agreement with the provider. +In most cases, Idura handles this process for you, though some countries require direct agreements. +For more information, see our [Moving to Production guide](/verify/getting-started/production/). diff --git a/src/snippets/client-secret-secure-alternatives.mdx b/src/snippets/client-secret-secure-alternatives.mdx new file mode 100644 index 00000000..0659bdc3 --- /dev/null +++ b/src/snippets/client-secret-secure-alternatives.mdx @@ -0,0 +1,10 @@ + + +For maximum security in production, we recommend moving away from client secret authentication and replacing it with safer alternatives: + +- **[Private key JWT authentication](/verify/guides/privatekey-jwt/)**: A secure authentication method for traditional web applications based on asymmetric key cryptography. +- **[PKCE (Proof Key for Code Exchange)](/verify/how-it-works/oidc-intro/#pkce-flow)**: An extension to the Authorization Code flow that eliminates the need for a client secret entirely. + +Learn more in our **[OpenID Connect security best practices](/verify/how-it-works/best-security-practices/)** guide. + + From b99fb3bb8adca5056526f8d70fca0056fcf33f76 Mon Sep 17 00:00:00 2001 From: nmoskaleva Date: Wed, 15 Apr 2026 12:07:33 +0200 Subject: [PATCH 4/4] Miscellaneous updates: improved glossary entries, formatting fixes, cross-referencing, etc. --- src/components/Navigation.tsx | 2 +- .../getting-started/register-application.mdx | 2 +- src/pages/signatures/graphql/examples.mdx | 6 +-- .../verify/e-ids/finnish-trust-network.mdx | 4 +- src/pages/verify/e-ids/norwegian-bankid.mdx | 8 +-- .../verify/getting-started/production.mdx | 2 +- .../verify/guides/authorize-url-builder.mdx | 4 +- src/pages/verify/guides/privatekey-jwt.mdx | 4 +- .../guides/pushed-authorization-requests.mdx | 16 +++--- .../how-it-works/best-security-practices.mdx | 6 +-- src/pages/verify/how-it-works/index.mdx | 4 +- src/pages/verify/how-it-works/overview.mdx | 50 ++++++++--------- .../verify/integrations/aspnet-core-v6.mdx | 2 +- src/pages/verify/integrations/javascript.mdx | 4 +- src/pages/verify/integrations/vuejs.mdx | 4 +- src/pages/verify/reference/glossary.mdx | 53 ++++++++++++++----- .../verify/reference/request-parameters.mdx | 2 +- src/snippets/generate-client-secret.mdx | 2 +- src/snippets/use-par.mdx | 6 +-- 19 files changed, 102 insertions(+), 79 deletions(-) diff --git a/src/components/Navigation.tsx b/src/components/Navigation.tsx index df9e06bc..54143be9 100644 --- a/src/components/Navigation.tsx +++ b/src/components/Navigation.tsx @@ -15,10 +15,10 @@ const SIGNATURES_CATEGORIES = ['Getting Started', 'GraphQL', 'Integrations', 'Gu const VERIFY_CATEGORIES = [ 'Getting Started', + 'How it works', 'eIDs', 'Guides & Tools', 'Integrations', - 'How it works', 'Reference', ]; diff --git a/src/pages/signatures/getting-started/register-application.mdx b/src/pages/signatures/getting-started/register-application.mdx index 26a175ad..0fea1052 100644 --- a/src/pages/signatures/getting-started/register-application.mdx +++ b/src/pages/signatures/getting-started/register-application.mdx @@ -8,7 +8,7 @@ subtitle: To interact with the Idura Signatures API, you must register a test ap ## Setting up a test application -To start working with the Idura Signatures API and using the [GraphQL examples](/signatures/graphql/examples), you'll need to register a test application to obtain your client credentials (**Client ID** and **Client Secret**). +To start working with the Idura Signatures API and using the [GraphQL examples](/signatures/graphql/examples), you'll need to register a test application to obtain your client credentials (**client ID** and **client secret**). To register a new Signatures application: diff --git a/src/pages/signatures/graphql/examples.mdx b/src/pages/signatures/graphql/examples.mdx index d91ca58c..ba3806d2 100644 --- a/src/pages/signatures/graphql/examples.mdx +++ b/src/pages/signatures/graphql/examples.mdx @@ -28,10 +28,10 @@ It is currently not possible to sign documents that have previously been signed, You can modify Idura Verify evidence provider settings to change the runtime behavior: -- To change authentication methods displayed, add [`acrValues`](/verify/reference/authorize-request-parameters/#acr_values). +- To change authentication methods displayed, add [`acrValues`](/verify/reference/request-parameters/#acr_values). - To disable popups, set `"alwaysRedirect": true`. -- To specify the user data you need, add [`scope`](/verify/reference/authorize-request-parameters/#scope). -- To add a message, use [`loginHint`](/verify/reference/authorize-request-parameters/#login_hint). +- To specify the user data you need, add [`scope`](/verify/reference/request-parameters/#scope). +- To add a message, use [`loginHint`](/verify/reference/request-parameters/#login_hint). To experiment with different settings, see [Authorize URL builder](/verify/guides/authorize-url-builder/). diff --git a/src/pages/verify/e-ids/finnish-trust-network.mdx b/src/pages/verify/e-ids/finnish-trust-network.mdx index 9943bfd2..578c1424 100644 --- a/src/pages/verify/e-ids/finnish-trust-network.mdx +++ b/src/pages/verify/e-ids/finnish-trust-network.mdx @@ -50,7 +50,7 @@ Please refer to the [Private Key JWT authentication guide](/verify/guides/privat ### Authentication request signing Your application must sign [authorization requests](/verify/reference/glossary/#authorization-request-authorize-url) to Idura and send them as JWT-secured Authorization Requests(JARs). -A JWT-secured Authorization Request is a method where the [request parameters](/verify/reference/authorize-request-parameters) are encapsulated in a [JSON Web Token (JWT)](/verify/reference/glossary/#json-web-token-jwt) signed by your application’s private key and included in the authorization request via the `request` parameter. +A JWT-secured Authorization Request is a method where the [request parameters](/verify/reference/request-parameters) are encapsulated in a [JSON Web Token (JWT)](/verify/reference/glossary/#json-web-token-jwt) signed by your application’s private key and included in the authorization request via the `request` parameter. Authentication request signing enhances security by guaranteeing that the request originated from your application and has not been tampered with. To understand the benefits in more detail, see: Why Signed Authorization Requests Elevate Your Security. @@ -65,7 +65,7 @@ If your [Idura domain](/verify/how-it-works/core-concepts/#domains) is `acme-cor where the only two parameters directly included in the URL are: -- `client_id`: contains the Client ID of your Idura application +- `client_id`: contains the client ID of your Idura application - `request`: contains Request Object (the JWT whose claims hold the JSON-encoded authorization request parameters) The code sample below demonstrates creating a Request Object JWT, then building authorize URL in a Node.js/Express application. diff --git a/src/pages/verify/e-ids/norwegian-bankid.mdx b/src/pages/verify/e-ids/norwegian-bankid.mdx index 0b5f33da..360c9d10 100644 --- a/src/pages/verify/e-ids/norwegian-bankid.mdx +++ b/src/pages/verify/e-ids/norwegian-bankid.mdx @@ -270,13 +270,7 @@ _The [BankID OIDC Biometrics](#bankid-biometrics-assurance-level) option is **al ### Ordering the client credentials -To order production credentials please send a request to - -

- orders@idura.eu -

- -with answers to these questions: +To order production credentials please send a request to orders@idura.eu with answers to these questions: 1. A short description of what your application does and why it needs BankID. 2. Your company: _Name, organisation number, and address_. diff --git a/src/pages/verify/getting-started/production.mdx b/src/pages/verify/getting-started/production.mdx index 17011b27..7f5164ac 100644 --- a/src/pages/verify/getting-started/production.mdx +++ b/src/pages/verify/getting-started/production.mdx @@ -48,7 +48,7 @@ issuance can take several business days depending on the eID provider.** - Go to the **Domains** tab and create a production domain. _Note that if you choose a domain that ends with something other than `idura.broker`, we recommend going with the Idura-managed option (**Managed SSL certificate**) for SSL certificate._ - Go to the **Applications** tab and register your production application. - _The **Client ID** of your production application must be unique._ + _The **client ID** of your production application must be unique._ 4. **Update your codebase.** Configure your website or application with the new production domain and API credentials. diff --git a/src/pages/verify/guides/authorize-url-builder.mdx b/src/pages/verify/guides/authorize-url-builder.mdx index 400de15b..03f0bc52 100644 --- a/src/pages/verify/guides/authorize-url-builder.mdx +++ b/src/pages/verify/guides/authorize-url-builder.mdx @@ -2,8 +2,8 @@ product: verify category: Guides & Tools sort: 0 -title: Authorize URL builder -subtitle: Use our URL builder to explore the various features offers by Idura Verify and how to use them. +title: Authorize URL Builder +subtitle: Build authorization URLs with various parameters to explore the features offered by Idura Verify. --- import AuthorizeURLBuilder from '../../../components/AuthorizeURLBuilder'; diff --git a/src/pages/verify/guides/privatekey-jwt.mdx b/src/pages/verify/guides/privatekey-jwt.mdx index 81d19003..7b675f6d 100644 --- a/src/pages/verify/guides/privatekey-jwt.mdx +++ b/src/pages/verify/guides/privatekey-jwt.mdx @@ -79,9 +79,9 @@ The client assertion is a JWT signed by the private key of the [RSA key pair](/v `alg`: The algorithm used to sign the JWT. Should be set to `RS256`. -`iss`: The Client ID of your Idura Verify application. You can find it in the General tab of your Application settings under `Client ID/Realm`. +`iss`: The client ID of your Idura Verify application. You can find it in the General tab of your Application settings under `Client ID/Realm`. -`sub`: Your application's Client ID (the same as in the `iss` claim). +`sub`: Your application's client ID (the same as in the `iss` claim). `aud`: Your Idura Verify domain that will receive the assertion, e.g. `https://{{YOUR_IDURA_DOMAIN}}.idura.broker`. diff --git a/src/pages/verify/guides/pushed-authorization-requests.mdx b/src/pages/verify/guides/pushed-authorization-requests.mdx index 47baeb82..81e6961f 100644 --- a/src/pages/verify/guides/pushed-authorization-requests.mdx +++ b/src/pages/verify/guides/pushed-authorization-requests.mdx @@ -9,8 +9,8 @@ title: Pushed Authorization Requests (PAR) Pushed Authorization Requests (PAR) is an OAuth 2.0 extension that allows sending [authorization request -parameters](verify/reference/authorize-request-parameters) to the authorization server via a POST -request, rather than in the browser's URL query string. +parameters](verify/reference/request-parameters/#authorization-endpoint) to the authorization server +via a `POST` request, rather than in the browser's URL query string. PAR enhances security by mitigating risks associated with exposing sensitive data in URLs. It also allows the authorization server to authenticate the client and validate the request early in the process (before any user interaction occurs), @@ -28,7 +28,7 @@ including the [Authorization Code flow](/verify/how-it-works/oidc-intro/#authori The process with PAR is slightly different: -- Your backend makes a POST request containing request parameters (_those you would otherwise include in the URL query string_) to the PAR endpoint (`/oauth2/par`). +- Your backend makes a `POST` request containing request parameters (_those you would otherwise include in the URL query string_) to the PAR endpoint (`/oauth2/par`). - The authorization server responds with a `request_uri`, a unique identifier for your request. - Your application redirects the user's browser to the authorization server's authorization endpoint, including the `request_uri` obtained from the PAR response and the application's `client_id`. - The authorization server retrieves the original request parameters using the `request_uri` and proceeds with the authentication flow. @@ -39,13 +39,13 @@ You can start using PAR in place of standard authorization requests by following ### 1: Push the authorization request from your backend -Send a POST request from your backend to the PAR endpoint. The PAR endpoint URL can be found in your +Send a `POST` request from your backend to the PAR endpoint. The PAR endpoint URL can be found in your [OIDC Discovery Document](/verify/reference/glossary/#oidc-discovery-document-well-knownopenid-configuration) under `pushed_authorization_request_endpoint`. -Your POST request should include: +Your `POST` request should include: - The same parameters you would normally send in an authorization request (e.g. `response_type`, `scope`, `redirect_uri`, `state`). - For the complete list of authorization request parameters, see [authorize request parameters](/verify/reference/authorize-request-parameters/). + For the complete list of authorization request parameters, see [authorize request parameters](/verify/reference/request-parameters/#authorization-endpoint). - Client authentication credentials (for [confidential clients](/verify/reference/glossary/#confidential-clients)): - either `client_id` and `client_secret` (included in the Basic Authorization header) or - `client_assertion` and `client_assertion_type` (included in the request body), when using [Private Key JWT](/verify/guides/privatekey-jwt/). @@ -81,7 +81,7 @@ state=abc456 ```http # Replace `YOUR_DOMAIN.idura.broker` with your Idura domain -# Replace $client_id with the Client ID of your Idura Application +# Replace $client_id with the client ID of your Idura Application # Replace `$client_assertion` with your JWT assertion # Replace `$your_return_url` with the redirect URI registered in your Idura application @@ -100,7 +100,7 @@ state=abc456 ```http # Replace `YOUR_DOMAIN.idura.broker` with your Idura domain -# Replace $client_id with the Client ID of your Idura Application +# Replace $client_id with the client ID of your Idura Application # Replace `$your_return_url` with the redirect URI registered in your Idura application POST https://YOUR_DOMAIN.idura.broker/oauth2/par HTTP/1.1 diff --git a/src/pages/verify/how-it-works/best-security-practices.mdx b/src/pages/verify/how-it-works/best-security-practices.mdx index beb852b5..1c84aaa4 100644 --- a/src/pages/verify/how-it-works/best-security-practices.mdx +++ b/src/pages/verify/how-it-works/best-security-practices.mdx @@ -3,10 +3,10 @@ product: verify category: How it works sort: 3 title: OpenID Connect security best practices -subtitle: Learn about key security guidelines for implementing basic OpenID Connect authentication in client applications. +subtitle: Key security guidelines and recommendations for implementing OIDC authentication in client applications. --- -In OpenID Connect(OIDC) authentication flows, there are two main roles: +In [OpenID Connect(OIDC) authentication flows](/verify/reference/glossary/#authorization-flow-authorization-grant), there are two main roles: 1. **Authorization Server (OpenID Provider)**: the server that authenticates the user and issues ID tokens and access tokens. It handles user authentication and returns the user’s identity information to the client application. 2. **Client Application**: the application requesting authentication on behalf of the end user. @@ -68,7 +68,7 @@ The `state` parameter helps prevent Cross-Site Request Forgery (CSRF) attacks by When initiating an [authorization request](/verify/how-it-works/oidc-intro/#example-authorization-url), the client application generates a random value and includes it in the `state` parameter. This random value links the request to the redirect URL to the client session. Once the user is authenticated, the authorization server redirects the user back to the client application's redirect URL, including the same `state` value in the URL. For example: `https://your-app.com/callback?code=authorization-code&state=unique-state-value` -Similarly, if the `state` parameter was included in the authorization request, the error responses from the authorization server will also contain the `state` parameter, providing protection against attacker-forged error responses. +Similarly, if the `state` parameter was included in the authorization request, the error responses from the authorization server will contain the `state` parameter, providing protection against error responses forged by an attacker. Overall, the `state` parameter allows the client application to verify that the authorization response is genuine and was sent by the authorization server in response to the original authorization request. diff --git a/src/pages/verify/how-it-works/index.mdx b/src/pages/verify/how-it-works/index.mdx index 0964ea45..2d65826b 100644 --- a/src/pages/verify/how-it-works/index.mdx +++ b/src/pages/verify/how-it-works/index.mdx @@ -2,8 +2,8 @@ product: verify category: How it works sort: 0 -title: Platform overview -subtitle: Understand the core concepts, architecture, and protocols behind Idura Verify. +title: Idura Verify basics explained +subtitle: Understand the core concepts, architecture, and protocols that power Idura Verify. --- import { isIndexPage } from '../../../utils'; diff --git a/src/pages/verify/how-it-works/overview.mdx b/src/pages/verify/how-it-works/overview.mdx index d0ab08e5..8d9e7d25 100644 --- a/src/pages/verify/how-it-works/overview.mdx +++ b/src/pages/verify/how-it-works/overview.mdx @@ -2,46 +2,48 @@ product: verify category: How it works sort: 0 -title: Idura Verify Overview +title: Idura Verify overview +subtitle: Understand the value of eID authentication, how Idura can help you integrate multiple eIDs, and what industry standards we use. --- -Idura Verify provides eID authentication as a service. +Idura Verify provides eID ([Electronic Identity](/verify/reference/glossary/#eid-electronic-identity)) authentication as a service. -An **eID** is a special kind of online identity that is tied to a _natural person_. A natural person in relation to eID is a living, individual human being. Examples of eIDs are Danish MitID, Swedish BankID, and Dutch iDIN. - -An eID may be used for authentication and, in most cases, for digitally signing legal documents. +An eID is a digital identity tied to a _natural person_(a living, individual human being). Examples of eIDs include Danish MitID, Swedish BankID, and Dutch iDIN. +An eID can be used for authentication and, in most cases, for digitally signing legal documents. -An eID is different from traditional external identity sources such as Google or Facebook. Generally, an online identity lifecycle has three main phases: +An eID is different from traditional social logins like Google or Facebook. +While anyone can create a social account under any name, an eID guarantees a legally verified identity. +Generally, an electronic identity lifecycle has three main phases: -1. **Enrollment** consists of verification of the legal identity and subsequently the issuance of a digital identity. -2. **Active, day-to-day use** is the authentication of the user as the holder of the issued digital identity. -3. **Archival** terminates the active use of the identity for authentication, but maintains the details for future reference in, for example, a dispute resolution case. +1. **Enrollment:** The physical verification of the user's legal identity and the subsequent issuance of a digital identity. +2. **Active use:** The day-to-day authentication of the user as the verified holder of the issued digital identity. +3. **Archival:** The termination of the active use of electronic identity for authentication. The identity details are maintained for future reference (e.g., for legal dispute resolution). ## Why use Idura Verify? -Generally you will want to use Idura Verify when one of these scenarios fits your situation: - -- You have a **regulatory requirements** to confirm and continuously verify the legal identity of your users. Consider as a prominent example anti-money laundering regulation, AML, which regulates of the financial services sector and sets strict requirements for customer identification. -- You develop e-commerce solutions and have a business interest in identifying customers as part of your **fraud reduction and mitigation** activities. - -In each of these cases using eID services provided by banks and governments may be the preferable solution: It may be used both for the initial onboarding identity verification, and as a continued means to strong authentication. Also, your customers will be familiar with using eID already, which caters for a smooth user experience. +You will generally want to use Idura Verify in one of these scenarios: -At Idura we strive to give developers and companies an easy path to using eID services without having to become security experts. To actually go live in production with eID you will (in most cases) have to set up a formal relationship with a bank or government body in each of the relevant countries. +- **Regulatory compliance (KYC/AML):** You have strict regulatory requirements to verify the legal identity of your users. An example is Anti-Money Laundering (AML) regulation in the financial sector. +- **Fraud prevention:** You develop e-commerce solutions and need to identify customers as part of your fraud reduction and mitigation activities. -Technically, you can connect any application (written in any language or on any stack) to Idura Verify and define the eID providers you want to use (how you want your users to log in). +### A single integration point for multiple eIDs -## Which industry standards does Idura use? +At Idura, our goal is to give developers an easy path to using eID services without having to become security experts. +**Idura Verify acts as a technical abstraction layer.** You can connect an application written on any stack to Idura Verify, and we handle the country-specific eID integrations. +This means you don't have to worry about the underlying technical changes of each specific provider. +_(Note: To go live in Production with an eID, you will typically need to set up a formal relationship with the relevant bank or government body in each country)._ -At the core, being able to provide a service like Idura Verify is based on the premise of _federated authentication_, which means that you delegate the authentication and authorization process to a service outside of your own applications. +## Which industry standards do we use? -Because federated authentication leverages one or more widely adopted industry standards, you are free to implement your applications without having to worry about how the actual identity services develop over time, or how they are secured. +At its core, Idura Verify is based on the premise of [federated authentication](/verify/reference/glossary/#federated-authentication). +This means you delegate the complex authentication process to our external service rather than building it directly into your application. -The identity industry standards that we use here in Idura are: +Because we leverage widely adopted industry standards, you can build your application once, and we ensure it remains secure and compatible as identity services evolve. We use: -- **Open Authorization (OAuth2)**: An authorization standard that allows a user to grant limited access to their resources on one site, to another site, without having to expose their credentials. -- **OpenID Connect (OIDC)**: An identity layer that sits on top of OAuth2 and allows for easy verification of the user's identity, as well the ability to get basic profile information from the identity provider. -- **JSON Web Tokens (JWT)**: An open standard that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. +- **OAuth 2.0 (Open Authorization):** An authorization standard that allows a user to grant a site limited access to their resources without exposing their credentials. +- **OpenID Connect (OIDC):** An identity layer that sits on top of OAuth 2.0. It allows your application to securely verify the user's identity and retrieve basic profile information. +- **JSON Web Tokens (JWT):** An open standard that defines a compact, self-contained, and cryptographically secure way to transmit information between parties. diff --git a/src/pages/verify/integrations/aspnet-core-v6.mdx b/src/pages/verify/integrations/aspnet-core-v6.mdx index 1135ad17..1b6d9a17 100644 --- a/src/pages/verify/integrations/aspnet-core-v6.mdx +++ b/src/pages/verify/integrations/aspnet-core-v6.mdx @@ -62,7 +62,7 @@ To add the authentication services, call the `AddAuthentication` method. To enab Next, configure the OIDC authentication handler. Add a call to `AddOpenIdConnect`. Configure the necessary parameters, such as `ClientId`, `ClientSecret`, `ResponseType`, and not least the `Authority`. The latter is used by the middleware to get the metadata describing the relevant endpoints, the signing keys etc. -The OIDC middleware requests both the `openid` and `profile` scopes by default, you may configure additional scopes if your application is [configured with dynamic scopes](/verify/reference/authorize-request-parameters/#scope-strategies-dynamic-vs-static). +The OIDC middleware requests both the `openid` and `profile` scopes by default, you may configure additional scopes if your application is [configured with dynamic scopes](/verify/reference/request-parameters/#scope-strategies-dynamic-vs-static). ```json // appsettings.json diff --git a/src/pages/verify/integrations/javascript.mdx b/src/pages/verify/integrations/javascript.mdx index c4541788..435a1547 100644 --- a/src/pages/verify/integrations/javascript.mdx +++ b/src/pages/verify/integrations/javascript.mdx @@ -89,7 +89,7 @@ The `CriiptoAuth` constructor takes an object with the following parameters: Use your application's [domain and client ID](#register-your-application-in-criipto-verify) as the values for `domain` and `clientID` respectively. -Additional authorization parameters can be passed in the `CriiptoAuth` constructor as well. For all available options, see [the full list of supported parameters](/verify/reference/authorize-request-parameters). +Additional authorization parameters can be passed in the `CriiptoAuth` constructor as well. For all available options, see [the full list of supported parameters](/verify/reference/request-parameters/#authorization-endpoint). ## Authorization methods @@ -189,7 +189,7 @@ console.log(match.id_token, match.claims); ``` -`prompt: 'login'` will force the user to re-authenticate even if they have already logged in. See [the list of supported parameters](/verify/reference/authorize-request-parameters) for more information. +`prompt: 'login'` will force the user to re-authenticate even if they have already logged in. See [the list of supported parameters](/verify/reference/request-parameters/#authorization-endpoint) for more information. ## Logging user out diff --git a/src/pages/verify/integrations/vuejs.mdx b/src/pages/verify/integrations/vuejs.mdx index 87c6b4bd..1aa01dd0 100644 --- a/src/pages/verify/integrations/vuejs.mdx +++ b/src/pages/verify/integrations/vuejs.mdx @@ -65,7 +65,7 @@ The `CriiptoAuth` constructor takes an object with the following parameters: Use your application's [domain and client ID](#register-your-application-in-criipto-verify) as the values for `domain` and `clientID` respectively. -Additional authorization parameters can be passed in the `CriiptoAuth` constructor as well. For all available options, see [the full list of supported parameters](/verify/reference/authorize-request-parameters). +Additional authorization parameters can be passed in the `CriiptoAuth` constructor as well. For all available options, see [the full list of supported parameters](/verify/reference/request-parameters/#authorization-endpoint). ## Authorization methods @@ -200,7 +200,7 @@ onMounted(() => { ``` -`prompt: 'login'` will force the user to re-authenticate even if they have already logged in. See [the list of supported parameters](/verify/reference/authorize-request-parameters) for more information. +`prompt: 'login'` will force the user to re-authenticate even if they have already logged in. See [the list of supported parameters](/verify/reference/request-parameters/#authorization-endpoint) for more information. ## Logging user out diff --git a/src/pages/verify/reference/glossary.mdx b/src/pages/verify/reference/glossary.mdx index 32277268..206eb737 100644 --- a/src/pages/verify/reference/glossary.mdx +++ b/src/pages/verify/reference/glossary.mdx @@ -10,12 +10,18 @@ subtitle: A list of identity terms used in this documentation. ### ACR (Authentication Context Class Reference) -A specific value or set of values sent in an authorization request (as `acr_values`) to request a specific authentication method (e.g., a particular eID like Swedish BankID). Idura uses `acr_values` to determine which eID provider or authentication method should be invoked. +A specific value or set of values sent in an authorization request (as `acr_values`) to request a specific authentication method (e.g., a particular eID like Swedish BankID). +Idura uses `acr_values` to determine which eID provider or authentication method should be invoked. + +### Access Token + +A credential that represents authorization issued by the [Authorization Server](/verify/reference/glossary/#authorization-server) to the [Client Application](/verify/reference/glossary/#application-client-application) +to access resources on behalf of the end-user. An Access Token states what the bearer of the token is allowed to do, but not who the user is. [OAuth 2.0](/verify/reference/glossary/#oauth-20) uses Access Tokens. ### Application (Client Application) Your software (e.g., a web application, mobile application, or backend service) that integrates with Idura Verify to authenticate users. -We use the term "application" as defined by the client role in OAuth 2.0. +We use the term "application" as defined by the client role in the OAuth 2.0 Authorization Framework. Functionally, your software acts as the client in a client-server model, where Idura Verify serves as the Authentication Server. You create and configure an **Application** in your Idura Dashboard as a digital representation of your software. @@ -25,13 +31,14 @@ It also holds configurations for the specific eID providers your software will i ### Authorization Code Flow The most common OAuth 2.0 Authorization Grant type. The client application first receives a temporary authorization code via a browser redirect. -This code is then securely exchanged for tokens (ID Token, Access Token) at the Authorization Server's Token Endpoint over a secure "backchannel." The backchannel communication makes it suitable for confidential clients, while public clients must use the PKCE extension. +This code is then securely exchanged for tokens (ID Token, Access Token) at the Authorization Server's Token Endpoint over a secure "backchannel". +The backchannel communication makes it suitable for confidential clients, while public clients must use [the PKCE extension](/verify/reference/glossary/#pkce-proof-key-for-code-exchange). ### Authorization Flow (Authorization Grant) -In OAuth 2.0, an Authorization flow describes a sequential workflow a client application follows to obtain an Authorization Grant (permission) from an Authorization Server (e.g., Idura Verify) to access protected resources on behalf of a user. -With Idura Verify, client applications can use Authorization Code flow, PKCE, CIBA or Implicit flow (for testing only). -The choice of the flow depends on the application's requirements and characteristics, such as its ability to securely store a Client Secret or hold a private key (if using Private Key JWT for client authentication). +In OAuth 2.0, an Authorization Flow describes a sequential workflow a client application follows to obtain an Authorization Grant (permission) from an Authorization Server (e.g., Idura Verify) to access protected resources on behalf of a user. +With Idura Verify, client applications can use Authorization Code Flow, PKCE, CIBA or Implicit Flow (for testing only). +The choice of the flow depends on the application's requirements and characteristics, such as its ability to securely store a client secret or hold a private key (if using Private Key JWT for client authentication). ### Authorization Request (Authorize URL) @@ -66,7 +73,7 @@ A public identifier for your application, registered with Idura Verify. It is se A confidential key known only to your application and Idura's Authorization Server. It is used to authenticate your application when it exchanges an authorization code for tokens at the token endpoint. An alternative and more secure client authentication method for confidential clients is Private Key JWT authentication. -_Note: public clients (like single-page applications or mobile apps) must use PKCE instead of a Client Secret for security reasons._ +_Note: public clients (like single-page applications or mobile apps) must use PKCE instead of a client secret for security reasons._ ### Code Exchange @@ -78,7 +85,7 @@ If the code-for-token exchange is successful, the Authorization Server will resp According to the OAuth 2.0 protocol, client applications are categorized as either **confidential** or [**public**](/verify/reference/glossary/#public-clients), depending on their ability to securely store credentials (e.g., client secrets). A confidential client is an application that can keep its credentials, typically because it runs on a trusted backend server. -Confidential clients can use authorization flows that require them to authenticate by specifying their Client ID and Client Secret when calling the Authorization Server's Token Endpoint. +Confidential clients can use authorization flows that require them to authenticate by specifying their client ID and client secret when calling the Authorization Server's Token Endpoint. ### Criipto @@ -104,6 +111,12 @@ _Note: Starting at the end of 2025, all newly created domains will use the `idur A digital credential used to verify an individual's identity online. In simple terms, it's a digital equivalent of a physical ID (like a passport or driver's license), containing verified personal information about the holder. eIDs enable secure online authentication, transactions, and digital signing of documents. The Danish MitID, Swedish BankID, and German Personalausweis are examples of eIDs. +## F + +### Federated Authentication + +A model where a [Client Application](/verify/reference/glossary/#application-client-application) delegates the user authentication process to a trusted [Identity Provider](/verify/reference/glossary/#identity-provider-idp). + ## I ### ID Token @@ -129,6 +142,12 @@ An OAuth 2.0 Authorization flow where an ID Token is returned directly to the cl ## J +### JWT-secured Authorization Request (JAR) + +A security enhancement to a standard [authorization request](/verify/reference/glossary/#authorization-request-authorize-url), defined in [RFC 9101](https://datatracker.ietf.org/doc/html/rfc9101). +In a JAR, the request parameters are encapsulated in a [JSON Web Token (JWT)](/verify/reference/glossary/#json-web-token-jwt) signed by the client application’s private key. +This signed JWT is then included in the authorization request via the `request` parameter. + ### JSON Web Token (JWT) An open standard (RFC 7519) that defines a compact, URL-safe means of representing claims to be transferred between two parties as a JSON object. @@ -153,11 +172,13 @@ Indicates the degree of confidence in the asserted identity of an end-user. eID ### OAuth 2.0 -An authorization standard that allows a user to grant limited access to their resources on one site to another site, without having to expose their credentials. OAuth 2.0 serves as the foundation for OpenID Connect (OIDC). +An authorization standard defined in [RFC 6749](https://datatracker.ietf.org/doc/html/rfc6749), that allows a user to grant limited access to their resources on one site to another site, without having to expose their credentials. +OAuth 2.0 serves as the foundation for [OpenID Connect (OIDC)](/verify/reference/glossary/#openid-connect-oidc). ### OpenID Connect (OIDC) -An identity layer built on top of OAuth 2.0. It allows client applications to verify the identity of an end-user based on the authentication performed by an Authorization Server, as well as to obtain basic profile information about the end-user. Idura Verify's API is based on OIDC. +An identity layer built on top of [OAuth 2.0](/verify/reference/glossary/#oauth-20) and standardized in the [OIDC Core 1.0 specification](https://openid.net/specs/openid-connect-core-1_0.html). +It allows client applications to verify the identity of an end-user based on the authentication performed by an Authorization Server, as well as to obtain basic profile information about the end-user. Idura Verify's API is based on OIDC. ### OIDC Discovery Document (.well-known/openid-configuration) @@ -165,6 +186,12 @@ A JSON document found at a well-known URI on your Idura domain (e.g., _https://y ## P +### Pushed Authorization Request + +A security enhancement to a standard [authorization request](/verify/reference/glossary/#authorization-request-authorize-url), defined in [RFC 9126](https://datatracker.ietf.org/doc/html/rfc9126). +Instead of passing request parameters in a query string, the client application pushes them directly to the server via an HTTP `POST` request. +The server returns a reference (`request_uri`), which is then used to initiate the standard browser redirect. + ### Personally Identifiable Information (PII) Any data that could potentially identify a specific individual. Attributes obtained through eID authentication (like name, SSN/CPR, address) are considered PII and must be handled securely and in compliance with privacy regulations (such as GDPR). @@ -175,12 +202,12 @@ An extension to the OAuth 2.0 [Authorization Code flow](/verify/reference/glossa ### Private Key JWT Authentication -One of the client authentication methods defined in Open ID Connect Core Client Authentication. It relies on using JWT assertions signed with asymmetric key pairs for client authentication, offering a more secure alternative to symmetric credentials (Client ID and Client Secret) for client authentication in traditional server-based web applications. +One of the client authentication methods defined in Open ID Connect Core Client Authentication. It relies on using JWT assertions signed with asymmetric key pairs for client authentication, offering a more secure alternative to symmetric credentials (client ID and client secret) for client authentication in traditional server-based web applications. ### Public Clients -Unlike [confidential clients](#confidential-clients), public clients are applications that are unable to securely hold credentials (such as a Client Secret) without the risk of exposing them to unauthorized parties. -Mobile apps and single-page applications (SPAs) are public clients. As they cannot rely on a Client Secret for authentication, public clients must use the PKCE extension to the Authorization Code flow. +Unlike [confidential clients](#confidential-clients), public clients are applications that are unable to securely hold credentials (such as a client secret) without the risk of exposing them to unauthorized parties. +Mobile apps and single-page applications (SPAs) are public clients. As they cannot rely on a client secret for authentication, public clients must use the PKCE extension to the Authorization Code flow. ## R diff --git a/src/pages/verify/reference/request-parameters.mdx b/src/pages/verify/reference/request-parameters.mdx index ed903f42..190782c3 100644 --- a/src/pages/verify/reference/request-parameters.mdx +++ b/src/pages/verify/reference/request-parameters.mdx @@ -417,7 +417,7 @@ Also allows you to manage end-user consent. Idura Verify supports the following - **Omitted (default):** Reuses an existing session if one is available. If no session exists, the user is prompted to authenticate. - `none`: Requires an existing active session. If the user is not already logged in, authentication fails. - `login`: Forces the user to re-authenticate, even if they already have an active session. -- `consent`: Allow the user to grant consent. +- `consent`: Might force the user to grant consent. - `consent_revoke`: Allows the user to revoke previously granted consent, for instance, via a ["forget-me" link](/verify/e-ids/norwegian-bankid/#forced-and-optional-consent). More information is available in our [SSO guide](/verify/guides/sso/#per-request-options) and [Authorize URL Builder](/verify/guides/authorize-url-builder/). diff --git a/src/snippets/generate-client-secret.mdx b/src/snippets/generate-client-secret.mdx index 557f1815..de674a3b 100644 --- a/src/snippets/generate-client-secret.mdx +++ b/src/snippets/generate-client-secret.mdx @@ -1,7 +1,7 @@ 1. Go to the **OpenID Connect** section of your Application settings. 2. Toggle on **Enable OAuth2 Code Flow** and click **Save**. ![Enable OAuth2 Code Flow toggle](./images/oauth2-enable.png) -3. Copy and save the generated **Client Secret**. _Idura only stores the secret as a hashed value, meaning that once you navigate away from this page, +3. Copy and save the generated **client secret**. _Idura only stores the secret as a hashed value, meaning that once you navigate away from this page, you won't be able to retrieve the raw value again. If you lose your client secret, you will have to generate a new one and update your code._ ![OAuth2 client secret](../pages/verify/getting-started/images/oauth2-client-secret.png) diff --git a/src/snippets/use-par.mdx b/src/snippets/use-par.mdx index a1fffd39..8ef380bf 100644 --- a/src/snippets/use-par.mdx +++ b/src/snippets/use-par.mdx @@ -1,9 +1,9 @@ - + A more secure alternative to traditional authorization requests is [Pushed Authorization Requests (PAR)](/verify/guides/pushed-authorization-requests). -PAR lets you initiate the authentication flow by sending [authorization request parameters](/verify/reference/authorize-request-parameters) directly to the authorization server via a POST request. -This approach keeps parameters out of URLs, making requests more secure and protecting their integrity. It also ensures sensitive data remains confidential, e.g. if you are including PII in a [`login_hint`](/verify/reference/authorize-request-parameters/#login_hint). +PAR lets you initiate the authentication flow by sending [authorization request parameters](/verify/reference/request-parameters/#authorization-endpoint) directly to the authorization server via a POST request. +This approach keeps parameters out of URLs, making requests more secure and protecting their integrity. It also ensures sensitive data remains confidential, e.g. if you are including PII in a [`login_hint`](/verify/reference/request-parameters/#login_hint). [View PAR implementation guide](/verify/guides/pushed-authorization-requests/#implementing-par-with-idura).