Implementing Frictionless Login#
Overview#
Frictionless Login is a streamlined authentication method designed to provide users access to your platform without requiring a password during each login session. This approach enhances user experience and security by leveraging existing authentication tokens, device trust, or 3rd-parties like Facebook OAuth2.
Difficulty Rating: ⭐☆☆☆☆
Key Points#
Token-based Authentication: Automatically recognize users by their authentication tokens stored securely on their devices.
Device Trust: Employ mechanisms to identify and trust users’ devices to skip repetitive authentication steps.
OAuth, SSO: Enable users to log in using third-party identity providers like Facebook.
Enhanced Security: Reduce the risk of password-related security vulnerabilities.
Improved UX: Simplify the login process to increase user engagement and retention.
Tutorial Steps#
Device Login#
Device login allows your application to use the unique device identifier (e.g., IMEI on mobile) to perform automatic account registration and login. When a device attempts login for the first time:
A new account is automatically created with the username being the device ID.
An email address is generated as
<device_id>@device.xsolla.cloud.A special user secret of type
deviceis created using a deterministic hash of the device information.
This ensures the device can log in securely and safely in subsequent sessions. During later logins, the device ID and secret hash are used to authorize the user.
The following example demonstrates how to perform a device login:
1CoreSDK->LoginDevice().then([](pplx::task<void> task)
2{
3 try
4 {
5 // Force the exception to be re-thrown if an error occurred.
6 task.get();
7 }
8 catch (const xbe::sdk::Exception& e)
9 {
10 // Handle error here
11 }
12});
1try
2{
3 await CoreSDK.LoginDevice();
4}
5catch (Exception error)
6{
7 // Handle error here
8}
1try
2{
3 await CoreSDK.loginDevice();
4}
5catch (error: any)
6{
7 // Handle error here
8}
1FOnlineSubsystemXBE* OSS = (FOnlineSubsystemXBE*)(IOnlineSubsystem::Get(XBE_SUBSYSTEM));
2check(OnlineSub != nullptr);
3const IOnlineIdentityPtr IdentityInterface = OnlineSub->GetIdentityInterface();
4check(IdentityInterface.IsValid());
5
6FDelegateHandle LoginDelegateHandler;
7auto LoginDelegate = FOnLoginCompleteDelegate::CreateLambda([=](
8 int32 InLocalUserNum,
9 bool bWasSuccessful,
10 const FUniqueNetId& UserId,
11 const FString& Error)
12{
13 if (Error.Len() > 0)
14 {
15 // Handle error here
16 }
17
18 IdentityInterface->ClearOnLoginCompleteDelegate_Handle(InLocalUserNum, LoginDelegateHandler);
19});
20
21LoginDelegateHandler = IdentityInterface->AddOnLoginCompleteDelegate_Handle(0, LoginDelegate);
22IdentityInterface->AutoLogin(0);
1try
2{
3 XBECoreSDK SDK = XBECoreSDK.GetInstance();
4 await SDK.Instance.LoginDevice();
5}
6catch (Exception error)
7{
8 Debug.LogError($"Failed device login. Error={error.Message}");
9}
Update User#
After some time, you may offer the user the ability to customize their account. This typically occurs at the end of a play session or through an on-demand user interface. The recommended minimum information to request is their email address, with optional fields such as:
Email (required)
First Name
Last Name
Phone Number
Username
The following example assumes all the above fields are prompted and provided. Note that the user must already be logged in to perform this action:
1auto user = CoreSDK->GetLoggedInUser();
2user->SetFirstName(_XPLATSTR("John"));
3user->SetLastName(_XPLATSTR("Smith"));
4user->SetEmail(_XPLATSTR("john.smith@gmail.com"));
5user->SetName(_XPLATSTR("john.smith"));
6user->SetPhone(_XPLATSTR("+1 213-555-1234"));
7
8auto service = CoreSDK->GetServiceFactory<xbe::sdk::services::UserService>();
9service->Update(user->GetUid(), user).then([=]
10 pplx::task<std::shared_ptr<xbe::sdk::models::User>> task)
11{
12 try
13 {
14 user = task.get();
15 }
16 catch (const xbe::sdk::Exception& e)
17 {
18 // Handle error here
19 }
20});
1User user = CoreSDK.LoggedInUser;
2user.FirstName = "John";
3user.LastName = "Smith";
4user.Email = "john.smith@gmail.com";
5user.Name = "john.smith";
6user.Phone = "+1 213-555-1234";
7
8UserService userService = CoreSDK.ServiceFactory.GetService<UserService>();
9try
10{
11 await userService.Update(user.Uid, user);
12}
13catch (Exception error)
14{
15 // Handle error here
16}
1const user: User = CoreSDK.LoggedInUser;
2user.firstName = "John";
3user.lastName = "Smith";
4user.email = "john.smith@gmail.com";
5user.name = "john.smith";
6user.phone = "+1 213-555-1234";
7
8const userService: UserService = ServiceFactory.getService(UserService);
9try
10{
11 await userService.update(user.uid, user);
12}
13catch (error: any)
14{
15 // Handle error here
16}
1const FOnlineSubsystemAXR* OnlineSub = (FOnlineSubsystemAXR*)Online::GetSubsystem(GetWorld());
2check(OnlineSub != nullptr);
3
4auto user = OnlineSub->CoreSDK->GetLoggedInUser();
5user->SetFirstName(_XPLATSTR("John"));
6user->SetLastName(_XPLATSTR("Smith"));
7user->SetEmail(_XPLATSTR("john.smith@gmail.com"));
8user->SetName(_XPLATSTR("john.smith"));
9user->SetPhone(_XPLATSTR("+1 213-555-1234"));
10
11auto service = OnlineSub->CoreSDK->GetServiceFactory<xbe::sdk::services::UserService>();
12service->Update(user->GetUid(), user).then([=](
13 pplx::task<std::shared_ptr<xbe::sdk::models::User>> task)
14{
15 try
16 {
17 user = task.get();
18 }
19 catch (const xbe::sdk::Exception& e)
20 {
21 // Handle error here
22 }
23});
1User user = CoreSDK.LoggedInUser;
2user.FirstName = "John";
3user.LastName = "Smith";
4user.Email = "john.smith@gmail.com";
5user.Name = "john.smith";
6user.Phone = "+1 213-555-1234";
7
8UserService userService = CoreSDK.ServiceFactory.GetService<UserService>();
9try
10{
11 await userService.Update(user.Uid, user);
12}
13catch (Exception error)
14{
15 // Handle error here
16}
Once this operation is complete, the user will receive an email asking them to verify their email address.
Create a Password#
For the user to log in to their account from another device, it is necessary to create a password. This can be done in two ways:
At the time of updating the account information (as in the update-user step).
Implicitly during the email verification step, where the user is prompted to create a password by clicking the verification link and completing the process via the Xsolla Backend Admin Console or a custom website.
The following example covers the first case, where a password is created after customizing the account information:
1auto secret = std::make_shared<xbe::sdk::models::UserSecret>();
2secret->SetType(xbe::sdk::models::UserSecret::Type::TYPE_PASSWORD);
3secret->SetSecret(_XPLATSTR("<PASSWORD>"));
4secret->SetUserId(CoreSDK->GetLoggedInUser()->GetUid());
5
6auto service = CoreSDK->GetServiceFactory<xbe::sdk::services::UserSecretService>();
7service->Create(secret).then([=]
8 pplx::task<std::shared_ptr<xbe::sdk::models::UserSecret>> task)
9{
10 try
11 {
12 task.get();
13 }
14 catch (const xbe::sdk::Exception& e)
15 {
16 // Handle error here
17 }
18});
1UserSecret secret = new();
2secret.Type = UserSecret.TYPE_MFA;
3secret.Secret = "<PASSWORD>";
4
5UserSecretService service = CoreSDK.ServiceFactory.GetService<UserSecretService>();
6try
7{
8 await service.Create(secret);
9}
10catch (Exception error)
11{
12 // Handle error here
13}
1const secret: UserSecret = new UserSecret();
2secret.type = UserSecret.TYPE_MFA;
3secret.secret = "<PASSWORD>";
4
5const service: UserSecretService = ServiceFactory.getService(UserSecretService);
6try
7{
8 await service.Create(secret);
9}
10catch (Exception error)
11{
12 // Handle error here
13}
1const FOnlineSubsystemAXR* OnlineSub = (FOnlineSubsystemAXR*)Online::GetSubsystem(GetWorld());
2check(OnlineSub != nullptr);
3
4auto secret = std::make_shared<xbe::sdk::models::UserSecret>();
5secret->SetType(xbe::sdk::models::UserSecret::Type::TYPE_PASSWORD);
6secret->SetSecret(_XPLATSTR(password));
7secret->SetUserId(CoreSDK->GetLoggedInUser()->GetUid());
8
9auto service = OnlineSub->CoreSDK->GetServiceFactory<xbe::sdk::services::UserSecretService>();
10service->Create(secret).then([=](
11 pplx::task<std::shared_ptr<xbe::sdk::models::UserSecret>> task)
12{
13 try
14 {
15 task.get();
16 }
17 catch (const xbe::sdk::Exception& e)
18 {
19 // Handle error here
20 }
21});
1UserSecret secret = new();
2secret.Type = UserSecret.TYPE_MFA;
3secret.Secret = "<PASSWORD>";
4
5UserSecretService service = CoreSDK.ServiceFactory.GetService<UserSecretService>();
6try
7{
8 await service.Create(secret);
9}
10catch (Exception error)
11{
12 // Handle error here
13}
Once a password is set, the user can use it for future logins on new devices. Note that the original device will continue to use automatic login without requiring re-authentication.
Conclusion#
The Frictionless Login tutorial provides a streamlined approach to authentication within the Xsolla Backend ecosystem. By implementing token-based methods, leveraging device trust, and integrating with third-party identity providers like OAuth2, developers can create a seamless login experience tailored to the needs of modern gaming platforms.
The outlined steps — Device Login, User Updates, and Password Creation — demonstrate how to efficiently manage user authentication, ensuring both ease of access and enhanced security. These features are designed to improve player engagement and retention.
Use Cases#
Quickstarts: Allow users to seamlessly log in without entering passwords on trusted devices.
Sessions: Simplify login for returning users using session cache.
Gaming Platforms: Enable quick access for players by integrating game network accounts.
Enterprise Systems: Create admin consoles by enabling Single Sign-On (SSO) for employees across multiple platforms.