API Endpoint Overview

Version: 1.7 updated 17.07.2024

The following endpoints are provided by Bank iD for consumption by the Service Provider. Each of these endpoints only contains examples. Please see the referenced API for a full authoritative description of these resources.

How to read the guideline

The CAPITALIZED words throughout these guidelines have a special meaning as defined in RFC2119: the key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC2119.

Glossary

  • Request object - Object representing action which requires end-users consent.
  • Authorization - If not explicitly stated otherwise, in context of this document Authorization is defined as voluntary and conscious consent with action represented by object
  • Authorization server - A component on the side of the Bank with handles request objects, and their authorization by end user
  • IdP - Identity Provider - A component on the side of the Bank which handles authorization and consent and issues tokens.
  • OIDC - OpenID Connect - An authentication framework, it is a superset of the OAuth2 authorization framework.
  • RP - Relying Party - RP is a party using the provider's identity services. From the bank's point of view, the Relying Party is Bank iD, and for Bank iD, it is the Service Provider.
  • SeP - Service Provider - A third party which is registered in the Bank iD system and intends to consume Bank APIs.
  • KYC - Know Your Customer - A Bank API which allows SePs to gather information about End-Users.
  • JWT - JSON Web Token - A Base64 encoded, signed and possibly encrypted JSON document.
  • JWK - JSON Web Key - A JSON describing keys for JWT encryption, description, signing and verification.

OpenID Configuration Discovery API

Exposed by the Bank iD. Authoritatively described in bankid_for_sep.yaml. Not authenticated.

This endpoint provides information about OpenID Connect configuration.

GET https://oidc.sandbox.bankid.cz/.well-known/openid-configuration

Response 200 OK:

{
  "introspection_endpoint_auth_signing_alg_values_supported": [
    "HS256",
    "HS512",
    "RS256",
    "RS512",
    "PS512",
    "ES512"
  ],
  "request_parameter_supported": false,
  "authorize_endpoint": "https://oidc.sandbox.bankid.cz/auth",
  "claims_parameter_supported": false,
  "introspection_endpoint": "https://oidc.sandbox.bankid.cz/token-info",
  "profile_endpoint": "https://oidc.sandbox.bankid.cz/profile",
  "issuer": "https://oidc.sandbox.bankid.cz/",
  "id_token_encryption_enc_values_supported": [
    "A256GCM"
  ],
  "userinfo_encryption_enc_values_supported": [
    "A256GCM"
  ],
  "authorization_endpoint": "https://oidc.sandbox.bankid.cz/auth",
  "service_documentation": "https://developer.bankid.cz/docs",
  "introspection_endpoint_auth_methods_supported": [
    "client_secret_post",
    "client_secret_jwt",
    "private_key_jwt"
  ],
  "claims_supported": [
    "addresses.buildingapartment",
    "addresses.city",
    "addresses.country",
    "addresses.ruian_reference",
    "addresses.street",
    "addresses.streetnumber",
    "addresses.type",
    "addresses.zipcode",
    "age",
    "birthcountry",
    "birthdate",
    "birthnumber",
    "birthplace",
    "claims_updated",
    "date_of_death",
    "email",
    "email_verified",
    "family_name",
    "gender",
    "given_name",
    "idcards.country",
    "idcards.description",
    "idcards.issue_date",
    "idcards.issuer",
    "idcards.number",
    "idcards.type",
    "idcards.valid_to",
    "limited_legal_capacity",
    "locale",
    "majority",
    "maritalstatus",
    "middle_name",
    "name",
    "nationalities",
    "nickname",
    "paymentAccounts",
    "pep",
    "phone_number",
    "phone_number_verified",
    "preferred_username",
    "primary_nationality",
    "sub",
    "title_prefix",
    "title_suffix",
    "txn",
    "updated_at",
    "verified_claims.verification",
    "zoneinfo"
  ],
  "op_policy_uri": "https://developer.bankid.cz/documents/privacy-policy",
  "token_endpoint_auth_methods_supported": [
    "client_secret_post",
    "client_secret_jwt",
    "private_key_jwt"
  ],
  "response_modes_supported": [
    "query"
  ],
  "backchannel_logout_session_supported": false,
  "token_endpoint": "https://oidc.sandbox.bankid.cz/token",
  "response_types_supported": [
    "code",
    "token"
  ],
  "request_uri_parameter_supported": true,
  "grant_types_supported": [
    "authorization_code",
    "implicit",
    "refresh_token"
  ],
  "ui_locales_supported": [
    "cs"
  ],
  "userinfo_endpoint": "https://oidc.sandbox.bankid.cz/userinfo",
  "verification_endpoint": "https://oidc.sandbox.bankid.cz/verification",
  "op_tos_uri": "https://developer.bankid.cz/documents/terms-of-use",
  "ros_endpoint": "https://oidc.sandbox.bankid.cz/ros",
  "require_request_uri_registration": true,
  "code_challenge_methods_supported": [
    "plain",
    "S256"
  ],
  "id_token_encryption_alg_values_supported": [
    "RSA-OAEP",
    "RSA-OAEP-256",
    "ECDH-ES"
  ],
  "frontchannel_logout_session_supported": false,
  "claims_locales_supported": [
    "en",
    "en-US"
  ],
  "request_object_signing_alg_values_supported": [
    "PS512",
    "ES512"
  ],
  "request_object_encryption_alg_values_supported": [
    "RSA-OAEP",
    "RSA-OAEP-256",
    "ECDH-ES"
  ],
  "scopes_supported": [
    "openid",
    "offline_access",
    "profile.addresses",
    "profile.birthdate",
    "profile.birthnumber",
    "profile.birthplaceNationality",
    "profile.email",
    "profile.gender",
    "profile.idcards",
    "profile.legalstatus",
    "profile.locale",
    "profile.maritalstatus",
    "profile.name",
    "profile.paymentAccounts",
    "profile.phonenumber",
    "profile.titles",
    "profile.updatedat",
    "profile.zoneinfo",
    "profile.verification",
    "notification.claims_updated"
  ],
  "backchannel_logout_supported": true,
  "check_session_iframe": "https://oidc.sandbox.bankid.cz/session-iframe",
  "acr_values_supported": [
    "loa3"
  ],
  "request_object_encryption_enc_values_supported": [
    "A256GCM"
  ],
  "display_values_supported": [
    "page"
  ],
  "profile_signing_alg_values_supported": [
    "PS512"
  ],
  "userinfo_signing_alg_values_supported": [
    "PS512"
  ],
  "profile_encryption_enc_values_supported": [
    "A256GCM"
  ],
  "userinfo_encryption_alg_values_supported": [
    "RSA-OAEP",
    "RSA-OAEP-256",
    "ECDH-ES"
  ],
  "end_session_endpoint": "https://oidc.sandbox.bankid.cz/logout",
  "token_endpoint_auth_signing_alg_values_supported": [
    "HS256",
    "HS512",
    "RS256",
    "RS512",
    "PS512",
    "ES512"
  ],
  "frontchannel_logout_supported": true,
  "profile_encryption_alg_values_supported": [
    "RSA-OAEP",
    "RSA-OAEP-256",
    "ECDH-ES"
  ],
  "jwks_uri": "https://oidc.sandbox.bankid.cz/.well-known/jwks",
  "subject_types_supported": [
    "public",
    "pairwise"
  ],
  "id_token_signing_alg_values_supported": [
    "PS512"
  ]
}

JWK Keys Discovery API

Exposed by the Bank iD. Authoritatively described in bankid_for_sep.yaml. Not authenticated.

This endpoint returns JSON Web Keys to be used as public keys for verifying OIDC ID Tokens and responses, as well as for encrypting requests.

GET https://oidc.sandbox.bankid.cz/.well-known/jwks

Response 200 OK:

{
  "keys": [
    {
      "kty": "EC",
      "x5t#S256": "6a5raMkhphZ9o5goG2Kz_P9VOxVYim38X7a4vTHWlHU",
      "crv": "P-384",
      "kid": "mtls",
      "x5c": [
        "MIIDDzCCApagAwIBAgICEFcwCgYIKoZIzj0EAwMwfjELMAkGA1UEBhMCQ1oxDjAMBgNVBAgMBVByYWhhMSAwHgYDVQQKDBdCYW5rb3ZuaSBpZGVudGl0YSwgYS5zLjEdMBsGA1UEAwwUQmFua0lEIFByb2R1Y3Rpb24gQ0ExHjAcBgkqhkiG9w0BCQEWD2FkbWluQGJhbmtpZC5jejAeFw0yMjAyMjIxNDIxMjlaFw0yMzAyMjIxNDIxMjlaMGExCzAJBgNVBAYTAkNaMQ4wDAYDVQQIDAVQcmFoYTEgMB4GA1UECgwXQmFua292bmkgaWRlbnRpdGEsIGEucy4xIDAeBgNVBAMMF0Jhbmtvdm5pIGlkZW50aXRhLCBhLnMuMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEEI2ki916DNKqUl7GsAynvjLPq9I7X9BMf92doibdnimY4wRhwIiuSVzWcn+/rg667AdK9T7tROVusM/n8T+uYiCW35ulQmpC4SiQw/Yryqa9FrRW2tFkhkTjKnaq/s3No4IBAjCB/zAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIFoDAzBglghkgBhvhCAQ0EJhYkQmFua0lEIFByb2R1Y3Rpb24gQ2xpZW50IENlcnRpZmljYXRlMB0GA1UdDgQWBBSrJqgCsqaNpwd2j0p5KPeTOWtZ+DAfBgNVHSMEGDAWgBQqoi9yTXXY0beUgU8zj/QtExL35jAOBgNVHQ8BAf8EBAMCBeAwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMEMDsGA1UdHwQ0MDIwMKAuoCyGKmh0dHBzOi8vY2EuYmFua2lkLmN6L2NybC9wcm9kL3Byb2QuY3JsLmNybDAKBggqhkjOPQQDAwNnADBkAjBfSqcluWBT7RWGv0QfhPg8hgwpInVoizPW2xv8thBolYhdanFWgNAGYEMntXzkxb8CMGCi5Xg4SpBqohFzWJL8g8mGRwnelOsZJbQU87WQUm43GgKHcr2nPXHfdEVtBFL4Hw=="
      ],
      "x": "EI2ki916DNKqUl7GsAynvjLPq9I7X9BMf92doibdnimY4wRhwIiuSVzWcn-_rg66",
      "y": "7AdK9T7tROVusM_n8T-uYiCW35ulQmpC4SiQw_Yryqa9FrRW2tFkhkTjKnaq_s3N"
    },
    {
      "kty": "RSA",
      "x5t#S256": "HTVG7ys3tfeK5CBviQ56s1Ish7LbAtWjQPA5SqPTTTI",
      "e": "AQAB",
      "use": "sig",
      "kid": "*rp-sign#fa5f17bc-628f-41ad-94e8-849fa15a84c2",
      "x5c": [
        "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"
      ],
      "n": "mC-JUCTLincFSJTEEcNnLpuzLKF7WT4kMOYT2e0Zuh0nJndenHWQu3WIkjpbXzVu4qk2RpoJZAZKVBkT_UIUdIdZ5Yqyyj6X60ExSJVPT5_eoey2SAeaAGHFjkVzY63tjXD_m81Bm2k8K4WSG2ggnMryIUoPdQox9nwYxZwvxyk4Q5m3YkRNWWKSnmQKUAFqjvBkmIlTD4vED9hfx9h0hieS8m8NSUEcCDUWjUp9dvd31PomDtfgf31CgmHb_nEkztl3WcM0V1JuYVz6m0UBYc8nLVFsAdCQg1ruqykpxWhbw4drhtbudBOfW-ZzDD_YBr-hik-qJuVKDZu6mAZR8xjI7m-bf9WKyTxFaKbnSdib86DcdAw5u4qADKO9Y4vBsM6_AnJxHKzOVjbK4mN6RVAYS-WigYmQRd2MepupQCUaNMOyithe-xFTPy6YMCLJKQ1fhbKzg7R1fC8VtuNUEGTb8DHpLjpRBJO6q2NxWREoiyDkfDIDtNA3HZ-Inp0DYzUkfXxwGKE_Mw--xIgaN0rgVcqHCQBK1Zn1eZH6j_ds24dNEeZtEA6DtXp0p2UlTfsW6WFO_eOGzRnAVj1Rz29-ScdIzKIDziQ453ew6PaTQaGVv0ddJv8vVOY4hyo0IeiYYBYDltmJyRY06rnPUDejyH7xZaugFHXaoJhGAxk"
    },
    {
      "kty": "RSA",
      "x5t#S256": "fYowjlnVtUVM3EvJahDnIBjZITeS2SK-9zeE4j3iZ-w",
      "e": "AQAB",
      "use": "enc",
      "kid": "rp-encrypt#b892f5d3-7adb-4ec8-afa6-e28478ea902e",
      "x5c": [
        "MIIElTCCBBugAwIBAgICECwwCgYIKoZIzj0EAwMwfjELMAkGA1UEBhMCQ1oxDjAMBgNVBAgMBVByYWhhMSAwHgYDVQQKDBdCYW5rb3ZuaSBpZGVudGl0YSwgYS5zLjEdMBsGA1UEAwwUQmFua0lEIFByb2R1Y3Rpb24gQ0ExHjAcBgkqhkiG9w0BCQEWD2FkbWluQGJhbmtpZC5jejAeFw0yMTAzMTgwNzUwMDJaFw0yNDAzMTcwNzUwMDJaMFsxCzAJBgNVBAYTAkNaMQ4wDAYDVQQIDAVQcmFoYTEgMB4GA1UECgwXQmFua292bmkgaWRlbnRpdGEsIGEucy4xGjAYBgNVBAMMEUJhbmtJRCBwcm9kdWN0aW9uMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxFLhcDDXnkdcO7CV1gjm4pXu60VFVuVKdYazZ+Bv1EXZ8I6NNQ/yrS0fysyLdaeNEwTrQ2rhb2BjuaR9aOvrPdhFlS2yKZ+k4+wkWeioc6t3jZvb9fJvKpCxozMU8XwC/OVO81G3Az5Gyv/nAGCzNmHRsXUiJBA9gh5OVduBJyAZN6w7s8F4A+QQlSdbMkVduHpUqGlGbvDDZ0zpssJQv2pA3i6y3mfAEPccr75Vgx/le9+6PC/e7BaZFUY/BdP6KmesitPZgD6EACP/QUh21jHn0feGDV+nGkZswPxZp3FCEz6YnkZg24/C6JHOjUee/gATjjjUC+uxpVPLuUGjR+Rf0WMmczMec3LJTfXwhx33ai6nQ02vp8UUGzjfSzF0UiztrWJQ9pRgc4o95h4npcLO+n7uh3NVR2/nHtBPEYGvxxZyX50Ux8HibaHEKZvoQARQ6/MTKgo0FpjGd0G97BxB5FKxw7WwiSLI9USQuDubnE3xqnQMsgJcAlg2HcQkCMu5P+6H2mer9l3wm127KFDHaZeUvV8feEBX6juz4kguQwwtZg/Op1/Hbjh/+pRvUCnbj+erjLzX4Y1rwYZlTlg3QRTaTbxV+Qhfv5gO7ZTlXSvyCIWhKnUYc8EGT1VpKDhoOdzVM23VT5m9plZKZQsyrMJMD1DP15sh2Tj1/8ECAwEAAaOB4DCB3TAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIGQDAzBglghkgBhvhCAQ0EJhYkQmFua0lEIFByb2R1Y3Rpb24gQ2xpZW50IENlcnRpZmljYXRlMB0GA1UdDgQWBBRzRJstwoejw003aJ6fquk9rsU1QDAfBgNVHSMEGDAWgBQqoi9yTXXY0beUgU8zj/QtExL35jALBgNVHQ8EBAMCBBAwOwYDVR0fBDQwMjAwoC6gLIYqaHR0cHM6Ly9jYS5iYW5raWQuY3ovY3JsL3Byb2QvcHJvZC5jcmwuY3JsMAoGCCqGSM49BAMDA2gAMGUCMQCDv5oUXSpGdQFgSD9QPzl6pqTRX2zMeFT4OPj3IKSJPrdEi7A4iPTjWs9r2dm9ngsCMEwCMeFbc3iIA6H+iZGDEgls4pOJQAn5qNq1td9VQijqw+XSeGMkwYmtV/SvRlOyyw=="
      ],
      "n": "xFLhcDDXnkdcO7CV1gjm4pXu60VFVuVKdYazZ-Bv1EXZ8I6NNQ_yrS0fysyLdaeNEwTrQ2rhb2BjuaR9aOvrPdhFlS2yKZ-k4-wkWeioc6t3jZvb9fJvKpCxozMU8XwC_OVO81G3Az5Gyv_nAGCzNmHRsXUiJBA9gh5OVduBJyAZN6w7s8F4A-QQlSdbMkVduHpUqGlGbvDDZ0zpssJQv2pA3i6y3mfAEPccr75Vgx_le9-6PC_e7BaZFUY_BdP6KmesitPZgD6EACP_QUh21jHn0feGDV-nGkZswPxZp3FCEz6YnkZg24_C6JHOjUee_gATjjjUC-uxpVPLuUGjR-Rf0WMmczMec3LJTfXwhx33ai6nQ02vp8UUGzjfSzF0UiztrWJQ9pRgc4o95h4npcLO-n7uh3NVR2_nHtBPEYGvxxZyX50Ux8HibaHEKZvoQARQ6_MTKgo0FpjGd0G97BxB5FKxw7WwiSLI9USQuDubnE3xqnQMsgJcAlg2HcQkCMu5P-6H2mer9l3wm127KFDHaZeUvV8feEBX6juz4kguQwwtZg_Op1_Hbjh_-pRvUCnbj-erjLzX4Y1rwYZlTlg3QRTaTbxV-Qhfv5gO7ZTlXSvyCIWhKnUYc8EGT1VpKDhoOdzVM23VT5m9plZKZQsyrMJMD1DP15sh2Tj1_8E"
    }
  ]
}

Authorization API

Bank iD Authorization flow supports various types of grants for the authentication process and obtaining access_token and id_token.

Flow typeDescription
authorization_code refresh_tokenCode grant is the most common authentication flow. It allows applications to securely exchange tokens and obtain a refresh_token in case of offline access.
implicitImplicit flow is suitable in those cases where it is impossible to communicate through back-end solutions/servers.

Diagram with example of code grant flow

The sequence diagram shows the course of the authentication flow with the exchange of code for tokens. In the case of offline access, the chart is supplemented by an example of exchanging a refresh token for an access token.

Diagram with example of implicit flow

The sequence diagram shows the acquisition of a token via the implicit flow. The diagram also shows the acquisition of a token from the IDP, which in any case takes place in the code grant flow mode.

Bank iD recommends using code grant flow approach, especially from a security perspective!

Authorization GET endpoint is a starting point for OAuth2 and OpenID Connect authorization flows. This request authenticates the user and returns tokens to the client application as a part of the callback response.

This endpoint is exposed by the Bank iD. Authoritatively described in bankid_for_sep.yaml.

GET or POST https://oidc.sandbox.bankid.cz/auth

Auth request example

GET /auth?
  redirect_uri=https://serviceprovider.cz/callback
  &client_id=589b7c53-c0bf-4f6d-8fee-c6972c5d88bf
  &response_type=code
  &scope=openid%20profile.name%20profile.gender%20offline_access
  &state=main002
Host: bankid.cz

Scope openid is required! In the context of its use, it is specified that the authorization flow control will be a process established by the OpenID standard (e.g., including the issuing of id_token).

The scope offline_access should be specified whenever the application, based on the authentication flow, requires the release of not only access_token but also refresh_token. For such a case, the application must have registered code grant flow and refresh_token grant (in its settings on the Bank iD Developer Portal).

Response redirection:

Auth success response

HTTP/1.1 302 Found
Location: https://serviceprovider.cz/callback?
  code=6a72a932a67cf859570a8fb986dcefce19c844995d30fe1ad32d1e5af5579eb2
  &state=main002

PKCE Usage in OAuth and Its Purpose

PKCE (Proof Key for Code Exchange) is used to enhance the security of the authorization code flow by preventing code interception attacks. PKCE introduces a flow with three new parameters: "Code Verifier", "Code Challenge", and "Code Challenge Method".

Auth request with PKCE:

As part of the authorization request, PKCE helps prevent code interception attacks by using a "Code Verifier" and a "Code Challenge".

Steps: Generate Code Verifier: Before the authorization request, the client creates and stores a secret called the "Code Verifier". This is a cryptographically random string used to identify the client. It must be between 43 and 128 characters long.

Generate Code Challenge: The client generates a "Code Challenge", which is a transformation of the "Code Verifier". The "Code Challenge" is sent with the initial authorization request, along with the "Code Challenge Method". PKCE supports two methods:

Plain: In plain mode, the code challenge is identical to the code verifier. S256: In S256 mode, the SHA-256 hash of the code verifier is encoded using BASE64URL encoding. S256 is recommended by the specification. Authorization Request: The "Code Challenge" is securely stored by the authorization server, and an authorization code is returned with the redirect URL as usual.

Auth request with PKCE example

GET /auth?
    client_id=30487be6-53e1-484a-ad41-cc0ec21bb8e9
    &response_type=code
    &redirect_uri=https://serviceprovider.cz/callback?
    &scope=openid%20profile.name%20profile.gender
    &state=main002
    &code_challenge=123
    &code_challenge_method=plain
Host: bankid.cz

Error redirection:

Auth error response

HTTP/1.1 302 Found
Location: https://serviceprovider.cz/callback?
    error=unauthorized_client
    &state=main002

Tokens used in the Bank iD solution

Tokens

Token typeUseRequired minimum token validity
access_tokenToken used to authorize API requests.3600 seconds
refresh_tokenToken representing offline (long-term) access. Refresh_token is used to release a new short-term access_token.1 year
id_tokenThis token was identifying the end-user within the current context and session.At least as long as access_token (3600 seconds)

Access Token

The access_token is the main result of the authentication process. This token is used to call user-centric APIs (primarily /userinfo and /profile). In the case of Bank iD, it may be an unsigned stateless token. The access_token hash can be specified as one of the id_token claims (specifically at_hash claim), which is issued simultaneously with the access_token to ensure integrity.

This token's validity can be found directly at issue in response (authorization_code and refresh_token grant flow) to the API /token or call the API /token-info. The response to the call /token MUST also include a list of scopes for the issued token.

Refresh Token

Refresh_token is used to issue a new access_token, for example, if the access_token expires. Refresh_token is issued if the application requesting its release has a refresh_token grant configured at the Developer Portal and if the application asked for an offline_access scope in the authentication request

The scope of this token's context corresponds to the result of the authentication at which it was issued. When exchanging refresh_token for access_token, the application can request the same or smaller range of scopes.

When exchaing refresh_token for access_token you have to use the same redirect_uri as was used to issue the refresh_token.

ID Token

The id_token represents the identification of the end-user and is issued together with the access_token. The id_token is always signed and contains information about the time of issue and validity of the token. In the case of Bank iD, it always contains the end-user identifier as a claim sub.

The id_token is issued signed by the issuer's key, and the application must verify this signature in each case.


Token exchange API

The token endpoint is used by the client to obtain an access token by presenting its authorization grant or refresh token.

Supported endpoint token authorization methods (the developer can select the required authorization method in the application settings in the Developer Portal):

Token endpoint auth methodDescription
Client Secret POSTThe Bank iD Developer portal will issue client_secret that the client application sends the request to the token endpoints.
Client Secret JWTThe Bank iD Developer portal will issue client_secret that the client application uses to sign the JWT assertion (an HMAC SHA algorithm).
Private Key JWTCommunication takes place via signed JWT client_assertion objects in requests. For this type of authorization, the client application must issue its public JWK keys.

Exposed by the Bank iD. Authoritatively described in bankid_for_sep.yaml. Uses client credentials for authentication.

What to watch out for:

  • do not limit the size of the JWT token programmatically.

POST https://oidc.sandbox.bankid.cz/token

Authorization code exchange request:

POST /token HTTP/1.1
Host: bankid.cz
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=DuorPn4S4ypn5cxH9u0DGo
&client_id=0f8837d4-77e0-47cc-9789-c53d8ca27928
&client_secret=AJFfp_BKiyzsSAY1rDYgE5mR_KPovQgnvBRKLc18yIqeuFd-jRR5h3wuGxiOioYBjKl5NYEXGT25n-NaMJU2AEU
&redirect_uri=https://serviceprovider.cz/callback

Authorization code exchange with PKCE:

When the client wants to exchange the authorization code for an access token, it sends a request that includes the initial "Code Verifier". The server then hashes the "Code Verifier" using SHA-256 (if it has a "Code Challenge" method of S256) and encodes the hashed value as a BASE64URL. The corresponding value is then compared to the "Code Challenge". If they match, an access token is issued. Otherwise, an error message is returned.

Authorization code exchange with PKCE example:

POST /token HTTP/1.1
    Host: bankid.cz
    Content-Type: application/x-www-form-urlencoded

    grant_type=authorization_code
    &code=8BFAC1DA-3F94-4BBD-A743-473080FB6073
    &redirect_uri=https://serviceprovider.cz/callback
    &client_secret=368b8099c14c0964a4a9f958c8b5786c46845ec1
    &code_verifier=123

Authorization code exchange response 200 OK:

{
  "access_token": "c03e997c-aa96-4b3f-ad0c-98626833145d",
  "token_type": "Bearer",
  "refresh_token": "1f703f5f-75da-4b58-a1b0-e315700e4227",
  "expires_in": 3600,
  "id_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
}

Refresh token exchange request:

POST /token HTTP/1.1
Host: bankid.cz
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token
&scope=openid%20profile.name%20profile.addresses%20offline_access
&refresh_token=1f703f5f-75da-4b58-a1b0-e315700e4227
&redirect_uri=https://serviceprovider.cz/callback
&client_id=0f8837d4-77e0-47cc-9789-c53d8ca27928
&client_secret=368b8099c14c0964a4a9f958c8b5786c46845ec1

Refresh token exchange response 200 OK:

{
  "access_token": "c03e997c-aa96-4b3f-ad0c-98626833145d",
  "token_type": "Bearer",
  "expires_in": 6000,
  "id_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
}

Response 400 Request invalid:

HTTP/2 400 Bad Request
Content-Type: application/json;charset=utf-8
Cache-Control: no-store
Pragma: no-cache

{
  "error": "invalid_request",
  "error_description": "The request is missing a required parameter"
}

TokenInfo API

The introspection endpoint is an OAuth 2.0 endpoint that takes a parameter representing an OAuth 2.0 token and returns a JSON representing the meta information surrounding the token, including whether this token is currently active.

Exposed by the Bank iD. Authoritatively described in bankid_for_sep.yaml. Uses client credentials or a client access token for authentication.

POST https://oidc.sandbox.bankid.cz/token-info

Request example:

POST /token-info HTTP/1.1
Host: oidc.sanbox.bankid.cz
Content-Type: application/x-www-form-urlencoded

token=WwVEraxkI7KbtP31wD3XSpZKqGpsLiXg
&token_type_hint=refresh_token

Response 200 OK:

{
  "active": true,
  "scope": "openid profile.addresses",
  "client_id": "d1bdc32e-1b06-4609-9f60-073685267f88",
  "token_type": "access_token",
  "exp": 1419356238,
  "iat": 1419350238,
  "sub": "25657805-66d4-4707-980a-f12429f17592",
  "aud": "https://rp.example.com/resource",
  "iss": "https://bankid.cz/"
}

Logout API

Exposed by the Bank iD. Authoritatively described in bankid_for_sep.yaml. Not authenticated.

SeP redirect the User-Agent of the End-User to this EP whenever it wishes to logout and forget a session. On completion, the End-User is redirected to post_logout_redirect_uri

POST https://oidc.sandbox.bankid.cz/logout

GET https://oidc.sandbox.bankid.cz/logout

Request example:

POST /logout HTTP/1.1
Host: bankid.cz
Content-Type: application/x-www-form-urlencoded

id_token_hint=eyJhbGciOiJSUzI1NiIsImtpZCI6IjdlOGFkZmMzMjU1OTEyNzI0ZDY4NWZmYmIwOThjNDEyIiwidHlwIjoiSldUIn0
&post_logout_redirect_uri=https://serviceprovider.cz/logout
&state=3cf56e5d-40b0-45b5-a329-8c27741947

Response 200 OK

Session logout successful


User info API

The UserInfo and Profile API are the basic interfaces for retrieving authenticated user data. The range of user-approved scopes strictly controls the range of data. A detailed list of scopes and their associated claims is available in the API technical documentation bankid-for-sep.yaml.

UserInfo endpoint is intended primarily for frequently performed identification and authentication, such as repeated login processes to the system/application. This endpoint's data range corresponds to the data ranges for logins using social network identities (Google, LinkedIn).

Access is authorized using a valid end-user access_token that was obtained from a completed login flow.

GET https://oidc.sandbox.bankid.cz/userinfo

Request example:

GET /userinfo HTTP/1.1
Host: oidc.sandbox.bankid.cz
Accept: application/json
Authorization: Bearer eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwi

Response 200 OK (application/json):

{
  "sub": "23f1ac00-5d54-4169-a288-794ae2ead0c4",
  "txn": "6941683f-c6ee-410c-add0-d52d63091069:openid:profile.name:profile.gender",
  "name": "Jan Novák",
  "given_name": "Jan",
  "family_name": "Novák",
  "gender": "male",
  "birthdate": "1970-08-01",
  "email": "j.novak@email.com",
  "zoneinfo": "Europe/Prague",
  "locale": "cs_CZ",
  "phone_number": "+420123456789",
  "updated_at": 1650887066
}

Sequence diagram of the Userinfo API call

Profile API

Unlike UserInfo, the Profile API is designed primarily to perform KYC or AML client authentication. This corresponds to a much more extensive range of data provided. A complete overview of claims and scopes is in the detailed API documentation bankid-for-sep.yaml.

Access is authorized using a valid end-user access_token that was obtained from a completed login flow.

GET https://oidc.sandbox.bankid.cz/profile

Request example:

GET /profile HTTP/1.1
Host: oidc.sandbox.bankid.cz
Accept: application/json
Authorization: Bearer eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwi

Response 200 OK (application/json):

{
  "sub": "23f1ac00-5d54-4169-a288-794ae2ead0c4",
  "txn": "6941683f-c6ee-410c-add0-d52d63091069:openid:profile.name:profile.addresses",
  "verified_claims": {
    "verification": {
      "trust_framework": "cz_aml",
      "verification_process": "45244782"
    }
  },
  "given_name": "Jan",
  "family_name": "Novák",
  "gender": "male",
  "birthdate": "1970-08-01",
  "birthnumber": "7008010147",
  "age": 50,
  "majority": true,
  "date_of_death": null,
  "birthplace": "Praha 4",
  "primary_nationality": "CZ",
  "nationalities": [
    "CZ",
    "AT",
    "SK"
  ],
  "maritalstatus": "MARRIED",
  "email": "J.novak@email.com",
  "phone_number": "+420123456789",
  "pep": false,
  "limited_legal_capacity": false,
  "addresses": [
    {
      "type": "PERMANENT_RESIDENCE",
      "street": "Olbrachtova",
      "buildingapartment": "1929",
      "streetnumber": "62",
      "city": "Praha",
      "zipcode": "14000",
      "country": "CZ",
      "ruian_reference": "14458921"
    }
  ],
  "idcards": [
    {
      "type": "ID",
      "description": "Občanský průkaz",
      "country": "CZ",
      "number": "123456789",
      "valid_to": "2023-10-11",
      "issuer": "Úřad městské části Praha 4",
      "issue_date": "2020-01-28"
    }
  ],
  "paymentAccounts": [
    "CZ0708000000001019382023"
  ],
  "updated_at": 1650887066
}

Sequence diagram of the Profile API call


API for a list of available banks

Exposed by the Bank iD. Authoritatively described in bankid_for_sep.yaml. Not authenticated.

This endpoint provides information regarding available banks and services they provide.

GET https://oidc.bankid.cz/api/v1/banks

Response 200 OK:

{
  "items": [
    {
      "id": "297d3f16-c4c0-4f48-8d98-c94d16eb9e35",
      "title": "Air Bank",
      "description": null,
      "available_logo_images": [
        {
          "id": "4cff4db9-adc4-499a-b698-c186fb14df49",
          "url": "https://img.bankid.cz/GjFzHXjQhWvhEx2HFdHhnIq8bVkbA3R4XRULujuBFzo/fit/1024/1024/no/1/czM6Ly9maWxlc2VydmljZS80Y2ZmNGRiOS1hZGM0LTQ5OWEtYjY5OC1jMTg2ZmIxNGRmNDkucG5n",
          "width": 1025,
          "height": 1025
        }
      ],
      "available_services": [
        "AUTHENTICATION",
        "SIGN_SINGLE_PDF",
        "SIGN_SO"
      ]
    },
    {
      "id": "3b42a926-7d02-472b-bd22-f12fdf22bf0f",
      "title": "Komerční banka",
      "description": "Komerční banka",
      "available_logo_images": [
        {
          "id": "1686e019-6909-4849-95d3-37fdb981bb13",
          "url": "https://img.bankid.cz/as1e3iLVdcQUCXQA996ho5qL736sLKKKitiKkRkVxEQ/fit/1024/1024/no/1/czM6Ly9maWxlc2VydmljZS8xNjg2ZTAxOS02OTA5LTQ4NDktOTVkMy0zN2ZkYjk4MWJiMTMucG5n",
          "width": 1024,
          "height": 1024
        }
      ],
      "available_services": [
        "AUTHENTICATION",
        "SIGN_SINGLE_PDF",
        "SIGN_SO",
        "NOTIFICATION"
      ]
    },
    {
      "id": "bd86df8c-56a0-4d0b-b7f6-bcff83fa09de",
      "title": "MONETA",
      "description": null,
      "available_logo_images": [
        {
          "id": "5702ae1a-8517-4fb8-a79d-61682e1b839b",
          "url": "https://img.bankid.cz/xpdBzR8AM9r_xBPG3vw3228l6hKGpLb9r6D6HXTzLNo/fit/1024/1024/no/1/czM6Ly9maWxlc2VydmljZS81NzAyYWUxYS04NTE3LTRmYjgtYTc5ZC02MTY4MmUxYjgzOWIucG5n",
          "width": 1024,
          "height": 1024
        }
      ],
      "available_services": [
        "AUTHENTICATION",
        "SIGN_SINGLE_PDF",
        "SIGN_SO",
        "NOTIFICATION"
      ]
    },
    {
      "id": "032051de-f43d-4cb8-a911-2ef03773a3b4",
      "title": "ČSOB a.s.",
      "description": null,
      "available_logo_images": [
        {
          "id": "0c13079d-e293-4fe3-b792-08a30e74e30d",
          "url": "https://img.bankid.cz/wal-WnJrnnLyq_dpXOKz86XA24LGBlmlDzqjzD3anXo/fit/1024/1024/no/1/czM6Ly9maWxlc2VydmljZS8wYzEzMDc5ZC1lMjkzLTRmZTMtYjc5Mi0wOGEzMGU3NGUzMGQucG5n",
          "width": 1024,
          "height": 1024
        }
      ],
      "available_services": [
        "NOTIFICATION",
        "AUTHENTICATION",
        "SIGN_SINGLE_PDF"
      ]
    },
    {
      "id": "7e623fed-8aab-4d24-9918-c610d3057859",
      "title": "Česká spořitelna",
      "description": "CSAS",
      "available_logo_images": [
        {
          "id": "5ad1f28e-3ef1-48e1-bfcb-1f8efab01982",
          "url": "https://img.bankid.cz/ec8G2pLQK8dTfCYzj_8hsa3gk61Kv2ieb7sfmwWQMhc/fit/2477/2477/no/1/czM6Ly9maWxlc2VydmljZS81YWQxZjI4ZS0zZWYxLTQ4ZTEtYmZjYi0xZjhlZmFiMDE5ODIucG5n",
          "width": 2477,
          "height": 2477
        }
      ],
      "available_services": [
        "AUTHENTICATION",
        "SIGN_SINGLE_PDF",
        "NOTIFICATION",
        "SIGN_SO"
      ]
    }
  ]
}

API for SIGN

You wish to request explicit consent from the End-User for some action, for example to obtain consent or sign document with end-user. Bank iD has a universal system for request object authorization based on OpenID Financial-grade API. Prior to start of authorization on end user side client SHOULD obtain end-user access token with grated scope "authorization". Authorization API is secured by using mutual TLS and use of signed and encrypted JWTs on endpoints.

Bank iD SIGN service is available in all applications, with set up production environment. It is necessary to expose publicly available JWKS endpoint of your signing key, to be able to use the service.

Authorization usage

Authorization flow is composed of 3 endpoints - /ros , /authorize and /token. For additional validation of signed document SeP can use endpoint /verification.)

  • Endpoint /ros MUST be called first for registration of request object on authorization server. Request object must be passed directly in request body. Request object MUST be signed by your signing key and ecrypted by Bank iD encryption key. Endpoint returns request_uri that is used in other endpoints. In case of document signature resource return upload url for document upload. Document MUST be uploaded prior to call of /authorize endpoint.

      Notes on interface: 
      - Authorization Bearer is not required for this resource. If it is not present, Bank iD will redirect end user to standard authentication process and after that will perform authorization or document signature.
      - If end user is not authenticated use of id_token_hint field is highly recommended, since IdP SHOULD allow skipping initial identification.
      - if end user is not authenticated additional scopes openid and authorization MUST be present
      - If you require to sign document, LoA 3 MUST be sent as acr_value, otherwise Bank iD will return error 
      - remote_authorization constitutes different behavior see remarks in general notes
      - If field scope is present and not empty, IdP will request consent for new scopes added by SeP prior or after authorization, if end user doesn't consent or authorize Bank iD returns error to SeP.
    
  • Endpoint /authorize is called next for initiating authorization on object by end-user. Returns redirect for end-user to authorize the object on Authorization Server. After a client's consent Authorization server redirects end-user on redirect URL provided in request. In case of anonymous ROS, authentication flow is initiated on Bank iD side to authenticate end-user. Interface is straightforward.

  • Processing of authorization and signature of documents could take up to 5 minutes, during which the end-user is presented with a loader.

  • Standard OIDC endpoint /token is used to get final result of authorization on Authorization server. Returns signed and if enabled encrypted JWT with the authorized object in an id_token and subject who had made authorization. A signed document can be downloaded on url found in the structured scope field in ID token.

  • Endpoint /verification may be used to get authenticity of signature in a document made by Bank iD. Document_id from PDF metadata must be used as parameter.

Please note that for use of Authorization API your application needs to have exposed, available JWKS endpoint, which is set in application configuration in Developer Portal. Kotlin example of exposing such endpoint can be found in our Demo Application

Controller

KeyProvider

Structured scope

Structured scope is object that represents action or statement for which end user gives consent by using identity proofing with substantial assurance level if required. Bank iD currently provides 2 types of object:

  • SignObject
  • DocumentObject

SignObject SHOULD be used for simple text consents, confirming of advertisements or similar tasks. Since SignObject is very generic structure, SeP should send correct ui_locale of end user and fill labels and values in correct language. IdPs SHOULD support minimum of Czech and English mutations. Other langugages MAY NOT be supported and these languages SHOULD be converted to Czech or English following rules that Czech and Slovak ui_locales SHOULD be converted as Czech, other languages as English.

DocumentObject CAN be used for signing legally binding PDF with digital signatures with provable audit trails.

Document signature

You can request document signature by end user via Bank iD. Authorization flow differs in this case, since Bank iD actively process document and provides additional features.

On call of /ros resource only metadata of document are sent and Bank iD returns upload_url additionally. Document MUST be uploaded prior to call of /authorize resource.

If you fail to upload document prior to call, /authorize will send error response and request object will be deleted.

On call of /authorize resource Bank iD validates the document, makes hash and flow continues as normal.

On a redirect from end-users browser, you get authorization code and Bank iD adds digital signature by user's certificate to document and seals it with a qualified seal.

Call of /token resource with authorization code returns response with an id_token, which contains URL of signed document, which is stored in Bank iD.

Document signing procedure

0. Prerequisites for the successful signing of the document

  • I have an application based on the developer portal. The application has been set to jwks_uri in Bank iD developer portal with public keys for signing and encrypting communication (use with sig and enc value). The JWKs_uri application endpoint is publicly available.
  • In the case of a production approach, the correct Sign product is selected. This is not necessary for the Sandbox.
  • If I want to sign a PDF document, I need to ensure that the PDF is:
    • Unlocked (signature respectively revision can be added in the document)
    • Contains custom metadata documetId, which includes the document identifier. This identifier is a string that would be unique within the SeP approach. Bank iD only validates the existence of this id, not its uniqueness.
    • If you require a visual signature element, you need to know which page you want to place the element on, and there is space on the given page (see below for dimensions and location).

1. Initiate authorization by sending document metadata and OIDC authentication parameters

To start the authorization flow, it is necessary to send the metadata of the signed document via the POST /ros endpoint.

Request payload of /ros endpoint is required in JWE format. Thus, the JSON payload request is first signed with the sig key of the application in the form of JWS and then encrypted with enc Bank iD key (from JWKs issued on the Bank iD environment). You can find the correct URL of Bank iD JWKs keys for the given environment (Sandbox or Production) in the developer portal at setting up your application on the credentials tab after "unpacking" the OIDC Discovery area.

Example request for /ros:

curl --location --request POST 'https://oidc.sandbox.bankid.cz/ros' \
--header 'Content-Type: application/jwe' \
--data-raw 'eyJraWQiOiJycC1lbmNyeXB0IiwiY3R5IjoiSldUIiwiZW5jIjoiQTI1NkdDTSIsImFsZyI6IlJTQS1PQUVQLTI1NiJ9.d6_-yDTqjHoJxyQZSle2V.......J9UNAzTEVlLCjTQMNd7_YGuRampypivjlx9Yc3w9AEYuShaV6f7HGNl3Q.ZDJ2vfR7TwGtKFCj6zyZ_A'

JSON payload JWE for /ros endpoint You can find a detailed description of the individual elements in the technical documentation here.

Things to watch out for:

  • JWT Header of encrypted JWT MUST contain all fields especially cty, kid, enc and alg
  • in exposed JWKS you have correctly marked keys with kid and use
  • structured_scope MUST contain a documentObject, signObject, or both
  • signObject MUST contain at least one field array element
  • elements outside the structured_scope (response_type, scope, client_id, …) contain common values as in the authentication flow. The values must correspond to the configuration of the application in the development portal.
  • the documet_id element MUST be present and MUST be unique for all documents
  • the document_hash element contains a byte hash of the content of the PDF document/file. The hash algorithm used MUST be specified in the element hash_alg (Bank iD recommends using the SHA512 algorithm, see JSON example).
  • the documet_uri element should point to where the document can be downloaded. In the current version, Bank iD is mandatory, but the site is not validated!
  • fields in the signObject element MUST have a defined priority that is unique as a number for each field
  • the maximum size of the key value in the signObject is 255 characters
  • the maximum size of the value element in signObject is 1024 characters
  • if you only send the scope openid, all scopes you have registered in the application will be requested automatically
  • pages in the PDF are numbered from 0. If it is required to place a signature on the first page of the document, it is necessary to state the value 0 in the page element.

For example of Kotlin code for signing and encrypting ROS see our Demo Application GitHub

Signature Area or Signature field

To create a visual signature in PDF, it is possible to either define an area in the PDF where the visual signature is to be generated via sign_area or to have a pre-generated signature field sign_field. The signature area sign_area represents a set of coordinates defining the location and size of the signature rectangle on the required page of the PDF document. The signature field sign_field is identified by a text string, for example "Signature 1". If it is not possible to place the rectangle in the document (e.g. because the specified page does not exist or the coordinates are outside the page) or the sign_area or sign_field definition is missing, the signature will be entered into the PDF as non-visual. If both sign_area and sign_field are present in the request, sign_area will be ignored. Individual coordinates for sign_area are in points (pt or PostScript) corresponding to 1/72 inch. For A4 paper size we recommend sign_area to be x-dist 160pt and y-dist 40pt.

Example of correctly filled in JSON payload for /ros request:

{
  "max_age": 3600,
  "ui_locales": "cs",
  "scope": "profile.name profile.titles openid profile.birthdate profile.addresses profile.legalstatus profile.email",
  "response_type": "code",
  "structured_scope": {
    "documentObject": {
      "document_title": "Bank iD Consent Document",
      "document_size": 68000,
      "document_subject": "Tribute to Bank iD",
      "document_language": "cs.CZ",
      "document_id": "BID001",
      "document_author": "Bankovni identita, a.s",
      "document_hash": "b2f50bbdeffbb3f0a34e426eee34f006fbbeebe001921eef46eeeeda3a9b27bde1d8b24b95b5db56a9ab27fa7157c0e8cb9bd7ef61d574f3c68d03eb127ff402",
      "document_read_by_enduser": true,
      "hash_alg": "2.16.840.1.101.3.4.2.3",
      "document_created": "2021-08-25T11:46:53+02:00",
      "sign_area": {
        "x-dist": 160,
        "x-coordinate": 320,
        "y-dist": 40,
        "y-coordinate": 400,
        "page": 0
      },
      "document_uri": "https://bankid.cz/documents?document_id=da17ca76-3257-4830-a588-1a84aac69697"
    },
    "signObject": {
      "fields": [
        {
          "priority": 1,
          "value": "Udeleni souhlasu s necim a k necemu",
          "key": "Ano souhlasim"
        }
      ]
    }
  },
  "txn": "455203fe-2db7-4177-b2a0-d6e2e126b2c9",
  "state": "H4YMSE8zcLS-xXLR7ZZ-toUcrYGeMLBLB9BbCtAo-2o",
  "nonce": "1jZ2YXn0kAa0Jfnn7Fig8Gz-wc_GBaQgDVzYtdXTkmI",
  "client_id": "d0cba9e6-ffff-487c-ffff-56395480c342"
}

Example of payload for /ros request with DocumentObjects and SignObject:

{
  "max_age": 3600,
  "bank_id": "29a0cec1-8e0f-4a1f-b0ce-92264b9922e8",
  "acr_values": "loa3",
  "scope": "openid profile.email profile.birthdate profile.name",
  "response_type": "code",
  "structured_scope": {
    "signObject": {
      "fields": [
        {
          "priority": 1,
          "value": "s odebranim ledviny",
          "key": "Souhlasim"
        },
        {
          "priority": 2,
          "value": "s odebranim jineho organu",
          "key": "Nesouhlasim"
        }
      ]
    },
    "documentObjects": {
      "documents": [
        {
          "document_title": "",
          "document_size": 13389,
          "document_subject": "Mr. Aguirre's experiments with pdfmark 1",
          "document_language": "CS",
          "document_id": "ID1",
          "document_priority": 1,
          "document_author": "Jaziel Aguirre",
          "document_hash": "f85e3545ffe29050fbb3a8fcc40e019ba6d7a2921d25120e0480898eb686d54e",
          "document_read_by_enduser": true,
          "hash_alg": "2.16.840.1.101.3.4.2.1",
          "document_created": "2018-12-29T09:46:53.000Z",
          "sign_area": {
            "x-dist": 160,
            "x-coordinate": 320,
            "y-dist": 40,
            "y-coordinate": 400,
            "page": 0
          },
          "sign_field": "Podpis 1",
          "document_uri": "www.testingsite.com"
        },
        {
          "document_title": "",
          "document_size": 13389,
          "document_subject": "Mr. Aguirre's experiments with pdfmark 2",
          "document_language": "CS",
          "document_id": "ID2",
          "document_priority": 2,
          "document_author": "Jaziel Aguirre",
          "document_hash": "f85e3545ffe29050fbb3a8fcc40e019ba6d7a2921d25120e0480898eb686d53e",
          "document_read_by_enduser": true,
          "hash_alg": "2.16.840.1.101.3.4.2.1",
          "document_created": "2018-12-29T09:46:53.000Z",
          "sign_area": {
            "x-dist": 160,
            "x-coordinate": 320,
            "y-dist": 40,
            "y-coordinate": 400,
            "page": 0
          },
          "sign_field": "Podpis 1",
          "document_uri": "www.testingsite.com"
        },
        {
          "document_title": "",
          "document_size": 13389,
          "document_subject": "Mr. Aguirre's experiments with pdfmark 3",
          "document_language": "CS",
          "document_id": "ID3",
          "document_priority": 3,
          "document_author": "Jaziel Aguirre",
          "document_hash": "f85e3545ffe29050fbb3a8fcc40e019ba6d7a2921d25120e0480898eb686d55e",
          "document_read_by_enduser": true,
          "hash_alg": "2.16.840.1.101.3.4.2.1",
          "document_created": "2018-12-29T09:46:53.000Z",
          "sign_area": {
            "x-dist": 160,
            "x-coordinate": 320,
            "y-dist": 40,
            "y-coordinate": 400,
            "page": 0
          },
          "sign_field": "Podpis 1",
          "document_uri": "www.testingsite.com"
        }
      ],
      "envelope_name": "test 10 05 10"
    }
  },
  "txn": "3f863765-0314-4cd6-a43b-6ca00ea4dbfe",
  "state": "state",
  "nonce": "nonce",
  "client_id": "0b927ef7-e3d8-4cba-a34f-96f300232e95"
}

If the request to /ros endpoint contains a documentObject definition, the response element upload_uri will be part of it, which must be used to upload the document. If the request to /ros endpoint contains a documentObjects definition, the response element upload_uris will be part of it, which must be used to upload the documents.

Example response to /ros endpoint:

{
  "exp": 1632831257,
  "request_uri": "urn:uuid:da1fa559-5af4-492e-ffff-ab55e687df70",
  "upload_uri": "https://api.bankid.cz/test/dev-portal-fileservice/api/v1/files/prepared/52f9dbb1-9f4e-ffff-8f14-d13e224f5ab4"
}

2. Upload PDF

From the response to POST /ros endpoint we get upload_uri or upload_uris, which is unique for this authorization flow and is intended for upload a PDF document(s) specified in the previous /ros request.

Things to watch out for:

  • documentId in the PDF metadata MUST be the same as the document_id specified in the previous /ros request
  • the hash of the uploaded document MUST be the same as the document_hash element during the last /ros request
  • the document is uploaded as a POST form-data
  • file has to be uploaded using MultipartFileResource, not ByteArrayResource

Example of document upload:

curl --location --request POST 'https://api.oidc.bankid.cz/test/dev-portal-fileservice/api/v1/files/prepared/52f9dbb1-9f4e-ffff-8f14-d13e224f5ab4' \
--form 'file=@"/C:/Files/signin.pdf"'

Example in Java:

public HttpStatus uploadFile(MultipartFile file, String uri) throws IOException {
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.MULTIPART_FORM_DATA );
    LinkedMultiValueMap body = new LinkedMultiValueMap<>();
    Resource resource = file.getResource();
    body.add("file", resource);
    HttpEntity> requestEntity
            = new HttpEntity<>(body, headers);
    ResponseEntity response = restTemplate.postForEntity(
            uri,
            requestEntity,
            Void.class
    );
    logger.debug("PDF file is uploaded");
    return response.getStatusCode();
}

Example in Kotlin:

    private fun uploadFile(
        uri: String
    ): HttpStatus {
        val fileStream = fileResource.inputStream

        val headers = HttpHeaders().apply {
            contentType = MediaType.MULTIPART_FORM_DATA
        }
        val filename = fileResource.filename
        val body: MultiValueMap = LinkedMultiValueMap()
        val resource: ByteArrayResource = object : ByteArrayResource(fileStream.readAllBytes()) {
            override fun getFilename(): String {
                return filename!!
            }
        }
        body.add("file", resource)
        val response = restTemplate.postForEntity(
            uri,
            HttpEntity(body, headers),
            Void::class.java
        )
        logger.debug("PDF file is uploaded")
        return response.statusCode
    }
}

3. Authorization flow

If a document authorization request is registered via the /ros endpoint and a PDF document is uploaded (if the document is part of a signature request), it is possible to start the authorization flow.

The authorization flow takes place in the end user's browser environment. The link to start the authorization can be obtained from the Bank iD discovery service or read from the application settings in the Developer Portal (credentials tab after expanding the OIDC Discovery area in the "Authorize sign endpoint" item).

Bank iD evaluates according to the input parameters in the /ros endpoint which IdPs to choose from, some IdPs may not support all the services specified in the /ros endpoint, so it is possible that if you use documentObjects you will have a smaller number of IdPs to choose from. than when using documentObject.

To build an authorization link, you will need:

  • Any application redirect_uri. One of the configured in the developer portal environment.
  • request_uri from response to request /ros endpoint. The validity of this uri is limited. See exp element in response to /ros.

Example of authorization line:

https://oidc.sandbox.bankid.cz/auth?redirect_uri=https://my.application.com/callback&request_uri=urn:uuid:da1fa559-5af4-492e-ffff-ab55e687df70

For proper authorization function (signing), we recommend using code_grant authorization flow.

The result of the authorization flow is a callback with code or tokens. The exchange of code for access_token and id_token takes place in the same way as for authentication flow. The link to download the signed document is part of the id_token as a document_uri element.

The validity of the PDF download link in document_uri is limited in time.

Example body of id_token JWT:

{
  "sub": "41f382d2-7cea-402f-97b4-afd864390b9c",
  "name": "John Doe",
  "exp": 1612258710,
  "iat": 1516239022,
  "iss": "https://idp.example.com",
  "sid": "41f382d2-7cea-402f-97b4-afd864390b9c",
  "aud": "https://rp.example.com/resource",
  "nonce": "gH4FFacc81",
  "acr": "loa3",
  "structured_scope": {
    "documentObject": {
      "document_id": "'7159534b-3b88-4f29-866b-9e83489d3053",
      "document_hash": "a93e305306c7a52ac2ccc55b83f197ea8e02b0ce6b317f53ae8e038586f88197",
      "hash_alg": "2.16.840.1.101.3.4.2.3",
      "document_title": "Smlouva o smlouvě budoucí",
      "document_subject": "Smlouva s společností ACME",
      "document_language": "en.EN",
      "document_created": "2020-06-24T08:54:11+00:00",
      "document_author": "Orange SK",
      "document_size": "1250000",
      "document_uri": "https://rp.net/documents?document_id=7e766e94-eb62-11ea-adc1-0242ac120002",
      "sign_area": {
        "page": 1,
        "x-coordinate": 10,
        "y-coordinate": 100,
        "x-dist": 35,
        "y-dist": 25
      }
    }
  }
}

Please note that Bank iD doesn't and cannot verify that document was signed by the person that should sign this document. Personal data of signatory person can be found in the certificate. Structure of certificate is below.

Please note that set of these objects can grow. Also, it's possible to combine more objects together in one request henceforth end user can authorize more actions in one bulk.

Requirements on documents

Following section describes requirements on documents which BanKID accepts for signatures

  • Document MUST be in PDF/A format, other formats are not accepted
  • Document CAN have signatures, document MUST NOT be locked in order to prevent changes. If document is locked Bank iD can't attach signature to document.
  • Document SHOULD have signature plane where Bank iD can place digital signature visualization. Specification of such area is manadatory.
  • Document MUST have same metadata as metadata sent through API

General notes on behavior of authorization

  • You can initiate authorization flow for anonymous user. If such flow is initiated Bank iD is initiating standard OIDC authentication flow.
  • You can also require for anonymous user to use remote authorization - meaning authorization flow is started without end-user's input and end-user authentication and authorization is executed asynchronously i.e. document signature with insurance agents or with utilities. See following sequence diagram:

  • For purposes of remote authorization or anonymous flow you MAY also provide an id_token (even old and expired) in /ros resource to prevent authentication of client and directly initiate authorization flow.
  • If you initiate authorization flow for authenticated user i.e. provide access token with granted scope, this end-user will be REQUIRED to use same bank to provide consent with adequate identity method.
  • If you use an id_token_hint from a previous login within the /ros endpoint, the Authorization Server will compare the "sub" values from the provided id_token with the new id_token and if it doesn't match, it will throw an error.

Authorization server requirements

Following requirements must be met by you in general behavior and are enforced by Bank iD API

  • shall support only confidential clients;
  • shall require the request object to contain an exp claim; and
  • shall require explicit consent by the user to authorize the requested scope if it has not been previously authorized;
  • shall return token responses that conform to section 4.1.4 of [RFC6749];
  • shall return the list of granted scopes with the issued access token;
  • shall require the redirect URIs to use the https scheme;

Security considerations

Usage of authorization flow is bounded by general security guidelines.

General

Following rules SHALL be followed for both client and authorization server JWS algorithms

SHALL use PS256 or ES256 algorithms;
SHALL not use algorithms that use RSASSA-PKCS1-v1_5 (e.g. RS256);
SHALL not use none;

Certificates and policies

Bank iD is for each usage generating new certificate for end user based on identity proofing with assurance level substantial. Details can be found in certification policy. Fo technical purposes of verification of identity, structure of certificate is following:

CN= title_prefix + " " +firstname +" "+last name
givenName= firstname
sn= last name
O= name of verifying bank 
C= country of residence
Pseudonym = sub - id of end user issued by Bank iD for you, MUST match sub in ID token
X509v3 Extensions:
dateOfBirth = date of birth in subjectDirectoryAttributes

We also add serial number, and we keep this as immutable record for future proofing of validity of signature.

Authorization server

Following guidelines must be followed by you to ensure security, and these are enforce by Bank iD APIs.

  • shall authenticate the confidential client at the token endpoint and private_key_jwt as specified in section 9 of [OIDC];
  • shall require a key of size 256 bits or larger for elliptic curve algorithms used for the client authentication;
  • shall require the redirect URIs to be pre-registered;
  • shall require the redirect_uri parameter in the authorization request;
  • shall require the value of redirect_uri to exactly match one of the pre-registered redirect URIs;
  • shall require user authentication at LoA 3 as defined in [X.1254].
  • shall require the response_type values code id_token or code id_token token;

SeP API endpoint overview

The following endpoints are to be made available to the Bank iD by the SeP. Each of these endpoints only contains illustratory examples. Please see the referenced OpenAPI documents for a full authoritative description of these resources.

Back-Channel Logout API

Logout endpoint specified in OpenID.BackChannelLogout.

Exposed by SeP. Authoritatively described in sep_for_bankid.yaml. Not authenticated.

POST /back-channel/logout

Request example:

POST /back-channel/logout HTTP/1.1
Host: serviceprovider.cz
Content-Type: application/x-www-form-urlencoded

logout_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Logout token JWT body example:

{
  "iss": "https://bankid.cz",
  "sub": "f232e6dc-c8b7-4454-8acc-9f07a267ffde",
  "aud": "d8b47d81318a86454babd47572d57252",
  "iat": 1605296323,
  "jti": "687cdd8e-25e7-11eb-adc1-0242ac120002",
  "sid": "4a4e93aa-5d06-4036-bdfa-eb8350bc44d6",
  "events": {
    "http://schemas.openid.net/event/backchannel-logout": {}
  }
}

Front-Channel Logout API

Logout endpoint specified in OpenID.FrontChannel. Rendered in an IFrame.

Exposed by SeP. Authoritatively described in sep_for_bankid.yaml. Not authenticated.

GET /front-channel/logout

Request example:

GET /front-channel/logout?
  iss=https%3A%2F%2Fbankid.cz
  &sid=db96bfbf-a11a-4dd8-a89d-94cd1a411ea6
Host: serviceprovider.cz

Notification API

Bank iD has a universal system for back-channel update notifications. Currently, only the "claims changed" notifications are supported, but the system is built to be extensible enough to support other kinds of notifications as well. Notifications are delivered in the form of webhooks. Notifications are secured and verified using signed and encrypted JWTs. Notifications can be delivered en-masse through the use of event batching. Notification webhook endpoint MUST be registered using the dynamic registration API. Specific notifications (like the aforementioned "claims changed" notification) MUST be requested during the authorization call and MAY be presented to the user on the consent screen.

Batch notification endpoint which accepts a list of notification tokens. These are mainly claim update notifications.

Registration

If the RP wishes to receive notifications, they MUST:

  • expose the notification endpoint. This endpoint is very similar to the back-channel logout endpoint, so much so that the back-channel endpoint MAY be reused for notifications. This endpoint is described in the OpenAPI specification in this document for Service Providers and this document for Banks..
  • register the notification endpoint to receive notifications. This is done using the notification_uri parameter defined for banks here. The Service Provider can set up this URI through the application configuration in the developer portal.

Notification format and security

  • Notifications are delivered in the Notification Token defined in this document.
  • The Notification Token contains a list of Notification Events; these events are discriminated using the type property. Currently, only the claims_updated type is supported.
  • The purpose of this complexity is to allow for batching and delayed notification delivery, while at the same time preventing unauthorized use and confidential information leakage.

Requesting notifications

Some notifications MAY require activation. The claims_updated notification requires a confirmation from the End-User on the consent screen. For this purpose, the scope notification.claims_updated MUST be requested during the Authorization Request. IDP MAY decide to show additional information or confirmation to the End-User on the consent screen.

Delayed notifications and batching

The IDP MAY delay notification delivery because of its internal processes and do it in a batch at a later point in time (for example at the end of day). This workflow is facilitated by the events property in the Notification Token, this property being an array and thus allowing batching. Delayed notifications are further supported by the optional original_event_at property of the Notification Event.

Notification type

claims_updated will be generated when the IDP has received an update of a specific claim of the End-User, e.g. when the End-User has updated their email address. When the RP receives this notification, it MAY decide to update its now outdated information and re-request the UserInfo or Profile. If the RP wishes to be notified of this event, the RP MUST request this notification by requesting notification.claims_updated during the authorization flow.

IdP should follow these guidelines for publishing new events:

  • If claim is one of AML attributes, IdP SHOULD send notification only in case when such claim is verified in Basic Registers.

If this change cannot be verified IdP SHOULD withhold notification for this claim until verification can be done. Other claims can be notified. If change of claim is obtained from Basic Registers, verification is not required.

  • If claim is not one of AML attributes and therefore cannot be verified in Basic Register (i.e.: phone number or e-mail), IdP SHOULD send notification for this claim.
  • IdP SHOULD prefer nightly batch processing of these notifications even in case it's consuming notifications from Basic Registers intraday.

This is recommended since Bank iD is expecting to have the majority of its traffic between 6 AM to 11 PM and will have more available processing capacity.

Implementation considerations

  • Be sure to properly filter which notifications are sent. It is important to only notify about those End-User changes which the SeP does have a consent with appropriate scopes.
  • Especially for the claims_updated event, the IDP MUST verify that for given SeP appropriate consent was obtained (with the notification.claims_updated scope) for this specific End-User.

Corresponding token does not necessary needs to be valid, this MAY be caused by fact that End-User gave consent for sending of notifications but did not give consent for issuing refresh token. SeP then needs to authenticate End-User once more at IdP to obtain changed data.

  • IdP SHOULD allow End-Users to revoke consent for notification for given event type and SeP

Exposed by SeP. Authoritatively described in sep_for_bankid.yaml. Not authenticated.

POST /notify

Request example:

POST /notify HTTP/1.1
Host: serviceprovider.cz
Content-Type: application/x-www-form-urlencoded

notification_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Notification token JWT:

{
  "iss": "https://idp.example.com",
  "iat": "1582797458",
  "jti": "913CC0F7-27BA-40D9-9F4F-8DF74AC3596B",
  "events": [
    {
      "type": "claim_updated",
      "original_event_at": "2020-06-15T14:16:32",
      "sub": "9456B875-62D3-4533-A502-E05D39936F3A",
      "affected_client_ids": [
        "F932FF05-E04C-4CD1-86E4-CE82F1F51EFB"
      ],
      "affected_claims": [
        "phone_number"
      ]
    }
  ]
}