openapi: "3.0.2"
info:
  title: BankID with biometrics OIDC Endpoints
  version: "1.0"
  description: BankID with biometrics OIDC Endpoints
  contact:
    url: https://developer.bankid.no
servers:
  - url: https://oidc.app.bankid.no/oidc/v1
security:
  - BearerAuth: []

tags:
  - name: oidcServer
    description: OIDC provider public endpoints

paths:
  /bc-authorize:
    post:
      operationId: bcAuthorize
      summary: Start CIBA authorization
      description: Start CIBA authorization
      tags: [oidcServer]
      requestBody:
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                login_hint_token:
                  $ref: "#/components/schemas/LoginHintToken"
                scope:
                  $ref: "#/components/schemas/Scope"
                binding_message:
                  type: string
                  description: |
                    Message to bind clients: the `bindingMessage` string returned at permission
                    registration. Treat it as opaque and forward it unchanged.
                expires_in:
                  type: integer
                  description: Desired expiration period of the returned auth_req_id, in seconds. Optional.
                client_assertion:
                  type: string
                  description: |
                    Access token. Alternative to Bearer token in header.
                client_assertion_type:
                  type: string
                  description: |
                    The method used to encode the client_assertion. Must be "urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer". Required if using client_assertion.
              required:
                - scope
                - login_hint_token
                - binding_message
              additionalProperties: false
      responses:
        "200":
          description: Backchannel authorization session started successfully
          content:
            application/json:
              schema:
                required:
                  - auth_req_id
                  - expires_in
                properties:
                  auth_req_id:
                    description: Authorization request ID that can be passed to the token endpoint to poll for authorization session completion.
                    type: string
                    minLength: 16
                    maxLength: 4096
                    pattern: '^[a-zA-Z0-9\-_\.]+$'
                  expires_in:
                    description: Expiration period for auth_req_id, in number of seconds
                    type: integer
                  interval:
                    description: Minimum polling interval when calling the token endpoint
                    type: integer
                  amr_hint:
                    description: |
                      Hint indicating the authentication method(s) the user will experience.
                      Only present for CustomerServiceCall permissions.
                      Values: "bis-hwk" (biometric), "bid-pwd" (password), or the combination of both.
                    type: array
                    items:
                      type: string
                      enum:
                        - bis-hwk
                        - bid-pwd
                additionalProperties: true
        "400":
          description: There was a problem with the request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OidcErrorResponse"
        "403":
          description: The client is not authorized to start the authentication session for this permission
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OidcErrorResponse"
        "500":
          description: An internal error occurred while handling the request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OidcErrorResponse"

  /iframe-authorize:
    post:
      operationId: iframeAuthorize
      summary: iframe authorization
      description: Start iframe authorization
      tags: [oidcServer]
      requestBody:
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                login_hint_token:
                  $ref: "#/components/schemas/LoginHintToken"
                scope:
                  $ref: "#/components/schemas/Scope"
                redirect_uri:
                  $ref: "#/components/schemas/RedirectUri"
                state:
                  type: string
                  description: |
                    Opaque value used to maintain state between the request and the callback.
                    Typically, Cross-Site Request Forgery (CSRF, XSRF) mitigation is done by
                    cryptographically binding the value of this parameter with a browser cookie.
                nonce:
                  type: string
                  description: |
                    String value used to associate an OIDC Client session with an ID Token, and to mitigate replay attacks. 
                    The value is passed through unmodified from the Authentication Request to the ID Token
                code_challenge:
                  type: string
                  description: |
                    Code challenge for PKCE https://datatracker.ietf.org/doc/html/rfc7636
                code_challenge_method:
                  type: string
                  description: |
                    Code challenge method for PKCE https://datatracker.ietf.org/doc/html/rfc7636
                  enum:
                    - S256
                frame_ancestor:
                  type: string
                  example: https://example.com/
                  description: |
                    The URI of the immediate parent page that the iframe will be embedded in. The authentication will fail on origin mismatch.
                ui_locales:
                  type: string
                  example: en nb
                  maxLength: 128
                  description: |
                    Space-separated list of preferred languages for the user interface, ordered by preference.
                    Each value is a BCP47 language tag (e.g., "en", "nb", "nn").
              required:
                - scope
                - login_hint_token
                - redirect_uri
                - state
                - nonce
                - code_challenge
                - code_challenge_method
                - frame_ancestor
              additionalProperties: false
      responses:
        "200":
          description: IFrame authorization session started successfully
          content:
            application/json:
              schema:
                required:
                  - request_uri
                  - expires_in
                properties:
                  authorize_uri:
                    description: Authorize URI to be opened in iframe.
                    type: string
                    minLength: 16
                    maxLength: 4096
                  expires_in:
                    description: Expiration period for authorize_uri, in number of seconds
                    type: integer
                additionalProperties: true
        "400":
          description: There was a problem with the request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OidcErrorResponse"
        "403":
          description: The client is not authorized to start the authentication session for this permission
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OidcErrorResponse"
        "500":
          description: An internal error occurred while handling the request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OidcErrorResponse"

  /app-authorize:
    post:
      description: Start App2App authorization
      operationId: appAuthorize
      summary: Start App2App authorization (public preview)
      tags: [oidcServer]
      requestBody:
        content:
          application/x-www-form-urlencoded:
            schema:
              oneOf:
                - $ref: "#/components/schemas/AppAuthorizeDeviceInfoRequest"
                - $ref: "#/components/schemas/AppAuthorizeThreeDsInfoRequest"
      responses:
        "200":
          description: App2App authorization session started successfully
          content:
            application/json:
              schema:
                required:
                  - authorize_uri
                  - auth_req_id
                  - expires_in
                  - interval
                properties:
                  auth_req_id:
                    description: Authorization request ID that can be passed to the token endpoint to poll for authorization session completion.
                    type: string
                    minLength: 16
                    maxLength: 4096
                    pattern: '^[a-zA-Z0-9\-_\.]+$'
                  authorize_uri:
                    description: URI to be opened as an universal link to switch into the authenticating app.
                    type: string
                    minLength: 16
                    maxLength: 4096
                  expires_in:
                    description: Expiration period for auth_req_id, in number of seconds
                    type: integer
                  interval:
                    description: Polling interval when calling the token endpoint
                    type: integer
                additionalProperties: true
        "400":
          description: There was a problem with the request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OidcErrorResponse"
        "403":
          description: The client is not authorized to start the authentication session for this permission
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OidcErrorResponse"
        "500":
          description: An internal error occurred while handling the request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OidcErrorResponse"

  /token:
    post:
      operationId: tokenExchange
      description: Exchange tokens
      requestBody:
        content:
          application/x-www-form-urlencoded:
            schema:
              $ref: "#/components/schemas/TokenRequest"

      responses:
        "200":
          content:
            application/json:
              schema:
                type: object
                properties:
                  token_type:
                    type: string
                  expires_in:
                    type: integer
                  id_token:
                    type: string
                    format: byte
                required:
                  - token_type
                  - expires_in
                  - id_token
                additionalProperties: true
          description: OK
        "400":
          description: There was a problem with the request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OidcErrorResponse"
        "403":
          description: The client is not authorized to get an access token for the given input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OidcErrorResponse"
        "500":
          description: An internal error occurred while handling the request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OidcErrorResponse"
      summary: OIDC token endpoint
      tags: [oidcServer]

components:
  schemas:
    Scope:
      type: string
      minLength: 6
      maxLength: 512
      example: openid profile
      pattern: "^[a-zA-Z:/_ ]+$"

    LoginHintToken:
      type: string
      minLength: 50
      maxLength: 4096
      pattern: '^[a-zA-Z0-9\-_]+\.[a-zA-Z0-9\-_]+\.[a-zA-Z0-9\-_]+$'
      description: |
        An access token that must be used in the process of granting this permission. This 
        is the `permissionToken` given by the /permissions endpoint.

    AppAuthorizeDeviceInfoRequest:
      type: object
      properties:
        login_hint_token:
          $ref: "#/components/schemas/LoginHintToken"
        scope:
          $ref: "#/components/schemas/Scope"
        device_info:
          type: string
          description: |
            Information of device that initiated authentication process in the merchant app, encoded using URL safe Base64 encoding
      required:
        - scope
        - login_hint_token
        - device_info
      additionalProperties: false

    AppAuthorizeThreeDsInfoRequest:
      type: object
      properties:
        login_hint_token:
          $ref: "#/components/schemas/LoginHintToken"
        scope:
          $ref: "#/components/schemas/Scope"
        3ds_info:
          type: string
          description: |
            EMV 3-D Secure device information decrypted by the issuer's ACS during the
            cardholder's 3-D Secure challenge, encoded using URL safe Base64 encoding. The
            decoded value is a JSON object of EMVCo device data element key/value pairs, for
            example (decoded):

            ```json
            {
              "C001": "Android",
              "C002": "SM-S908B",
              "C008": "1080x2340"
            }
            ```

            See [3DS app2app ACS integration](../advanced-topics/3ds-app2app-acs.md) for the
            full field list and guidance on sourcing each one from the 3DS SDK.

            This field is deliberately an opaque string rather than a typed object. The
            platform identifier (`C001`, EMVCo device data element) carries real-world values
            such as `PlayStation` and `Windows` in addition to `Android` and `iOS`. A typed
            enum would reject those values with `400`, on exactly the population for which we
            most need to return a clean `403` ("no matching device", i.e. app-switch is not
            possible) instead of a request error.
      required:
        - scope
        - login_hint_token
        - 3ds_info
      additionalProperties: false

    TokenRequestBaseSchema:
      type: object
      properties:
        client_assertion:
          type: string
        client_assertion_type:
          type: string
          example: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
          enum:
            - "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
      additionalProperties: false

    TokenRequest:
      anyOf:
        - $ref: "#/components/schemas/AuthCodeTokenRequest"
        - $ref: "#/components/schemas/CibaTokenRequest"

    AuthCodeTokenRequest:
      allOf:
        - $ref: "#/components/schemas/TokenRequestBaseSchema"
        - type: object
          required:
            - grant_type
            - code
            - redirect_uri
          properties:
            grant_type:
              type: string
              example: authorization_code
              enum:
                - authorization_code
            code:
              type: string
            code_verifier:
              type: string
              description: |
                Code verifier for PKCE https://datatracker.ietf.org/doc/html/rfc7636
            redirect_uri:
              $ref: "#/components/schemas/RedirectUri"
          additionalProperties: false

    CibaTokenRequest:
      allOf:
        - $ref: "#/components/schemas/TokenRequestBaseSchema"
        - type: object
          required:
            - grant_type
            - auth_req_id
          properties:
            grant_type:
              type: string
              example: urn:openid:params:grant-type:ciba
              enum:
                - urn:openid:params:grant-type:ciba
            auth_req_id:
              type: string
              pattern: '^[a-zA-Z0-9\-_\.]+$'
          additionalProperties: false

    RedirectUri:
      type: string
      format: uri
      minLength: 15
      maxLength: 256
      example: https://login.bankid.no/idFederationLanding
      description: User will be redirected here afterwards, this value is trusted as it's in the JWT

    OidcErrorResponse:
      type: object
      required:
        - error
        - error_description
      properties:
        error:
          type: string
          description: |
            Error code.
            * `invalid_request`: There was a problem with the request data. For `/app-authorize`,
               `error_description` may additionally be `invalid 3ds_info` (the `3ds_info` value
               could not be decoded or parsed), `device_info and 3ds_info are mutually exclusive`
               (both fields were present), or `device_info or 3ds_info must be provided`
               (neither field was present)
            * `invalid_grant`: The provided authorization code is already exchanged, 
               invalid, expired, revoked or was issued to another client
            * `expired_login_hint_token`: The permission token passed as `login_hint_token`
               is invalid or expired (`/bc-authorize` and `/app-authorize` only)
            * `invalid_binding_message`: The binding message is not correct for the given permission
               (`/bc-authorize` only)
            * `access_denied`: The user denied the authorization request (`/token` endpoint)
               or the permission is not appropriate for this type of authorization flow
            * `unknown_user_id`: User has not signed up for Substantial authentication and a
               backchannel authorization session could not be started (`/bc-authorize` and `/app-authorize` only)
            * `server_error`: An unexpected server error occurred
            * `authorization_pending`: The user has not yet approved or denied backchannel authoriation
               (`/token` endpoint in backchannel and app authorization flow only)
            * `failed_dependency`: A dependency failed
          enum:
            - invalid_request
            - invalid_grant
            - expired_login_hint_token
            - invalid_binding_message
            - access_denied
            - unknown_user_id
            - server_error
            - authorization_pending
            - failed_dependency
        error_description:
          type: string
          description: Human readable error description

    IdTokenBaseSchema:
      type: object
      required:
        - iss
        - sub
        - aud
        - amr
        - acr
        - azp
        - exp
        - iat
        - nbf
        - nonce
      properties:
        iss:
          type: string
          description: Issuer identifier for the issuer of the response
          example: https://app.bankid.no
        sub:
          type: string
          description: Subject identifier internal to BankID Substantial
          pattern: '^[a-zA-Z0-9\-_\.]+$'
          example: 3cbisiTQ_YIWaSAiWDhpNvyc4omeIV7sYyK5X43ZbWo
        acr:
          type: string
          description: Authentication Context Class Reference
          enum:
            - urn:bankid:bih
            - urn:bankid:bis
            - urn:bankid:bid
          default: urn:bankid:bis
        aud:
          type: string
          description: Audience(s) that this ID Token is intended for
          pattern: '^[a-zA-Z0-9\-_]+$'
          example: aletheia-bankid-oidc-client
        amr:
          type: array
          maxItems: 10
          description: Authentication methods used in the authentication
          items:
            enum: [bis-mfa, bis-hwk, bis-bid]
            type: string
            pattern: '^[a-zA-Z0-9\-_]+$'
            example: bis-bid
        azp:
          type: string
          description: Authorized party - the party to which the ID Token was issued
          pattern: '^[a-zA-Z0-9\-_]+$'
          example: aletheia-bankid-oidc-client
        exp:
          type: integer
          description: Expiration time on or after which the ID Token MUST NOT be accepted for processing
          example: 1516239022
        iat:
          type: integer
          description: Time at which the JWT was issued
          example: 1516239022
        nbf:
          type: integer
          description: Time before which the JWT MUST NOT be accepted for processing
          example: 1516239022
        nonce:
          type: string
          description: String value used to associate a Client session with an ID Token, and to mitigate replay attacks
          example: -MEDBtU6SeGtFsdHHI-rlA
        originator:
          type: string
          description: Originator id for the issuer of the id
          example: "9980"
        typ:
          type: string
          description: Type of token
          example: "ID"
        sid:
          type: string
          description: Session identifier
          example: 1.Ag._esziS0YZ2JtbSsGtVlmUyAuaj7Zliz.E9ULYa6BoN7E.IC5qPtmWLMT1QthroGg3sQ
        proof_key_id:
          type: string
          description: Proof key id if applicable
          example: 1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef

    IdTokenProfileSchema:
      type: object
      properties:
        name:
          type: string
          description: Full name of the user for scope profile
          example: "Ola Nordmann"
        updated_at:
          type: integer
          description: Time at which the user profile was last updated for scope profile
          example: 1516239022
        given_name:
          type: string
          description: Given name of the user for scope profile
          example: "Ola"
        family_name:
          type: string
          description: Family name of the user for scope profile
          example: "Nordmann"
        birthdate:
          type: string
          description: Birthdate of the user for scope profile
          example: "1970-01-01"

    IdTokenWebAuthnKeyRegistrationSchema:
      type: object
      properties:
        bound_key_id:
          type: string
          description: Key id of the registered key
          example: "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"

    IdTokenIdentifierSchema:
      type: object
      properties:
        id_nnin:
          type: string
          description: National identity number of the user
          example: "01010112345"
        sub_nnin:
          type: string
          description: National identity number of the user
          example: "01010112345"
        id_bankid:
          type: string
          description: BankID oid of the user
          example: "01010112345"
        sub_bankid:
          type: string
          description: BankID oid of the user
          example: "01010112345"

    IdTokenSchema:
      allOf:
        - $ref: "#/components/schemas/IdTokenBaseSchema"
        - $ref: "#/components/schemas/IdTokenProfileSchema"
        - $ref: "#/components/schemas/IdTokenWebAuthnKeyRegistrationSchema"
        - $ref: "#/components/schemas/IdTokenIdentifierSchema"

  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
