Authentication
Introduction
The Authentication service is responsible for handling authentication related operations.
Prerequisites
Before integrating the Authentication Service, ensure you have:
- A valid Paysafe account with appropriate permissions.
- Set up instructions for Paysafe SDK Android or iOS.
To get started, initialize the AuthenticationService
instance in your app as shown below:
- Kotlin
- Swift
import com.paysafe.wallet.android.core.wallet.Wallet
val authenticationService = Wallet.getInstance().getAuthenticationService()
import PaysafeWallet
let authentication = Wallet.instance.authenticationService
Get Authentication Details Feature in development
Retrieves the authentication details, including the URL that is used to start the Authorization Code Flow.
The returned URL is Authorization endpoint as defined in RFC 6749 Section 3.1. This endpoint allows clients to securely authenticate into the Embedded Wallet infrastructure by generating an authorization code RFC 6749 Section 4.1, which can then be exchanged for an access token. For an enhanced security all clients must use the PKCE extension with the SHA-256 code challenge method.
Authenticate User with Authorization Code
In order to authenticate a user with a token that was generated through the Authorization Code process, the following steps are in place:
- Retrieve Authentication Details: The AuthenticationDetails are retrieved by calling the getAuthenticationDetails method of the
AuthenticationService
. - Redirect user to Authentication UI: Opens the URL in an external user-agent - web browser or Android Custom Tabs/
SFSafariViewController
. - User Authentication: The user authenticates through the Paysafe Authentication UI by submitting their credentials and they are redirected back to the Partner Application with code and state parameters in the URL as query parameters.
- (OPTIONAL) Configure SDK: If the partner application has been reloaded, the SDK must be configured again by calling the configure method of the Wallet.
- State Verification: The state parameter is verified by the client to ensure that it matches the one generated in step 1.
- Issue Token: The user is authenticated through the Paysafe Wallet API on the partner backend by providing the received code parameter, which results in receiving an accessToken.
- Authenticate User: The accessToken is passed to the SDK, which authenticates the user - Getting Started on iOS and Getting Started on Android
Authorization Code Flow should only be made through external user-agent - web browser or Android Custom Tabs/SFSafariViewController
.
Full security recommendations are outlined in RFC 8252.
- Kotlin
- Swift
import com.paysafe.wallet.android.core.wallet.Wallet
val state = "random-string"
val codeVerifier = "random-alphanumeric-string-64-characters"
val codeChallenge = "hashed-and-base-64-url-encoded-code-verifier"
val authenticationDetailsRequest = AuthenticationDetailsRequest(
clientId = "client-identifier",
codeChallenge = codeChallenge,
resetCredentialsUrl = "https://www.paysafe.com/reset-credentials",
state = state,
locale = "en-GB",
loginHint = "someone@partner.com",
loginStrategy = LoginStrategy.PIN,
redirectUri = "http://www.partner.com/wallet/dashboard",
)
val authenticationDetails =
wallet.getAuthenticationService().getAuthenticationDetails(
brandIdentity = "brand-identity",
request = authenticationDetailsRequest,
)
val authenticationUrl = authenticationDetails.authenticationUrl
val uri = Uri.parse(authenticationUrl)
val customTabsIntent = CustomTabsIntent.Builder().build()
customTabsIntent.launchUrl(this, uri)
import PaysafeWallet
import CryptoKit
import SafariServices
let state = "random-string"
let codeVerifier = "random-alphanumeric-string-64-characters"
let codeChallengeHash = SHA256.hash(data: Data(codeVerifier.utf8))
let codeChallenge = Data(codeChallengeHash).base64URLEncodedString()
let request = Wallet.AuthenticationDetailsRequest(
clientIdentifier: "client-identifier",
codeChallenge: codeChallenge,
resetCredentialsURL: "https://www.paysafe.com/reset-credentials",
state: state,
redirectURI: "http://www.partner.com/wallet/dashboard",
locale: "en-US",
loginHint: "someone@partner.com",
loginStrategy: .pin // or .password
)
Wallet.instance.authenticationService.getAuthenticationDetails(brandIdentity: "brand-identity",
request: request,
completion: { result in
switch result {
case .success(let response):
let safariViewController = SFSafariViewController(url: response.authenticationURL)
present(safariViewController, animated: true)
case .failure(let error):
// Handle error
break
}
})
Handle Strong Customer Authentication (SCA) Challenges
Some wallet operations require Strong Customer Authentication and will trigger a process as described in Strong Customer Authentication.
Step 1.
Once the eventId
and walletOperationId
parameters are available, the first step is to initiate the SCA challenge with the method sendScaChallenge
, specifying
the preferred SCA verification method from the list of availableVerifications
. SCA verification methods with channel REDIRECT
also require returnUrl
provided in action parameter.
Use the same method to resend the challenge if the customer does not receive the first one.
Step 2.
When the customer enters the requested SCA code, the second step is to use the method submitScaAttempt
to solve the SCA challenge. Provide
the same eventId
, walletOperationId
, method, and channel. The response from submitScaAttempt
is an object of type
ScaAuthenticationEventAttemptEmbeddedHybridResponse / ScaAuthenticationEventAttemptEmbeddedHybridResponse,
containing a status
and statusReason
fields. The status
will be Wallet.SCAAuthenticationEventAttemptStatus.verified / ScaAuthenticationEventAttemptStatus.VERIFIED for successful attempt and
Wallet.SCAAuthenticationEventAttemptStatus.failed / ScaAuthenticationEventAttemptStatus.FAILED for invalid value.
A WalletError.dataError / DataException will be
returned if the customer reaches the maximum number of unsuccessful attempts.
Step 3.
Once the SCA challenge is solved, repeat the requested wallet operation, providing the scaDetails
optional parameter.
Pass the same eventId
and walletOperationId
, that was used to solve the challenge.
- Kotlin
- Swift
// Obtain eventId and walletOperationId from the operation that requires SCA
val eventId = "..."
val walletOperationId = "..."
// 1. Instruct the system to send an one-time password (OTP) code via SMS
authenticationService.sendScaChallenge(
eventId = eventId,
request = ScaAuthenticationEventChallengeEmbeddedHybridRequest(
walletOperationId = walletOperationId,
verification = ScaAuthenticationEventAttemptVerification(
ScaAuthenticationEventAttemptVerificationMethod.OTP,
ScaAuthenticationEventAttemptVerificationChannel.SMS
)
)
)
// 2. Submit the one-time password entered by the customer
authenticationService.submitScaAttempt(
eventId = eventId,
request = ScaAuthenticationEventAttemptEmbeddedHybridRequest(
walletOperationId = walletOperationId,
verification = ScaAuthenticationEventAttemptVerification(
ScaAuthenticationEventAttemptVerificationMethod.OTP,
ScaAuthenticationEventAttemptVerificationChannel.SMS
),
value = "123456" // Replace with actual OTP value
)
)
// 3. Retry the wallet operation, providing the scaDetails parameter with the same eventId and walletOperationId.
// Obtain eventId and walletOperationId from the operation that requires SCA
let eventID = "..."
let walletOperationID = "..."
// 1. Instruct the system to send an one-time password (OTP) code via SMS
authenticationService.sendSCAChallenge(
eventID: eventID,
request: Wallet.SCAAuthenticationEventChallengeEmbeddedHybridRequest(
walletOperationID: walletOperationID,
verification: Wallet.SCAAuthenticationEventAttemptVerification(
method: .otp,
channel: .sms
)
),
completion: { result in
switch result {
case .success(let scaEventChallenge):
// Display information about scaEventChallenge
case .failure(let error):
// Handle error
}
}
)
// 2. Submit the one-time password entered by the customer
authenticationService.submitSCAAttempt(
eventID: eventID,
request: Wallet.SCAAuthenticationEventAttemptEmbeddedHybridRequest(
walletOperationID: walletOperationID,
verification: Wallet.SCAAuthenticationEventAttemptVerification(
method: .otp,
channel: .sms
),
value: "123456" // Replace with actual OTP value
),
completion: { result in
switch result {
case .success(let scaEventAttempt):
// Handle result of scaEventAttempt
case .failure(let error):
// Handle error
}
}
)
// 3. Retry the wallet operation, providing the scaDetails parameter with the same eventId and walletOperationId.