API Endpoint Overview#
Version: 1.14 updated 23.02.2026
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:#
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:#
Authorization API#
Bank iD Authorization flow supports various types of grants for the authentication process and obtaining access_token and id_token.
| Flow type | Description |
|---|---|
| authorization_code refresh_token | Code grant is the most common authentication flow. It allows applications to securely exchange tokens and obtain a refresh_token in case of offline access. |
| implicit | Implicit 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.czScope
openidis 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_accessshould be specified whenever the application, based on the authentication flow, requires the release of not onlyaccess_tokenbut alsorefresh_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=main002PKCE 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.czError redirection:#
Auth error response
HTTP/1.1 302 Found
Location: https://serviceprovider.cz/callback?
error=unauthorized_client
&state=main002Tokens used in the Bank iD solution#
Tokens#
| Token type | Use | Required minimum token validity |
|---|---|---|
| access_token | Token used to authorize API requests. | 3600 seconds |
| refresh_token | Token representing offline (long-term) access. Refresh_token is used to release a new short-term access_token. | 1 year |
| id_token | This 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.
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 method | Description |
|---|---|
| Client Secret POST | The Bank iD Developer portal will issue client_secret that the client application sends the request to the token endpoints. |
| Client Secret JWT | The Bank iD Developer portal will issue client_secret that the client application uses to sign the JWT assertion (an HMAC SHA algorithm). |
| Private Key JWT | Communication 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/callbackAuthorization 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=123Authorization 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=368b8099c14c0964a4a9f958c8b5786c46845ec1Refresh 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
client_id=0f8837d4-77e0-47cc-9789-c53d8ca27928
&client_secret=368b8099c14c0964a4a9f958c8b5786c46845ec1
&token=WwVEraxkI7KbtP31wD3XSpZKqGpsLiXg
&token_type_hint=refresh_tokenResponse 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
What to watch out for:
- The size of a JWT token is not limited. If the total length in a GET request exceeds the typical limit of 2048 characters, the request will be rejected by the server. In such cases, it is necessary to call POST and fill
id_token_hintin the request body. This problem typically occurs with tokens issued during signing.
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-8c27741947Response 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 eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiResponse 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":
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 eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiResponse 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":
Response 403 Forbidden (appliaction/json):#
{
"error": "insufficient_scope",
"error description": "Insufficient age of the end user for this operation",
"trace_id": "6978cbb3ac59fa00843e40d606228857"
}Examples of most common errors#
| Error code | Error message | Usage / Description |
|---|---|---|
insufficient_scope | Insufficient age of the end user for this operation | This error is returned when age requirements are not met. For example, during AML verification (scope: profile.verification), where the end user's age is validated against the 15/18 year limit set in the Developer Portal. |
GetProfileCallFailedException | getProfile call failed: Authentication with an expired access token or bearer access token having insufficient privileges | This error is returned when calling with an expired access token. |
GetProfileCallFailedException | getProfile call failed: Invalid/missing access token | This error is returned when calling with an invalid or missing access token. |
GetProfileCallFailedException | getProfile call failed: Some of the required parameters are missing: birthnumber | This error is returned if the end user's data is missing in the bank's records. Most commonly occurs for foreign nationals, minors, or persons under legal guardianship. |
GetProfileCallFailedException | getProfile call failed: Some of the required parameters are missing: birthplaceNationality | This error is returned if the end user's data is missing in the bank's records. Most commonly occurs for foreign nationals, minors, or persons under legal guardianship. |
GetProfileCallFailedException | getProfile call failed: Some of the required parameters are missing: legalstatus | This error is returned if the end user's data is missing in the bank's records. Most commonly occurs for foreign nationals, minors, or persons under legal guardianship. |
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:#
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.
-
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.
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
Seamless UX and Skipping Bank Selection (using id_token_hint)#
To ensure the best user experience and minimize the number of steps, we recommend using the id_token_hint parameter. Insert a previously obtained id_token for the specific user into the id_token_hint field within the request to the /ros endpoint. Even when this is used, the user will always be prompted by their bank for a final confirmation (e.g., in their mobile app), which is essential for authorizing the signature. The id_token is valid for 10 minutes by default; therefore, the id_token_hint can only be used during the validity period of the original token.
This mechanism enables so-called silent user identification and automatically skips the bank selection landing page (bank chooser). Bank iD automatically recognizes the user's bank from the provided token. After calling /authorize, the user is redirected straight to their bank's interface.
Key Benefits:
- Background Identification: The identity determination process occurs without user interaction (silent), significantly increasing the conversion rate.
- Identity Continuity: You gain certainty that the person signing the document is the same person who previously identified themselves within the given session
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 (
usewithsigandencvalue). 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,encandalg - in exposed JWKS you have correctly marked keys with
kidanduse structured_scopeMUST contain adocumentObject,signObject, or bothsignObjectMUST 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_idelement MUST be present and MUST be unique for all documents - the
document_hashelement contains a byte hash of the content of the PDF document/file. The hash algorithm used MUST be specified in the elementhash_alg(Bank iD recommends using the SHA512 algorithm, see JSON example). - the
documet_urielement 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
signObjectelement MUST have a defined priority that is unique as a number for each field - the maximum size of the
keyvalue in the signObject is 255 characters - the maximum size of the
valueelement in signObject is 1024 characters - the
max_agevalue MUST be specified in seconds in the interval minimum: 600 to maximum: 15552000 (=180 days). If the value is outside this interval, it will be limited by the currently defined minimum/maximum. - 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
pageelement.
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"
Example of payload for /ros request with DocumentObjects and SignObject:
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:
documentIdin the PDF metadata MAY not be the same as thedocument_idspecified in the previous /ros request- the hash of the uploaded document MUST be the same as the
document_hashelement during the last /ros request - the document is uploaded as a POST
form-data - file has to be uploaded using
MultipartFileResource, notByteArrayResource
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<String, Object> body = new LinkedMultiValueMap<>();
Resource resource = file.getResource();
body.add("file", resource);
HttpEntity<MultiValueMap<String, Object>> requestEntity
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<String, Any>
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_urifrom 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-ab55e687df70Document signing requires code_flow to function correctly; other authorization flows are not supported.
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"
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 subjectDirectoryAttributesWe 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_adQssw5cLogout 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":{}
}
}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 SeP 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_uriparameter 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
typeproperty. Currently, only theclaims_updatedtype 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 SeP receives this notification, it MAY decide to update its now outdated information and re-request the UserInfo or Profile. If the SeP wishes to be notified of this event, the SeP 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 because Bank iD has the majority of its traffic between 6 AM to 11 PM and it 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_updatedevent, the IdP MUST verify that for given SeP appropriate consent was obtained (with thenotification.claims_updatedscope) 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_adQssw5cNotification 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", "addresses", "idcards"]
}
Field: affected_claims#
The following list contains all supported claims that can be included in the affected_claims field:
sub, claims_updated, birthdate, age, date_of_death, email, email_verified, locale, name, given_name, family_name, middle_name, nickname, preferred_username, phone_number, phone_number_verified, updated_at, zoneinfo, addresses, birthnumber, gender, paymentAccounts, paymentAccountsDetails, title_prefix, title_suffix, birthplace, birthcountry, primary_nationality, nationalities, idcards, majority, pep, limited_legal_capacity, maritalstatus, trust_framework, time, verification_process