openapi: 3.0.3
info:
  title: Device Signature Verification MFA
  description: 'The DEVICE SIGNATURE VERIFICATION APIs allow clients to initiate and verify an authentication challenge by signing the challenge id with an existing device. This functionality enables secure verification before proceeding with sensitive operations.'
  version: '1'
  x-plumery-audit-action-source: https://capabilities.plumery.com/mfa-device-signature/api
servers:
- url: 'https://api.plumery.com'
  description: Live Server
security:
- cookieAuth: [ ]
paths:
  '/internal/v1/mfa/device-signature/{challengeId}:initiate':
    post:
      tags:
        - InternalChallenge
      operationId: Initiate device signature for step-up authentication internally
      summary: internalInitiateDeviceSigningStepUp
      x-plumery-audit-action-type: InternalDeviceSignatureStepUpInitiated
      x-plumery-audit-action-name: Internal Device Signature StepUp Initiated
      x-plumery-audit-action-description: Internal device signature was successfully initiated for step-up authentication
      x-internal: true
      description: |-
        Creates a pending device signature. 
        
        Then, on the mobile app, the user has to complete the device signature process:
        * Retrieve the pending device signature using the external 'Get pending device signatures'
        * Initiate the device signature using the external 'Initiate pending device signature' endpoint
        * Verify the device signature using the external 'Verify pending device signature' endpoint

        After the verification is successful, the status can be polled internally using the 'Confirm device signature' endpoint.
      parameters:
        - name: challengeId
          in: path
          required: true
          schema:
            type: string
          description: |
            The ID is challenge id received during operation initiation which required challenge.
      responses:
        '200':
          description: Signing session initiated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InternalInitiateDeviceSigningResponse'
        '400':
          description: Invalid request payload provided
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Device not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  '/internal/v1/mfa/device-signature:initiate':
    post:
      tags:
        - InternalChallenge
      operationId: Initiate device signature for any content internally
      summary: internalInitiateDeviceSigningGeneric
      x-plumery-audit-action-type: InternalDeviceSignatureGenericInitiated
      x-plumery-audit-action-name: Internal Device Signature Generic Initiated
      x-plumery-audit-action-description: Internal device signature was successfully initiated for any content
      x-internal: true
      description: |-
        Creates a pending device signature. 
        
        Then, on the mobile app, the user has to complete the device signature process:
        * Retrieve the pending device signature using the external 'Get pending device signatures'
        * Initiate the device signature using the external 'Initiate pending device signature' endpoint
        * Verify the device signature using the external 'Verify pending device signature' endpoint

        After the verification is successful, the status can be polled internally using the 'Confirm device signature' endpoint.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/InitiateDeviceSigningGenericRequest'
      responses:
        '200':
          description: Signing session initiated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InternalInitiateDeviceSigningResponse'
        '400':
          description: Invalid request payload provided
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Device not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  '/internal/v1/mfa/device-signature/{challengeId}:confirm':
    post:
      tags:
        - Challenge
      operationId: Confirm device signature
      summary: confirmDeviceSignature
      x-plumery-audit-action-type: DeviceSignatureConfirmed
      x-plumery-audit-action-name: Device Signature Confirmed
      x-plumery-audit-action-description: Device signature was successfully confirmed
      description: |-
        Confirms the pending device signature.
        
        Internal device signatures have to be verified using the external 'Verify pending device signature' endpoint.
        After the external verification is successful, this endpoint can be used to confirm the verification.
        
        This endpoint can be polled while waiting for the verification on the mobile app.
      parameters:
        - name: challengeId
          in: path
          required: true
          schema:
            type: string
          description: |
            The ID is challenge id received during operation initiation which required challenge.
      responses:
        '200':
          description: Returns the current status of the device signature
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConfirmDeviceSignatureResponse'
        '400':
          description: Invalid request payload provided
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Device signature not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error occurred
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InternalServerError'
        default:
          description: Unexpected error
      x-internal: true
components:
  schemas:
    InitiateDeviceSigningGenericRequest:
      type: object
      properties:
        signableContent:
          type: string
          description: The content to be signed
        confirmationMessage:
          type: string
          description: A confirmation message to be displayed to the user on the mobile app
        username:
          type: string
          description: The username of the user who is performing the signing
        source:
          type: string
          description: The source of the content to be signed
      required:
        - signableContent
        - confirmationMessage
        - username
        - source
    InternalInitiateDeviceSigningResponse:
      type: object
      properties:
        challengeId:
          type: string
          description: Challenge id for which the device signature is pending.
          example: 1b2a754f-4e6a-4bf8-9f5a-9d84a52b0a11
      required:
        - challengeId
    InternalServerError:
      enum:
        - 'urn:plmr:mfa:device-signature:INTERNAL_SERVER_ERROR'
      example: 'urn:plmr:mfa:device-signature:INTERNAL_SERVER_ERROR'
    ErrorCode:
      enum:
        - 'urn:plmr:mfa:device-signature:BAD_REQUEST'
        - 'urn:plmr:mfa:device-signature:RESOURCE_UNKNOWN'
        - 'urn:plmr:mfa:device-signature:UNAUTHORIZED'
        - 'urn:plmr:mfa:device-signature:FORBIDDEN'
        - 'urn:plmr:mfa:device-signature:DEVICE_NOT_FOUND'
        - 'urn:plmr:mfa:device-signature:INCORRECT_SIGNATURE'
        - 'urn:plmr:mfa:device-signature:STEP_UP_REQUEST_CONTEXT_NOT_FOUND'
        - 'urn:plmr:mfa:device-signature:USER_ID_DOES_NOT_MATCH_WITH_CHALLENGE_SUBJECT_USER_ID'
        - 'urn:plmr:mfa:device-signature:SIGNING_SESSION_NOT_INITIATED_OR_EXPIRED'
        - 'urn:plmr:mfa:device-signature:DEVICE_PASSCODE_SIGNING_NOT_ENABLED'
        - 'urn:plmr:mfa:device-signature:DEVICE_BIOMETRICS_SIGNING_NOT_ENABLED'
        - 'urn:plmr:mfa:device-signature:USER_NOT_FOUND'
        - 'urn:plmr:mfa:device-signature:SIGNING_CONFIRMATION_MESSAGE_TEMPLATE_NOT_FOUND'
        - 'urn:plmr:mfa:device-signature:SIGNING_CONFIRMATION_MESSAGE_TEMPLATE_EVALUATION_FAILED'
        - 'urn:plmr:mfa:device-signature:PENDING_DEVICE_SIGNATURE_ALREADY_EXISTS_FOR_CHALLENGE_ID'
        - 'urn:plmr:mfa:device-signature:PENDING_DEVICE_SIGNATURE_NOT_FOUND'
        - 'urn:plmr:mfa:device-signature:USER_ID_DOES_NOT_MATCH_WITH_CONTEXT_USER_ID'
        - 'urn:plmr:mfa:device-signature:DEVICE_ID_QUERY_PARAM_MANDATORY'
        - 'urn:plmr:mfa:device-signature:USER_ID_DOES_NOT_MATCH_WITH_DEVICE_USER'
      type: string
      example: 'urn:plmr:mfa:device-signature:UNAUTHORIZED'
    FieldErrorCode:
      enum:
        - 'urn:plmr:mfa:device-signature:NOT_BLANK'
        - 'urn:plmr:mfa:device-signature:NOT_NULL'
      type: string
    FieldError:
      type: object
      properties:
        code:
          $ref: '#/components/schemas/FieldErrorCode'
        message:
          type: string
        field:
          type: string
    ErrorResponse:
      type: object
      properties:
        code:
          $ref: '#/components/schemas/ErrorCode'
        message:
          type: string
        requestId:
          type: string
        fieldErrors:
          type: array
          items:
            $ref: '#/components/schemas/FieldError'
    DeviceSignatureStatus:
      enum:
        - 'PENDING'
        - 'COMPLETE'
    ConfirmDeviceSignatureResponse:
      type: object
      properties:
        status:
          $ref: '#/components/schemas/DeviceSignatureStatus'
  securitySchemes:
    cookieAuth:
      type: apiKey
      in: cookie
      name: auth-session