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

  1. 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.
  1. The user clicks the login button through the game.
  2. The user is redirected to the game where they are automatically redirected to Web Shop as an authorized user.

  1. 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.

  1. The user scans the QR code using their mobile device, after which the Web Shop opens on their mobile device.
  2. The mobile version of the game opens on the user's mobile device.
  3. The user is automatically redirected to Web Shop on the mobile device as an authorized user.
  1. 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
After registering the scheme, when the user authenticates in the Web Shop via the game, the game should open at the specified address. Example of a URL scheme to register a game: 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.
Example of registering a URL scheme in iOS applications:
Copy
Full screen
Small screen
    <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
    Full screen
    Small screen
      <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>
      

      1. Implement the generation of an authorization token in JWT format using the user ID from the game.

      ParameterTypeDescription
      stringID of the authorization method from the Publisher Account. Required.
      stringURL 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.
      stringProject 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.
      stringUser ID in the game. Required.
      stringUser name. Required.

      Example of calling the user token generation API method using curl:

      Copy
      Full screen
      Small screen
      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”}.

      1. Add a pop-up window with a notification of successful authorization (optional).
      2. 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 domain
      • https:​//example.xsollasitebuilder.com/?token={token}, if you are using an Xsolla domain
      Where {token} is the user’s authorization token.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.
      1. Open the project in the Publisher Account.
      2. In the side menu, click Site Builder.
      3. Click Configure on the card of your Web Shop site with authentication via User ID.
      4. Add deep link authentication using a Custom code block.
      5. 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.
      1. 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 the source = 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
      Full screen
      Small screen
      {
          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);
          }
        }
      }
      
      1. 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
      Full screen
      Small screen
      <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>
      
      1. 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
      Full screen
      Small screen
      --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!
      Is there anything we can improve? Message
      We’re sorry to hear that
      Please explain why this article wasn’t helpful to you. Message
      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
      Is there anything we can improve?

      Don’t want to answer

      Thank you for your feedback!
      Last updated: April 10, 2024

      Found a typo or other text error? Select the text and press Ctrl+Enter.

      Report a problem
      We always review our content. Your feedback helps us improve it.
      Provide an email so we can follow up
      Thank you for your feedback!