Setting up authentication via deep link
Deep links allow the user to authenticate in the Web Shop through the game in one click, instead of going through the authentication process via user ID or Xsolla Login.
If the game is installed on the user’s mobile device, they will be redirected to the game via deep link to get an authorization token.
User flow
In the mobile app
- An unauthorized user in the Web Shop clicks the login button or the buy button. A modal window for entering the user ID or logging in through the mobile game opens.
- The user clicks the login button through the game.
- The user is redirected to the game where they are automatically redirected to Web Shop as an authorized user.
In the desktop version of the game
- An unauthorized user in the Web Shop clicks the login button or the buy button. A modal window for entering the user ID or logging into the mobile version of the game using a QR code opens.
- The user scans the QR code using their mobile device, after which the Web Shop opens on their mobile device.
- The mobile version of the game opens on the user's mobile device.
- The user is automatically redirected to Web Shop on the mobile device as an authorized user.
Services interaction flow
How to set up
On the game side
- In your game’s mobile app settings, register a URL scheme to open the game via deep link:
- in Android applications — in the
AndroidManifest.xml
file - in iOS applications — in the
Info.plist
file
- in Android applications — in the
gamename://authorize
, where:gamename
— the name of your game that should open on a mobile device for user authentication.authorize
— an example of an action name that should be performed after the gamename game opens. Use the action name that matches the actions in your application’s operating system.
Copy
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<string>gamename</string>
</array>
</dict>
</array>
Example of registering a URL scheme in Android applications:
Copy
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="gamename" android:host="authorize" />
</intent-filter>
- Implement the generation of an authorization token in JWT format using the user ID from the game.
Parameter | Type | Description |
---|---|---|
loginId | string | ID of the authorization method from the Publisher Account. Required. |
webhookUrl | string | URL for receiving the User validation in Web Shop webhook. The URL should match the address specified when setting up authentication via user ID. Xsolla expects a 200 HTTP code in response to the webhook to authorize the Web Shop user. If Xsolla receives a response with a 404 HTTP code or does not receive a response, the user will not be authorized.You can specify the value "webhookUrl": "https://nowebhook.com" to disable webhooks when authenticating via deep link. In this case, Xsolla does not check for the existence of the user, so make sure that in the user.id parameter you pass the user ID that exists in the game.Required. |
settings.projectId | string | Project ID found in Publisher Account, which is specified next to the name of your project in browser address bar. The URL has the following format: https://publisher.xsolla.com/merchant ID/Publisher Account section . Required. |
user.id | string | User ID in the game. Required. |
user.name | string | User name. Required. |
Example of calling the user token generation API method using curl:
Copy
- curl
curl --location 'https://sb-user-id-service.xsolla.com/api/v1/user-id' \
--header 'Content-Type: application/json' \
--data '{
"loginId": "000001aa-001a-0ab0-00001-01a01a01a01a",
"webhookUrl": "https://nowebhook.com",
"settings": {
"projectId": 123456,
"merchantId": 123456
},
"user": {
"id": "123",
"name": "a-user-name"
}
}'
You will get a response like {token=“JWT_TOKEN”}
.
- Add a pop-up window with a notification of successful authorization (optional).
- Implement the opening of the Web Shop in the browser using the obtained user token.
Example of creating a URL for opening the Web Shop in the browser for an authorized user:
https://example.com/?token={token}
, if you are using a custom domainhttps://example.xsollasitebuilder.com/?token={token}
, if you are using an Xsolla domain
{token}
is the user’s authorization token.Within Site Builder
You can perform the settings on the Site Builder side yourself, using the code examples provided below.For setup assistance, contact your Customer Success Manager or email to csm@xsolla.com.
Note
First set up authentication via User ID. This is necessary so that users have an alternative way of authentication if deep link authentication is not available. For example, if the game is not installed on a user’s mobile device.
- Open the project in the Publisher Account.
- In the side menu, click Site Builder.
- Click Configure on the card of your Web Shop site with authentication via User ID.
- Add deep link authentication using a Custom code block.
- In the Custom code block on the JavaScript tab add the custom code below, which implements deep link authentication for the mobile and desktop versions.
- Replace the values of the constants in the code with your values, where:
DEEPLINK_URL
— URL scheme for opening the game via deep link.QR_CODE
— QR code for opening the game in SVG format. The QR code should contain a link to the Web Shop with thesource = pc
parameter.deeplink__description
— description that is displayed under the game authorization button in the mobile application.qr-code-auth__description
— description displayed under the QR code in the desktop version of the game.
Copy
- js
{
const DEEPLINK_URL = 'gamename://authorize';
const QR_CODE = `YOUR_SVG_WITH_QR_CODE`;
handleQRCodeRedirect();
// Event handlers are set up for opening the authentication modal or for successful authentication.
// If the event of opening the authentication modal occurs, the handler is triggered and a button or QR code is added to this window.
// If the event of successful authentication occurs, the handler is triggered and the successful authentication modal is opened.
window.addEventListener('custom-event:modal:render', updateLoginModal);
window.addEventListener('custom-event:authorization:login', showLoginSuccessPopup);
let getToken;
const searchParams = new URLSearchParams(window.location.search);
const tokenParam = searchParams.get('token');
if (tokenParam) {
showLoginSuccessPopup(tokenParam);
}
window.SB.subscribe(api => {
getToken = api.getToken;
});
// Customization of the authentication modal window (adding a QR code for the desktop version of the game or a login button for the mobile version of the game).
function updateLoginModal({detail}) {
const {name, element} = detail;
if (name !== 'user-id' || element.dataset.isQrAdded === 'true') return;
const isMobile = checkMobileDevice();
const ModalBody = element.querySelector('.ui-site-modal-window__body');
const ModulInput = ModalBody.querySelector('.user-id-modal__input');
element.classList.toggle('mobile-layout', isMobile);
ModalBody.append(getTemplate(isMobile));
element.dataset.isQrAdded = true;
}
// Function to display the successful authentication modal window.
function showLoginSuccessPopup(t) {
let username = 'Gamer';
try {
const token = getToken?.() || t;
if (token) {
const jwtData = parseJwt(token);
const payload = JSON.parse(jwtData.payload);
if (payload.userInfo.name) {
username = payload.userInfo.name;
} else if (jwtData.username) {
username = jwtData.username;
} else if (jwtData.server_custom_id) {
username = jwtData.server_custom_id;
}
}
} catch (e) {
console.log('error', e);
}
const Modal = document.querySelector('.deeplink-auth');
const ModalTitle = Modal.querySelector('.deeplink-auth__title');
ModalTitle.textContent = ModalTitle.textContent.replace(/\{username}/g, username);
Modal.classList.add('_visible');
Modal.querySelector('.deeplink-auth__button').addEventListener('click', closeModal);
function closeModal() {
Modal.classList.remove('_visible');
}
}
// Function that displays a QR code for the desktop version of the game or a login button for the mobile version of the game. Your_game should be replaced with the actual name of your game. To continue with the authentication, the game has to be installed on the user's phone.
function getTemplate(isMobile) {
const Deeplink = document.createElement('div');
if (isMobile) {
Deeplink.innerHTML = `
<div class="deeplink__title"><span class="deeplink__title-text">or login via game</span></div>
<a href="${DEEPLINK_URL}" class="deeplink__button" rel="noopener">Login via Mobile Game</a>
<div class="deeplink__description">
You need to have the Your_game installed on your phone in order to proceed with authorization and purchase
</div>
`;
return Deeplink;
}
Deeplink.innerHTML = `
<div class="separator">
<span class="separator__text">or login via game</span>
</div>
<div class="qr-code-auth">
<div class="qr-code-auth__image">${QR_CODE}</div>
<span class="qr-code-auth__description">
Scan the QR code with your phone and you will continue purchasing on your mobile device
</span>
</div>
`;
return Deeplink;
}
function checkMobileDevice() {
return (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent));
}
// Function that decodes the JWT token and returns its payload as a JavaScript object.
function parseJwt(token) {
const base64Url = token.split('.')[1];
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
const jsonPayload = decodeURIComponent(window.atob(base64).split('').map(function (c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
}).join(''));
return JSON.parse(jsonPayload);
}
// Function to verify the vent occurred when the user scanned the QR code.
function handleQRCodeRedirect() {
const url = new URL(window.location.href);
const source = url.searchParams.get('source');
if (source === 'pc') {
if (checkMobileDevice()) {
const button = document.getElementById("deeplink-redirect");
button.click();
}
setTimeout(function () {
url.searchParams.delete('source');
window.history.pushState({}, '', url);
}, 2000);
}
}
}
- In the Custom code block on the HTML tab, add code for a modal window indicating successful authorization in the Web Shop. You can set your own text values.
Copy
- html
<div class="ui-site-modal-window deeplink-auth">
<div class="deeplink-auth__body">
<div class="deeplink-auth__image"></div>
<div class="deeplink-auth__title">Hi, {username}!</div>
<div class="deeplink-auth__description">You’ve successfully logged in to the Web Shop</div>
<button class="deeplink-auth__button">Continue</button>
</div>
</div>
<a href="gamename://authorize" id="deeplink-redirect" style="display: none;"></a>
- In the Custom code block on the CSS tab, add styles for new interface components. Recommended styles are suggested below. Specify the URL of your own image displayed upon successful authorization.
Copy
- css
--deeplink-success-image-url: url("Successful_authorization_image_url");
.html-v2:has(.deeplink-auth._visible) {
z-index: var(--modalsZIndex);
}
.user-id-modal__content {
padding: 0 16px !important;
}
@media (min-width: 768px) {
.user-id-modal__content {
max-width: 460px !important;
padding: 0 3.8rem 4rem !important;
overflow: auto;
justify-content: center;
}
}
.mobile-layout .user-id-modal__close {
right: 8px;
top: 16px;
}
.user-id-modal__logo {
width: 109px;
height: 38px;
margin: 4rem auto 2rem;
}
.user-id-modal.mobile-layout .user-id-modal__logo {
margin: 3rem auto 2.5rem;
}
.user-id-modal__wrapper {
margin-top: 0;
}
.user-id-modal__title {
margin-bottom: 3rem;
}
.user-id-modal__input-wrapper .ui-site-input {
height: 41px;
}
.user-id-modal__input {
margin-top: 0;
border-radius: var(--border-radius-16);
}
.user-id-modal__button {
height: auto;
margin: 12px auto 0;
padding: 1.25rem;
border-radius: var(--border-radius-16);
}
.user-id-modal__button .simple-button__text {
font-size: 1.8rem;
font-weight: 500;
line-height: 2.5rem;
letter-spacing: 0.5px;
}
.user-id-modal .ui-site-instruction-cut__title .ui-site-description {
margin-bottom: 3rem;
font-size: 1.2em;
}
.user-id-modal .ui-site-instruction-cut__steps {
overflow-y: visible;
}
.user-id-modal .ui-site-instruction-card:first-child {
margin-top: 0;
}
.user-id-modal .ui-site-instruction-card:last-child {
padding-bottom: 4rem;
}
.user-id-modal .separator {
display: flex;
margin-bottom: 3.8rem;
align-items: center;
}
.user-id-modal .separator__text {
padding: 0 1.5rem;
font-family: var(--main-font);
font-size: 1.8rem;
font-weight: 500;
line-height: 2.5rem;
letter-spacing: 0.8px;
}
.user-id-modal .separator::before,
.user-id-modal .separator::after {
content: "";
background-color: #797979;
height: 1px;
flex-grow: 1;
}
.user-id-modal .qr-code-auth__image {
display: block;
width: 160px;
height: 160px;
margin: 0 auto 20px;
}
.user-id-modal .qr-code-auth__image svg {
display: block;
width: 100%;
height: 100%;
}
.user-id-modal .qr-code-auth__description {
display: inline-block;
font-family: var(--main-font);
font-size: 1.5rem;
font-weight: 400;
line-height: 2.5rem;
text-align: center;
color: rgba(255, 255, 255, 0.7);
}
.deeplink__title {
display: flex;
margin-bottom: 3.8rem;
align-items: center;
}
.deeplink__title-text {
padding: 0 1.5rem;
font-family: var(--main-font);
font-size: 1.8rem;
font-weight: 500;
line-height: 2.5rem;
letter-spacing: 0.8px;
}
.user-id-modal .deeplink__title::before,
.user-id-modal .deeplink__title::after {
content: "";
background-color: #797979;
height: 1px;
flex-grow: 1;
}
.deeplink__button {
display: block;
margin-bottom: 2.2rem;
padding: 1.3rem;
font-family: var(--main-font);
font-size: 1.8rem;
font-weight: 500;
line-height: 2.5rem;
letter-spacing: 0.5px;
text-align: center;
color: var(--button-text-color);
background: var(--accent-color);
border-radius: var(--border-radius-16);
text-decoration: none;
transition: transform .2s cubic-bezier(.509, .001, .25, 1);
}
.deeplink__button:hover {
transform: scale(.95);
}
.deeplink__description {
margin-bottom: 2rem;
font-family: var(--main-font);
font-size: 1.6rem;
line-height: 2.3rem;
text-align: center;
color: rgba(255, 255, 255, 0.7);
letter-spacing: 0.5px;
}
.deeplink__cta-wrapper {
display: flex;
flex-direction: row;
gap: 1.8rem;
padding: 0 1.2rem;
}
.deeplink__cta-wrapper .ui-site-calltoaction__item {
margin: 0;
flex-grow: 2;
}
.deeplink__cta-wrapper .ui-site-calltoaction {
width: 100%;
min-width: auto;
height: auto;
min-height: auto;
padding-bottom: 36%;
background-size: contain;
border-radius: 0;
}
.deeplink-auth {
display: none;
}
.deeplink-auth._visible {
display: flex;
}
.deeplink-auth__body {
position: relative;
width: 100%;
max-width: 336px;
padding: 16px;
border-radius: 12px;
background-color: var(--colors-core-background-secondary-rgb);
border: var(--border-size) solid var(--border-color);
font-family: var(--main-font);
color: var(--colors-core-text-primary);
}
@media (min-width: 768px) {
.deeplink-auth__body {
max-width: 460px;
padding: 24px 30px;
}
}
.deeplink-auth__image {
margin-bottom: 20px;
padding-bottom: 49%;
border-radius: 12px;
background-image: var(--deeplink-success-image-url);
background-repeat: no-repeat;
background-position: center;
background-size: cover;
}
@media (min-width: 768px) {
.deeplink-auth__image {
padding-bottom: 37%;
}
}
.deeplink-auth__title {
margin-bottom: 10px;
font-size: 20px;
font-weight: 500;
line-height: 24px;
text-align: center;
letter-spacing: 0.7px;
}
@media (min-width: 768px) {
.deeplink-auth__title {
margin-bottom: 20px;
font-size: 24px;
line-height: 30px;
}
}
.deeplink-auth__description {
margin-bottom: 20px;
font-size: 13px;
font-weight: 400;
line-height: 18px;
text-align: center;
letter-spacing: 0.5px;
color: rgba(255, 255, 255, 0.7);
}
.deeplink-auth__button {
display: block;
width: 100%;
padding: 1.3rem;
font-family: var(--main-font);
font-size: 1.8rem;
font-weight: 500;
line-height: 2.5rem;
letter-spacing: 0.5px;
text-align: center;
color: var(--button-text-color);
background: var(--accent-color);
border-radius: var(--border-radius-16);
text-decoration: none;
transition: transform .2s cubic-bezier(.509, .001, .25, 1);
}
@media (min-width: 768px) {
.deeplink-auth__button {
padding: 12px;
font-size: 16px;
line-height: 24px;
}
}
.deeplink-auth__button:hover {
transform: scale(.95);
}
Was this article helpful?
Thank you for your feedback!
We’ll review your message and use it to help us improve your experience.Rate this page
Rate this page
Don’t want to answer
Thank you for your feedback!
Found a typo or other text error? Select the text and press Ctrl+Enter.