--- title: "Authenticate cardholders using the Authentication SDK" description: "Use our Authentication SDK for out-of-band authentication of payments made with Adyen-issued cards." url: "https://docs.adyen.com/issuing/3d-secure/oob-auth-sdk/authenticate-cardholders" source_url: "https://docs.adyen.com/issuing/3d-secure/oob-auth-sdk/authenticate-cardholders.md" canonical: "https://docs.adyen.com/issuing/3d-secure/oob-auth-sdk/authenticate-cardholders" last_modified: "2023-07-12T13:49:00+02:00" language: "en" --- # Authenticate cardholders using the Authentication SDK Use our Authentication SDK for out-of-band authentication of payments made with Adyen-issued cards. [View source](/issuing/3d-secure/oob-auth-sdk/authenticate-cardholders.md) Each time your cardholder makes a transaction that requires 3D Secure, you need to authenticate the cardholder. The authentication time frame is valid for 10 minutes. The following are the steps you need to do to authenticate the cardholder. 1. [Set up a webhook endpoint](#step1-set-up-webhook-endpoint). 2. [Get data from the relayed authentication webhook](#step2-receive-auth-webhooks). 3. [Authenticate the cardholder](#step3-auth-authenticate-cardholder) using the information from the relayed authentication webhook. 4. [Finalize the cardholder authentication](#step4-finalize-auth) using the 3D secure challenge ID from the webhook and the SDK output from the authentication step. 5. [Get updates](#get-updates) on the outcome of the authentication using the cardholder authenticated webhook. ## Set up and accept relayed authentication webhooks Your integration must be able to receive and process webhooks for relayed authentication requests. The flow to set up relayed authentication webhooks is as follows: 1. You create an endpoint on your server that: * Can receive a JSON object. * Has an open TCP port for HTTPS traffic on port **443**, **8443**, or **8843**. * Can handle basic authentication. 2. You send the endpoint URL to your Adyen contact. 3. Adyen does the following: 1. If necessary, we enroll your balance platform into [ACS](/get-started-with-adyen/adyen-glossary/#access-control-server-acs). 2. We subscribe your balance platform to the relayed authentication webhook. 3. Based on your preference, we prioritize either the [out of band (OOB)](/issuing/3d-secure/oob-auth-sdk/) flow, or the [one-time password (OTP)](/issuing/3d-secure/password-otp/) flow. 4. Adyen creates a ws user for your balance platform, and shares its basic auth credentials with you. You can use these credentials to access ACS. If you want to change your basic auth credentials, you must reach out to your Adyen contact. When we send an authentication webhook, your server must respond to the webhook within 2 seconds. Otherwise, the authentication process fails. To learn more about setting up webhooks, see [Set up webhooks](/development-resources/webhooks/configure-and-manage). ## Get data from the authentication webhooks When the cardholder makes a transaction that requires authentication, we send a webhook to your server. When you receive the webhook: The webhook contains the following parameters: 1. From the header and body of the webhook, gather the data that you will need in the following steps of the authentication process. The following table shows the header parameter. | Header parameter | Description | | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `www-authenticate` | **SCA realm="Challenge" authparm1="{sdkInput}"**, where `sdkInput` is a Base64-encoded blob of data to be passed to the SDK in the [next step](#step3-auth-authenticate-cardholder). | **Authentication webhook header** ```bash WWW-authenticate: SCA realm="Challenge" authparm1="{sdkInput}" ``` The following table shows the request body parameters. | Request body parameter | Description | | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------- | | `id` | A unique reference of the 3D Secure challenge. | | `paymentInstrumentId` | The unique identifier of the payment instrument. Use this field to direct the authentication request to the correct cardholder. | | `purchase` | An object containing the details of the purchase, such as date, merchant name, and amount. | **Authentication webhook body** ```json { "id": "778a7bd0-0f08-4d0b-b6ec-76c3a47f775c", "paymentInstrumentId": "PI3227J223222B5F3FXD4B5MJ", "purchase": { "date": "2022-06-22T08:00:00.000000+02:00", "merchantName": "Merchant", "originalAmount": { "value": 1000, "currency": "EUR" } } } ``` 2. Acknowledge the webhook with the **HTTP 200** response that you previously [set up](#step1-set-up-webhook-endpoint).\ The following example shows an **HTTP 200** response. **Acknowledge the webhook** ```json { "authenticationDecision": { "status": "proceed" } } ``` If we do not receive the response message within 2 seconds or receive `status` set to **refused**, the authentication process will stop. For more information about webhook setup and best practices, see [Set up webhooks](/development-resources/webhooks/configure-and-manage). ## Authenticate cardholder Your application must display the [data from the previous step](#step2-receive-auth-webhooks) to the cardholder during the authentication. We recommend that you implement a push notification and use the data from the webhook to inform the cardholder about the details of the transaction they are authenticating for. After being prompted on the merchant interface, the cardholder proceeds to the application. Authenticate your cardholder using the SDK. To do so: 1. Trigger the SDK to start cardholder authentication and pass the `sdkInput` value from the previous step. ### Tab: Kotlin **Authenticate cardholder using the SDK** ```kotlin lifecycleScope.launch { if (adyenAuthentication.hasCredential("sdkInput")) { // Authenticate existing credential val authenticationResult: AuthenticationResult = adyenAuthentication.authenticate("sdkInput") when (authenticationResult) { is AuthenticationResult.AuthenticationSuccessful -> { authenticationResult.sdkOutput } is AuthenticationResult.Canceled -> { // Cardholder cancelled the authentication flow } is AuthenticationResult.Error -> { // Unexpected error authenticationResult.errorMessage } is AuthenticationResult.AuthenticationError -> { // FIDO API Error authenticationResult.authenticationError } } } else { // None of the known credentials exist in this device } } ``` ### Tab: Swift **Authenticate cardholder using the SDK** ```swift delegatedAuthenticationSession.authenticate(withBase64URLString: sdkInput) { [weak self] result in switch result { case let .success(sdkOutput): /// send the sdkOutput to the backend case let .failure(error): /// authentication failed } } ``` 2. If the authentication is successful, get the Base64-encoded `sdkOutput` generated by the SDK. 3. Pass `sdkOutput` to your server. In case of any error that occurs at this stage, reach out to your Adyen contact. ## Finalize authentication You must finalize the authentication within 10 minutes of receiving the request. To finalize the authentication: 1. From your server, make a PATCH `/challenges` request. Use the basic auth credentials you received from Adyen when you [set up the relayed authentication webhook](#set-up-and-accept-relayed-authentication-webhooks). In your request, specify the following parameters: | Parameter | Parameter type | Description | | ------------------ | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `id` | Path | The `id` value that you receive in the body of the relayed [authentication webhook](#step2-receive-auth-webhooks). | | `www-authenticate` | Header | Set `SCA realm` to **Challenge**. Set `authparm1` with the Base64-encoded value of **sdkOutput** from [cardholder authentication](#step3-auth-authenticate-cardholder). | | `completed` | Body | Set to **true**. | The following example shows how to finalize an authentication process. **Finalize authentication** ```bash curl https://balanceplatform-api-live.adyen.com/acs/v1/challenges/{id} \ -H 'Authorization: ACS_BASIC_AUTH_CREDENTIALS' \ -H 'content-type: application/json' \ -H "www-authenticate: SCA realm="Challenge" authparm1="eyJjaGFsbGVuZ2UiOiJiVlV6ZW5wek0waFNlQzF..."" \ -X PATCH \ -d '{ "completed": true }' ``` 2. Verify that the response contains the following: * The `id` of the challenge * `challengeCompleted`: **true**\ This indicates that the cardholder has been authenticated and the transaction will continue.\ Otherwise, refer to [Troubleshooting](/issuing/3d-secure/troubleshooting) The following example shows a response for a finalized authentication. **Response for a finalized authentication** ```json { "id": "778a7bd0-0f08-4d0b-b6ec-76c3a47f775c", "challengeCompleted": true } ``` ## Get updates You can use the cardholder authenticated webhook to get notified about the status and outcome of the cardholder's 3D secure authentication. Regardless of outcome of the authentication process, we send the [balancePlatform.authentication.created](https://docs.adyen.com/api-explorer/acs-webhook/latest/post/balancePlatform.authentication.created) webhook. To keep track of webhooks, make sure that your server can [receive and accept webhooks](/development-resources/webhooks/configure-and-manage). The [balancePlatform.authentication.created](https://docs.adyen.com/api-explorer/acs-webhook/latest/post/balancePlatform.authentication.created) webhook contains the following information. ### Tab: Authenticated (frictionless flow) **Successful authentication via the frictionless flow** ```json { "data": { "authentication": { "acsTransID": "6a4c1709-a42e-4c7f-96c7-1043adacfc97", "challengeIndicator": "01", "createdAt": "2022-12-22T15:45:03+01:00", "deviceChannel": "app", "dsTransID": "a3b86754-444d-46ca-95a2-ada351d3f42c", "exemptionIndicator": "lowValue", "inPSD2scope": true, "messageCategory": "payment", "messageVersion": "2.2.0", "riskScore": 0, "threeDSServerTransID": "6edcc246-23ee-4e94-ac5d-8ae620bea7d9", "transStatus": "Y", "type": "frictionless" }, "balancePlatform": "YOUR_BALANCE_PLATFORM", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "paymentInstrumentId": "PI3227C223222B5BPCMFXD2XG", "purchase": { "date": "2022-12-22T15:49:03+01:00", "merchantName": "TeaShop_NL", "originalAmount": { "currency": "EUR", "value": 1000 } }, "status": "authenticated" }, "environment": "test", "type": "balancePlatform.authentication.created" } ``` ### Tab: Authenticated (challenge flow) **Successful authentication via the challenge flow** ```json { "data": { "authentication": { "acsTransID": "6a4c1709-a42e-4c7f-96c7-1043adacfc97", "challenge": { "flow": "OOB", "lastInteraction": "2022-12-22T15:49:03+01:00" }, "challengeIndicator": "01", "createdAt": "2022-12-22T15:45:03+01:00", "deviceChannel": "app", "dsTransID": "a3b86754-444d-46ca-95a2-ada351d3f42c", "exemptionIndicator": "lowValue", "inPSD2scope": true, "messageCategory": "payment", "messageVersion": "2.2.0", "riskScore": 0, "threeDSServerTransID": "6edcc246-23ee-4e94-ac5d-8ae620bea7d9", "transStatus": "Y", "type": "challenge" }, "balancePlatform": "YOUR_BALANCE_PLATFORM", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "paymentInstrumentId": "PI3227C223222B5BPCMFXD2XG", "purchase": { "date": "2022-12-22T15:49:03+01:00", "merchantName": "TeaShop_NL", "originalAmount": { "currency": "EUR", "value": 1000 } }, "status": "authenticated" }, "environment": "test", "type": "balancePlatform.authentication.created" } ``` ### Tab: Rejected (retries exceeded) **Rejected authentication due to exceeded number of retries** ```json { "data": { "balancePlatform": "YOUR_BALANCE_PLATFORM", "creationDate": "2023-01-19T17:07:59+01:00", "id": "a8fc7a40-6e48-498a-bdc2-494daf0f490a", "authentication": { "acsTransId": "a8fc7a40-6e48-498a-bdc2-494daf0f490a", "challenge": { "flow": "OTP_SMS", "lastInteraction" : "2023-01-19T17:37:13+01:00", "phoneNumber" : "******6789", "resends" : 0, "retries" : 2 }, "challengeIndicator": "01", "createdAt": "2023-01-19T17:07:17+01:00", "deviceChannel": "app", "dsTransID": "59de4e30-7f84-4a77-aaf8-1ca493092ef9", "exemptionIndicator": "noExemptionApplied", "inPSD2Scope": "false", "messageCategory": "payment", "messageVersion": "2.2.0", "threeDSServerTransID": "8bc0fdbd-5c8a-4bed-a171-9d10347e7798", "transStatus": "N", "transStatusReason": "19", "type": "challenge" }, "paymentInstrumentId": "PI3227C223222B5BPCMFXD2XG", "purchase": { "date": "2022-12-22T15:49:03+01:00", "merchantName": "TeaShop_NL", "originalAmount": { "currency": "EUR", "value": 1000 } }, "status": "rejected" }, "environment": "test", "type": "balancePlatform.authentication.created" } ``` ## Next steps Learn about the payment stages and authorisation in particular. [Payment stages](/issuing/payment-stages) [Learn about the payment stages and when funds are added to or deducted from the balance account.](/issuing/payment-stages) [required](/issuing/authorisation) [Payment authorisation](/issuing/authorisation) [Approve or decline payments by using transaction rules or relayed authorisation.](/issuing/authorisation)