Skip to main content

Transaction Confirmation Guide

Overview#

Transaction confirmation is a way to dynamically capture customer consent specific for a given action, which provides verifiable proof the customer consented to a given action using FIDO certified authentication.

Transaction confirmation requires a backend API call, therefore it is recommended to leverage the LoginID server SDK. However, the LoginID APIs can be called directly if required.

Prerequisites#

  • Create an application on LoginID Dashboard
  • Download Client SDK
  • Download Server SDK (optional)

With Server SDK#

Step 1: Create Transaction#

When the contents of the transaction is finalized, you must call LoginID to generate a transaction identifier.

info

The tx_payload should be persisted on your backend to be able to verify the transaction at the end of the flow.

Server SDK: .createTx(tx_payload, nonce?)

If desired, you can include a unique identifier for the transaction in the form of a nonce.

This method creates the transaction in our system, and returns a tx_id. This identifier must be passed to your front end for use in Step 2.

Step 2: Confirm Transaction#

Client SDK: .confirmTransaction(tx_id)

This method will prompt the customer to authenticate using their FIDO2 credential. When the user authenticates, the user’s private key signs the tx_challenge, generating a verifiable signature. The tx_challenge is a combination of the hash of the tx_payload, nonce, and server_nonce.

LoginID stores the signature, nonce, server_nonce, and payload hash for 2 years to allow for clients to resolve dispute claims.

The result of this operation is a transaction token, in the form of a JWT signed by LoginID.

note

The LoginID SDK does not display the raw transaction information to the user, it only prompts for FIDO authentication. It is your responsibility to display the transaction information.

Step 3: Verify Result#

The JWT returned in Step 2 can be validated through the Server SDK.

Server SDK: .verifyTransaction(jwt)

With Client SDK Only#

Step 1: Create and Confirm Transaction#

When the details of the transaction are finalized, you can call LoginID with the transaction information, and the SDK will prompt the user for verification and return back a signed token.

Client SDK: .createAndConfirmTransaction(username, tx_payload, nonce?, authorization_token?)

info

The tx_payload should be persisted on the client backend to be able to verify the transaction at the end of the flow.

If an API credential is assigned to this integration, you must first generate an authorization token. You can do that using the .generateTxAuthToken server SDK method or by generating the token on your backend yourself. If a token is provided, a nonce must not be provided.

This method will prompt the customer to authenticate using their FIDO2 credential. When the user authenticates, the user’s private key signs the tx_challenge, generating a verifiable signature. The tx_challenge is a combination of the payload_hash, nonce, and server_nonce.

LoginID stores the signature, nonce, server_nonce, and payload hash for 2 years to allow for clients to resolve dispute claims.

The result of this operation is a transaction token, in the form of a JWT signed by LoginID.

note

The LoginID SDK does not display the raw transaction information to the user, it only prompts for FIDO authentication. It is your responsibility to display the transaction information.

Step 2: Verify Result#

The JWT returned in Step 1 can be validated in two ways: using the server SDK or the LoginID public key. The server SDK method is:

Server SDK: .verifyTransaction(tx_token, tx_payload)

The LoginID public key can be obtained:

curl https://jwt.[base url]/certs?kid=​<key id here>

where the kid is obtained from the JWT header:

{
  "alg": "ES256",
  "typ": "JWT",
  "kid": "<example kid>"
}

Once the public key has been obtained, this can be used to verify the original transaction payload. The JWT returned from the createAndConfirmTransaction SDK methods is of the form:

{
  "iss": "loginid.io",
  "sub": "d285bd82-6da8-47dd-a8d5-af44cb462faf",
  "aud": "a93791e15ef07929",
  "nid": "a93791e15ef07929",
  "iat": <issue date of the token>,
  "username": "<username>",
  "action": "tx_confirmation",
  "nonce": "<client generated nonce, only included in response if provided in request>​",
  "server_nonce": "<loginID generated nonce>",
  "tx_hash": "<hased transaction value>"
}

The tx_hash is generated using the following formula:

  • If nonce is provided:
    • tx_hash = base64url(sha256(tx_payload + nonce + server_nonce))
  • If nonce is not provided:
    • tx_hash = base64url(sha256(tx_payload + server_nonce))

Where the tx_payload value is the tx_payload sent in the createAndConfirmTransaction method.

Calling APIs Directly (i.e. no Server SDK)#

Step 1: Create Transaction#

An Auth token must be created before calling the LoginID endpoint to initiate the transaction confirmation flow. This token is sent as the Authorization header.

{
  "scope": "tx.create",
  "nonce": <generated nonce value>,
  "payload_hash": <hashed and encoded raw_tx_payload>,
  "iat": <long unix time in seconds>
}

where payload_hash is the base64 URL safe encoded value of the hashed (SHA256) tx_payload. The tx_payload is the transaction information in string format.

info

The tx_payload should be persisted on the client backend to be able to verify the transaction at the end of the flow.

Auth token header:

{
  "alg": "ES256",
  "typ": "JWT"
}

Once the token is created, it must be signed with the ES256 algorithm using your API_PRIVATE_KEY.

Request:

POST /tx

Header:

Authorization: Bearer <auth_token>

Body:

{
  "client_id": <string >
  "tx_payload": <string>
  "nonce": <string>
}

Response:

tx_id

Step 2: Confirm Transaction#

Client SDK: .confirmTransaction(tx_id)

This method will prompt the customer to authenticate using their FIDO2 credential. When the user authenticates, the user’s private key signs the tx_challenge, generating a verifiable signature. The tx_challenge is a combination of the hash of the tx_payload, nonce, and server_nonce.

LoginID stores the signature, nonce, server_nonce, and hashed tx_payload for 2 years to allow for clients to resolve dispute claims.

The result of this operation is a transaction token, in the form of a JWT signed by LoginID.

note

The LoginID SDK does not display the raw transaction information to the user, it only prompts for FIDO authentication. It is your responsibility to display the transaction information.

Step 3: Verify result#

The JWT returned in Step 2 can be validated using LoginID's public key, which can be obtained:

curl https://jwt.[base url]/certs?kid=<key id here>

For example, the URL for the LoginID sandbox environment is:

curl https://jwt.sandbox-usw1.api.loginid.io/certs?kid=​<key id here>

where the kid is obtained from the JWT header:

{
  "alg": "ES256",
  "typ": "JWT",
  "kid": "<example kid>"
}

Once the public key has been obtained, this can be used to verify the original transaction payload. The JWT returned from the create/confirm SDKs methods is of the form:

{
  "iss": "loginid.io",
  "sub": "d285bd82-6da8-47dd-a8d5-af44cb462faf",
  "aud": "a93791e15ef07929",
  "nid": "a93791e15ef07929",
  "iat": <issue date of the token>,
  "username": "<username>",
  "action": "tx_confirmation",
  "nonce": "<client generated nonce>​",
  "server_nonce": "<loginID generated nonce>",
  "tx_hash": "<hashed transaction value>"
}

The tx_hash is generated using the following formula:

tx_hash = base64url(sha256(tx_payload + nonce + server_nonce))

Where the tx_payload value is the tx_payload sent in the POST /tx call.