Unity Integration with the Cloud

In this tutorial we will demonstrate the complete pipeline on integrating Unity with the web services provided in MAGES 3.2.

We will start of by creating a product for the Sample App through the Web Portal, and then create a user and assign him a valid product license.

After the online walkthrough is completed, we will proceed to integrate the Login service with the Sample App Unity side. Additionally, we will provide a configuration for uploading the user analytics to the AnalyticsAPI.

The end goal of this tutorial is to make you product-ready, meaning that you are able to build your VR Module and authenticate/check out any user licenses you want. Further, you will be able to upload user analytics to the AnalyticsAPI and display them in the Web Portal.

Tutorial Overview:


Prerequisites

Note

For brevity, we assume in this tutorial that you have set up and running a Development –localhost environment of the Web Services.

In any case, it is easy to translate the following steps to the equivalent Production configuration.


Product & User Creation

The first step is to login to the portal with an Admin user account.

Point your browser to localhost:4200 and you will be redirect to the Login Service https://localhost:44355 for authentication.

Upon successful login, you will be redirected to the Portal Dashboard as below:

Portal Dashboard

Add New Product

Proceed to expand the Admin menu item from the main sidebar, and further the child Products menu item as depicted in the figure:

Portal Admin Menu

From the Products menu, you can either click on the List Products menu item, or on the Add Product menu item.

List Products Option

If you clicked on the List Products you will see an overview of all your Products as the one below:

Portal List Products

Click on the Plus Icon button on the right and a new table row will appear with an inline form. There, go on to create a product with the following configuration:

{
    "ProductName": "Test2021",
    "ProductFormalName": "This is the formal name of the product",
    "DefaultLicense": "None",
    "Playable": "True"
}

Click on the Check Icon on the right and a successful notification will appear on the top right of the page!

Note

Notice that the ID field is grayed out, this will be populated by the Login Service.

Add Product Option

If you clicked on the Add Product menu item, you will end up in the following page:

Add New Product

Here is a dedicated form that has also predefined options for the DefaultLicense and the Playable fields, instead of writing these info by hand.

Fill it in with the values in the JSON snippet above and click the Submit button to create the product.

User Creation

After the Product is created, navigate to the Manage Users expandable item and click on the Create User menu item.

The following stepper form will load:

Add New User

With this form you can create a user and optionally assign him product licenses.

Step 1.

Proceed to fill in the Personal Details of the dummy user as you see fit.

Step 2.

In the Account Details step, create a username and a password for your user.

Warning

When creating a user manually through the Portal, an email confirmation link will not be sent to the user’s email address!

The email address is considered Confirmed for the Login Service.

By default, we assume admins are able to correctly register their users. This also helps testing user creation, because you do not have to enter a valid email address.

Step 3.

If the password meets the Validator, you can proceed to select the User Roles. If you user is a typical VR-only application user, select the User role only.

Otherwise, you can proceed to add more roles – but recall, roles are inclusive! (e.g. An admin has all three roles assigned, etc.)

Step 4.

Select the Organization this user belongs to. For simplicity, select your own organization name or the Default one if applicable.

Step 5.

In the final step, we assign the user a license of the product we created earlier.

Make sure you input a date in the future and the DefaultLicense of the user is set to Unlimited as below:

Add New User

Note

Unlimited means that the user can access the VR application frivolously, until the expiration day.

Go ahead and click the Submit button in the Step 5. to create the user. If everything went smoothly, you should see a green Notification bar at the top right corner.

Your user is now created with a product license attached! Congrats!


MAGES SDK Sample App

After the product and the user is set, let’s move on to the SDK Sample App for the integration part with the Login service.

Open your MAGES SDK project in Unity and select the Sample App scene from ovidVRcomp/Operation/Scenes/.

Enable User Login Window

First, we need to make sure the checkbox Enable User Login Window For Build is checked in the Unity Editor.

To do so, expand the Scene Manager component and click on the Scene Graph component.

On the Inspector you can see if the Enable User Login Window For Build checkbox is ticked, as in the figures below:

Enable local window for login

This variable is set in the scenegraph.cs script and will make sure that when you build your VR application, the Login window prefab will spawn automatically, asking for user authentication before the user can access your application!

Login Flow UI

By default, the prefab of the Login window is set to UILicenseRequestSSO.prefab under Resources/ovidVRres/Operation/Prefabs/UI 2.0/.

Login SSO Prefab Login SSO Prefab

Notice that the UI prefab offers 3 available options for user sign in & authentication.

  1. Authenticate by typing Username & Password and pressing the Login button on the right.


  1. Authenticate by clicking on the Login with SSO button. This will open and point the user’s browser at your Login service, where the user can sign in either with username/password credentials or by clicking on a SingleSignOn button (e.g. Google). Then, the user will be presented with a 4-digit code that has to fill in at the next screen in the picture below.

4-digit Prefab
  1. If already at the 4-digit code on the browser, the user can click on the Enter SSO 4-DIGIT Code button on the bottom right to directly enter the 4-digit code.


Client Configurations

Now we need to set the Client configurations to connect Unity with the Login service.

There are two distinct parts that need to be defined, each for a separate purpose:

  1. User Authentication with Username & Password (w/o SingleSignOn)

  2. User Authentication via browser with SSO capabilities

To properly work with the provided UI Login prefab, you need to configure both settings.

However, if you only wish to authenticate users with either of the two methods, you can simply edit the prefab and remove one of the options.

User Authentication with Username & Password

For the first part, you need to open the LicenseRequest.cs script that is under the ovidVRcomp/SDKAddons/ovidVRScripts/License/ directory.

Navigate to the Login function at line 162, it should be identical to the one below:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public void Login()
{

    if ((!string.IsNullOrEmpty(userField.text) && !string.IsNullOrEmpty(passwordField.text)))
    {
        loginButton.ButtonActivation(false);
        StartCoroutine(FadeMusic(false));
        StartCoroutine(DelayLogin());
    }

    #if UNITY_STANDALONE_WIN
        KeyboardController.SetKeyboardState(true);
    #endif
    user = userField.text;
    pass = passwordField.text;

    #if !UNITY_EDITOR
        // Login Flow and Checkout User License
        ClientConfiguration client = new ClientConfiguration()
        {
            ClientId = "",
            ClientSecret = "",
        };
        var identityUrl = "";
        AuthenticationHandler.Instance.LoginUserWithoutSSO(client, identityUrl, user, pass, "SampleApp", result =>
        {
            if (result == LoginStatus.Success)
            {
                Debug.Log("User authenticated");
                hasLic = true;
                cameraRig.enabled = true;
                Destroy(this.gameObject);
            }
            else
            {
                Debug.Log("Error authentication");
                Debug.Log(result);
                Init(true);
            }
        });
    #endif
    InterfaceManagement.Get.applicationLanguage = selectedLang;
}

The Login function performs the Sign In directly from the UI prefab, with the use of Username & Password.

To do so, it utilizes the Resource Owner Password and Client Credentials (ROPC) grant type as described in the official IdentityServer4 documentation.

Note

ROPC is considered a valid client only for trusted and native (legacy) applications.

You should use it only for your Unity projects, and not for web-based applications, as we do on the Portal.

You need to populate the configuration between lines 19 and 25.

To get you started, we have already defined a similar Client to the Login service inside the Config.cs file at the root directory.

For brevity, we will use the same configuration with ClientId: UnityModuleWithoutSSO.

Danger

Make sure to alter the ClientSecret value to a secure and private one, never use the defaults provided in a Production setting!

We proceed to replace lines 19-25 with the following:

// Login Flow and Checkout User License
ClientConfiguration client = new ClientConfiguration()
{
    ClientId = "UnityModuleWithoutSSO",
    ClientSecret = "unity.module.without.sso.2020",
};
var identityUrl = "https://localhost:44355/";
AuthenticationHandler.Instance.LoginUserWithoutSSO(client, identityUrl, user, pass, "Test2021", result =>
{
    if (result == LoginStatus.Success)
    {
        Debug.Log("User authenticated");
        hasLic = true;
        cameraRig.enabled = true;
        Destroy(this.gameObject);
    }
    else
    {
        Debug.Log("Error authentication");
        Debug.Log(result);
        Init(true);
    }
});

Notice also, the parameter Test2021 we passed in function LoginUserWithoutSSO matches the ProductName of the one we created through the Portal.

Essentially, this function will authenticate the user with given credentials from the prefab’s text boxes (i.e., username and password) and checkout a user license for the product Test2021. Additionally, the underlying service will attempt to retrieve the User Data from the Login Service, that you can further utilize to upload analytics or populate your COOP, etc.

If any of the above steps fails, you will be prompted with an error message, and the Login screen will remain in place.

If the operation was successful, the Login screen will self-destroy and the user shall be able to continue with your module.

User Authentication via Browser (SSO support)

Let’s proceed to configure our settings for the second enriched Login flow that provides us with SSO capabilities.

In the same code file LicenseRequest.cs, navigate to the LoginSSO function, it should be identical to the one below:

public void LoginSSO()
{
    // Login Flow and Checkout User License
    ClientConfiguration client = new ClientConfiguration()
    {
        ClientId = "",
        ClientSecret = "",
        AllowedScopes = ""
    };
    var identityUrl = "";
    var loopbackUrl = "";
    AuthenticationHandler.Instance.LoginUserBrowser(client, identityUrl, loopbackUrl, result =>
    {
    });
    Spawn4DigitUI();
}

When a user clicks on the Login with SSO button, this function will spawn up a browser and point to your Login service for authentication.

The delegate is empty due to the fact that it will only spawn a browser page, and does not need to handle anything on return.

Sequentially, the 4DigitUI will be spawned, waiting for the user to enter his 4-Digit code.

To get you started, we have already defined a similar Client to the Login service inside the Config.cs file at the root directory.

For brevity, we will use the same configuration with ClientId: UnityModule.

Proceed to replace the snippet above with the following:

Danger

Make sure to alter the ClientSecret value to a secure and private one, never use the defaults provided in a Production setting!

public void LoginSSO()
{
    // Login Flow and Checkout User License
    ClientConfiguration client = new ClientConfiguration()
    {
        ClientId = "UnityModule",
        ClientSecret = "unity.module.2020",
        AllowedScopes = "openid profile offline_access IdentityServerApi"
    };
    var identityUrl = "https://localhost:44355/";
    var loopbackUrl = "https://localhost:44355/unity";
    AuthenticationHandler.Instance.LoginUserBrowser(client, identityUrl, loopbackUrl, result =>
    {
    });
    Spawn4DigitUI();
}

When a user logs in through the browser, he will be redirected to the unity endpoint defined in the Login service, which will serve him with the 4-Digit code, as in the example below:

4-digit Prefab

Then, the user heads back to Unity and enter the code.

Note

Each code expires after 3 minutes even though it states 2. This is to handle sloths.

Notice also, that no license checkout happens at this place. The checkout will happen when the user enters the 4-digit code at Unity and clicks on the Verify button.

Final step - 4Digit Code

To complete the flow, we also need to provide configurations for the 4DigitUI.prefab.

Proceed to open the VRNumpadController.cs file under the ovidVRcomp/SDKAddons/VRKeyboard/Scripts/ directory.

Navigate to CheckCode function and it should be identical to the one below:

public void CheckCode()
{
    string code = GameObject.Find("Digit1/Placeholder").GetComponent<Text>().text + GameObject.Find("Digit2/Placeholder").GetComponent<Text>().text + GameObject.Find("Digit3/Placeholder").GetComponent<Text>().text + GameObject.Find("Digit4/Placeholder").GetComponent<Text>().text;

    ClientConfiguration client = new ClientConfiguration()
    {
        ClientId = "",
        ClientSecret = "",
        AllowedScopes = ""
    };
    var identityUrl = "";
    AuthenticationHandler.Instance.CheckoutUser(client, identityUrl, code, "SDK", result =>
    {
        if (result == LoginStatus.Success)
        {
            Debug.Log("User authenticated");
            LicenseRequest.hasLic = true;
            GameObject.Find("UILicenseRequestSSO(Clone)").GetComponent<LicenseRequest>().cameraRig.enabled = true;
            Destroy(GameObject.Find("UILicenseRequestSSO(Clone)"));
            Destroy(GameObject.Find("4DigitUI(Clone)"));
        }
        else
        {
            Debug.Log("Error authentication");
            Debug.Log(result);
            _header.GetComponent<Text>().text = "The 4-Digit Code was incorrect.";
        }
    });
}

To make sure that our application is authenticated and no one else can start posting up 4-digit codes, we use an additional Client for this request.

As before, to get you started, we have already defined a similar Client to the Login service inside the Config.cs file at the root directory.

For brevity, we will use the same configuration with ClientId: UnityInternal.

Proceed to update the following fields like below:

public void CheckCode()
{
    ClientConfiguration client = new ClientConfiguration()
    {
        ClientId = "UnityInternal",
        ClientSecret = "unity.internal.2020",
        AllowedScopes = "IdentityServerApi"
    };
    var identityUrl = "https://localhost:44355/;
    AuthenticationHandler.Instance.CheckoutUser(client, identityUrl, code, "Test2021", result =>
    {
        // redundant
    });
}

And we are done!

But before you go, notice also here that we have to provide the ProductName we are checking out against, as in the Username/Password flow.

Further, the UnityInternal client authenticates/protects an API resource with Client credentials only (i.e., ClientID, ClientSecret), make sure to check it out on the official IdentityServer4 documentation.

Now you can proceed to build a version of the SampleApp (whether Windows standalone or Android), and you can checkout your users product licenses ;).

Otherwise, keep reading for more.


User Account Manager

By default, and for convenience, we populate the UserAccountManager class with the authenticated user information.

This includes username, the JWT token, and other optional values such as first and last name.

Therefore, you don’t need to acquire any user information throughout the whole application experience. Everything is there for you to utilize as you see fit.


Uploading User Analytics

Last but not least, to upload user analytics we have to configure Scenegraph.cs script to populate your Upload with the info from UserAccountManager described above.

Proceed to open Scenegraph.cs and navigate to line 174.

It should be identical to the one here:

// Configuration properties that regard post Scenegraph initilization such as Analytics, Users go here.
//Configuration.User = new ApplicationUser
//{
//    id = "app-user",
//    firstName = "User",
//    lastName = "User",
//    country = "Devland",
//    userName = "proplayer"
//};
//Configuration.UserPassword = ("default");

// For uploading analytics specify your endpoint and add any extra form fields or headers required from your service.
Configuration.OnlineURL = "";
//Configuration.OnlineURL = "";
Configuration.FormFields = new List<AnalyticsExporter.FormField>
{
    new AnalyticsExporter.FormField { key = "Username", value = UserAccountManager.Get.GetUsername() },
    new AnalyticsExporter.FormField { key = "Operation", value = "SDK" },
};
Configuration.HeaderKeys = new List<AnalyticsExporter.HeaderKey>
{
    new AnalyticsExporter.HeaderKey { key = "Authorization", value = "Bearer " }
};
Configuration.ConfigurePostInitialization(); // Important

Since we will be using the UserAccountManager to upload user analytics, you can proceed to remove the commented lines that create an ApplicationUser:

Ideally, the end result should look like this:

// For uploading analytics specify your endpoint and add any extra form fields or headers required from your service.
Configuration.OnlineURL = "http://localhost:5002/Upload";
Configuration.FormFields = new List<AnalyticsExporter.FormField>
{
    new AnalyticsExporter.FormField { key = "Username", value = UserAccountManager.Get.GetUsername() },
    new AnalyticsExporter.FormField { key = "Operation", value = "Test2021" },
};
Configuration.HeaderKeys = new List<AnalyticsExporter.HeaderKey>
{
    new AnalyticsExporter.HeaderKey { key = "Authorization", value = "Bearer " + UserAccountManager.Get.GetUserToken() }
};
Configuration.ConfigurePostInitialization(); // Important

All we did is specify the API endpoint, set the Operation name, in this case the Product we created earlier, and finally, use the authenticated user JWT to authenticate against the Login service.

Recall that AnalyticsAPI is connected with Login to work properly. Therefore, tokens are delegated to the Login service for authorization.

This is particularly useful for making authorized requests on behalf of the user.

Note

In a full playthrough with an authenticated user, if Analytics are uploaded correctly, you should be able to see them via the Web Portal.


This is everything required to integrate Unity with the Web Services seamlessly.

Next step is to actually build your application and test it!

Happy coding!