Add Applications to a Website

This topic shows how to populate a website with dynamic content from Identity Platform by calling the REST APIs to retrieve and display application icons for access to a user's web and mobile applications.

Before starting this tutorial, be certain that:

The instructions in this tutorial take you through the process of creating a new website and adding code to populate it with icons to launch a user's web applications. Rather than create the website from scratch yourself, you can simply open the sample application to view the code as you work through the instructions. Also, instead of creating a new website, you can easily adapt the code to add application icons to an existing website.

The sample code contains a number of hard-coded user names and passwords. If you want to build and view the sample website before working through the tutorial, replace these placeholder strings in Default.aspx.cs with actual account names and passwords from your Identity-Service tenant:

Replace hard-coded strings:

  1. In Visual Studio open Default.aspx.cs in the sample project and find the following string definitions:
public static string PodURL = "https://myTenant.my.centrify.com/"; //FQ Tenant URL
 
...
 
public static string AdminUserName = "adminAccount"; //Service Account Username. Must not require MFA. Replace with real admin service account. 
public static string AdminPassword = "xxxx1234";    // Service Account Password. Replace with real password.
 
...
 
string strGetAppsUserName = currentUser; //Replace with the username of an Identity-Service user.
  1. Replace myTenant with your tenant ID.
  2. Replace adminAccount with a real administrator account for your tenant.
  3. Replace xxxx1234 with the password for the administrator account.
  4. Replace currentUser with the name of an Identify-Service user.
  5. Save and close the file.

Create a website

Visual Studio provides a number of different approaches to creating a web site, but the one that this tutorial takes is to create an empty web site and add a web form:

Create a web site:

  1. In Visual Studio, create a new website by clicking FILE > New > Web Site and selecting ASP.NET Empty Web Site.
  2. Visual Studio creates a website node and two configuration files (Web.config and Web.Debug.config).
  3. In Solution Explorer, right-click the website node and click Add > Web Form.
  4. Type a name, or accept the default name (Default) and click OK.

Visual Studio creates two main files for your web site:

  • Default.aspx, which contains HTML front-end code to define the UI for the website. The Add introductory text section section below, steps you through adding additional code to this file.
  • Default.aspx.cs, which contains back-end C# code. The information in the rest of this topic guide you through the process of adding code to this file to interact with Identity Platform.

Visual Studio also creates a configuration file and a debug configuration file, Web-config and Web.Debug.config. You can view and edit all of these files in Solution Explorer in Visual Studio by expanding the website node:

315

The path to these files in the file system is:

C:\Users\username\My Documents\Visual Studio version#\Websites\websiteName  //path
 
C:\Users\admin1\My Documents\Visual Studio 2013\Websites\Website1           //example

Add introductory text section to website

In Default.aspx, which defines the user interface, you're going to create a container (<div>) with these elements:

  • Static text to introduce what the website does.
  • HTML server controls to add dynamic, server-side content, to the page.

For example, open Default.aspx and add an HTML div section similar to this:

<div class="row" runat="server" id="Apps">
    <h2>Centrify Applications</h2>
        <p>
            Here are the applications from Centrify that you can access:
        </p>
</div>

The HTML tags: <h2></h2> and <p></p> define a static bold-face heading and a paragraph of static text. To view this text, build the website in Visual Studio (BUILD > Build Web Site), and open the page in a browser; you should see a result similar to this:

405

The div declaration also contains attributes to define an HTML server control:
runat="server" id="Apps"

The run="server" attribute specifies to run server-side code before creating the web page and sending it to the client. The id="Apps" attribute identifies the specific Page_Load element in Default.aspx.cs to call. The current topic explains how to define this element (which adds application icons) in the "Add REST calls to retrieve applications" section of this topic.

Add error section to website

In Default.aspx, create a section to display an error message when the server code is unable to retrieve the applications for the user. For example, the following HTML code displays the text "Error:" to introduce error messages.

<div class="row" runat="server" id="Error" visible="false">
    <p>
        Error:
    </p>
</div>

The paragraph tags (<p> </p>) define a static string: "Error" to display when the call to retrieve applications returns an error message.

The run="server" attribute specifies to run server-side code before creating the web page and sending it to the client. The id="Error" attribute identifies the specific Page_Load element in Default.aspx.cs to call. "Add back-end code to retrieve error messages" section of the current topic explains how to define this element.

The visible="false" attribute sets the error-section content as invisible to begin with. The back-end code in Default.aspx.cs that implements error retrieval makes the content in this section visible when the REST calls return error messages instead of applications, and makes the application section invisible.

Add server-side code

When you add a web form to a website, Visual Studio creates a file, Default.aspx.cs for server-side code. This file contains a Page class object with a Page_Load function to add dynamic content to a website. You put the code to retrieve and run a user's web applications in the Page_Load function.

Before doing anything else, add using directives for resources that the code calls from other namespaces. Open Default.aspx.cs and you can see that the file contains a default set of using directives for the namespaces that a typical web site uses:

using System;
using System.Collections.Generic;
.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

Add one additional namespace to this list (to support a function that you need to add to deserialize JSON strings): using System.Web.Script.Serialization;

Define members for the Page class

Define the following members for the Page class, as shown in this code snippet:

public partial class _Default : Page
 
{
 
public static string PodURL = "https://myTenant.my.centrify.com/"; //Tenant URL
 
//Endpoints for APIs to call
 
public static string ServiceLoginURL = "security/login";
public static string CentGetAppsURL = "uprest/GetUPData";
public static string CentRunAppURL = "run?appkey=";
 
//Service Account Config
 
public static string AdminUserName = "adminAccount"; //Service Account Username. Must not require MFA. Replace with real account.
public static string AdminPassword = "xxx1234"; // Service Account Password.

You call Identity Platform APIs by specifying a tenant URL and the API endpoint. PodURL defines your tenant URL, and ServiceLoginURL, CentGetAppsURL, and CentRunAppURL define the three API endpoints to call in the sample application. You combine them to create a complete URL to an API endpoint to call; for example:

PodURL + CentGetAppsURL  //Equivalent to: https://myTenant.my.centrify.com/uprest/GetUPData`

Authenticate user and Add REST calls to retrieve applications, in the current topic, show how to use these elements in calls to authenticate a user and retrieve a user's applications.

The tenant URL in Default.aspx.cs (myTenant.my.centrify.com) is a placeholder. Replace it with the tenant ID for your tenant.

All Identity Platform API calls require authentication. AdminUserName and AdminPassword hard code the name and password for an account with authority to run /UPRest/GetUPData. Obviously, in a production website you would not hard code an administrator's name and password, but would retrieve them through a login dialog, or some other dynamic means. The purpose of hard-coding authentication is to enable the sample website to successfully retrieve applications without showing in detail how to code authentication.

If you haven't done so already, change the placeholder values in AdminUserName and AdminPassword to a real administrator and password.

Security/Login is a deprecated call. The sample website calls /Security/Login because it is easier to implement than the supported authentication calls (/Security/StartAuthentication and /Security/AdvanceAuthentication), however, never use /Security/Login in a production environment. Note that Security/Login supports username/password login only. You cannot use it for an account that requires multifactor authentication.

Add Centrify API interface file

Centrify provides an interface file, Centrify_API_Interface.cs, that contains code to support making a call to any REST API end point, such as to /UPRest/GetUPData, which you call later in this topic to retrieve application icons for a user. Rather than call each API end point directly, you can call the MakeRestCall function in Centrify_API_Interface.cs and pass the URL and payload for each REST API function to call.

MakeRest passes the required headers, creates cookies, and handles the response, including HTTP- or API-specific errors, so you don’t have to do so for each API end point that you call.

To make this file available to your website, create a folder named App_Code and copy Centrify_API_Interface.cs from the sample project to the new App_Code folder in your project. In a website project, the App_Code folder has special status — source code files in App_Code are compiled automatically at run time and the resulting assembly is accessible to any other code in your web application.

Add App_Code folder and interface file to website project:

  • In the file system, add a folder called App-Code to your website folder.
  • The default location for the website is C:\Users\username\My Documents\Visual Studio version#\Websites\websiteName
  • Copy Centrify_API_Interface.cs from the sample project to the App_Code folder in your project.
  • In Visual Studio Solution Explorer, right-click the website folder (Website3) and click Refresh > Folder to update the project with the new folder and file.

Add a try-catch statement

Add a try-catch statement with:

  • A try block to authenticate the user and retrieve the application.
  • A catch block to display error messages if application retrieval fails.

Authenticate user

In the try statement, you don't call the Security/Login function directly to authenticate a user. Instead, you call MakeRestCall in Centrify_API_Interfaces.cs, which is defined as follows:

public Centrify_API_Interface MakeRestCall(string CentEndPoint, string JSON_Payload)
    {      
        string PostData = JSON_Payload;
 
        var request = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(CentEndPoint);
 
        request.Method = "POST";
        request.ContentLength = 0;
        request.ContentType = "application/json";
        request.Headers.Add("X-CENTRIFY-NATIVE-CLIENT", "1");
 
        //Create Container for Cookie
        request.CookieContainer = new CookieContainer();
 
        if (Context.Request.Cookies.AllKeys.Contains(".ASPXAUTH"))
        {
            Cookie cCookie = new Cookie();
            cCookie.Name = ".ASPXAUTH";
            Uri uDomain = new Uri(CentEndPoint);
            cCookie.Domain = uDomain.Host;
            cCookie.Value = Context.Request.Cookies[".ASPXAUTH"].Value;
 
            request.CookieContainer.Add(cCookie);
        }
 
        if (!string.IsNullOrEmpty(PostData))
        {
            var encoding = new System.Text.UTF8Encoding();
            var bytes = System.Text.Encoding.GetEncoding("iso-8859-1").GetBytes(PostData);
            request.ContentLength = bytes.Length;
 
            using (var writeStream = request.GetRequestStream())
            {
                writeStream.Write(bytes, 0, bytes.Length);
            }
        }
 
        using (var response = (System.Net.HttpWebResponse)request.GetResponse())
        {
            var responseValue = string.Empty;
 
 
            if (response.StatusCode != System.Net.HttpStatusCode.OK)
            {
                var message = String.Format("Request failed. Received HTTP {0}", response.StatusCode);
                throw new ApplicationException(message);
            }
 
            // grab the response
            using (var responseStream = response.GetResponseStream())
            {
                if (responseStream != null)
                    using (var reader = new System.IO.StreamReader(responseStream))
                    {
                        responseValue = reader.ReadToEnd();
                    }
            }
 
            returnedCookie = null;
 
            if (response.Cookies[".ASPXAUTH"] != null)
            {
                if (response.Cookies[".ASPXAUTH"].Value != "")
                {
                    HttpCookie cASPXAuth = new HttpCookie(".ASPXAUTH");
                    cASPXAuth.Value = response.Cookies[".ASPXAUTH"].Value;
                    //cASPXAuth.Domain = "kibble.centrify.com";
                    cASPXAuth.Expires = response.Cookies[".ASPXAUTH"].Expires;
 
                    returnedCookie = cASPXAuth;
                }
            }
                       
            returnedResponse = responseValue;
 
            return this;
        }
    }

MakeRestCall takes two parameters:

  • CentEndPoint: Specifies the URL to the REST API endpoint — /tenantId.my.centrify.com/security/login, in this case.
  • JSON_Payload: Specifies the JSON field/value pairs to send to the API endpoint — user/password in this case.

To authenticate a user, call MakeRestCall as follows:

//Admin Service Account Login API Call
 
string strAdminLoginPostData = "{user:'" + AdminUserName + "', password:'" + AdminPassword + "'}";
Centrify_API_Interface centLogin = new Centrify_API_Interface().MakeRestCall(PodURL + ServiceLoginURL, strAdminLoginPostData);
HttpContext.Current.Response.Cookies.Add(centLogin.returnedCookie);
Session["ASPXAUTH"] = centLogin.returnedCookie.Value;

As you can see:

  • PodURL + ServiceLoginURL combine to form the URL: https://myTenant.my.centrify.com/Security/Login for the CentEndPoint parameter.
  • strAdminLoginPostData creates a JSON payload of: "user:adminAccount", "password:xxxx1234".

If you haven't done so already, be certain that you have replaced the value of PodURL with your tenant URL and supplied a valid administrator account and password for AdminUserName and AdminPassword.

Add REST calls to retrieve applications

In the try statement, you don't call the /UPRest/GetUPData function directly to retrieve applications, but rather call MakeRestCall in Centrify_API_Interfaces.cs and pass the URL to UPRest/GetUPData, and a JSON payload with the name of a user whose applications are to be retrieved.

//Centrify_API_Interfaces.cs
 
...
 
public Centrify_API_Interface MakeRestCall(string CentEndPoint, string JSON_Payload)
 
_________________
 
//Default.aspx.cs file
 
string strGetAppsUserName = currentUser; //Replace with the username of an Identity-Service user.
 
...
 
//Get Up Data API Call           
 
string strGetAppsPostData = "{username:'" + strGetAppsUserName + "'}";
Centrify_API_Interface centApps = new Centrify_API_Interface().MakeRestCall(PodURL + CentGetAppsURL, strGetAppsPostData);

MakeRestCall, which is defined in Centrify_API_Interfaces.cs, takes two parameters:

  • CentEndPoint specifies the URL for the REST API endpoint. For this parameter, you pass "PodURL + CentGetAppsURL", which combine to form this URL: https://myTenant.my.centrify.com/UPRest/GetUPData.
  • JSON_Payload specifies the JSON field/value pairs to send to the API endpoint, UPRest/GetUPData. which requires a single parameter, username, with the name of the logged in user. You pass strGetAppsPostData, which contains a string (username), and a variable (strGetAppsUserName) that holds the name of the current user, to define this parameter as: "username":"currentUser".

The sample code specifies a placeholder, currentUser for the user's name. Be certain to change this to the name of a Cloud-service account. Obviously, in a production website, you wouldn't hard-code a user name but would retrieve the name dynamically through a dialog box, or by some other mechanism. For example, you could do something like the following:

Identity a user through session data

  • Store session data when you authenticate a user.
  • Call /UserMgmt/GetUserAttributes and pass the saved session data to identify the user.
  • Retrieve the user's name from the response data send it to the call to retrieve the user's applications.

MakeRestCall returns JSON-formatted data in centApps.returnedResponse for all the applications for the specified user. The next section shows how to parse and display these results.

Parse and display function results

The first step in handling the results from MakeRestCall (to retrieve the response from UPRest/GetUPData) is to deserialize the JSON response, by using the JavaScriptSerializer class in System.Web.Script.Serialization:

//Parsing Apps From Get Up Data
 
string strApps = centApps.returnedResponse;
var jss = new JavaScriptSerializer();
Dictionary<string, dynamic=""> sData = jss.Deserialize<dictionary<string, dynamic="">(strApps);
var dApps = sData["Result"]["Apps"];
</dictionary<string,></string,>

Next, do the following:

  • Initiate a counter.
  • Retrieve three items from the returned data for each application: DisplayName, AppKey, and Icon.
  • Pass these three items, as well as iCount to AddUrls.
//App Counter
     int iCount = 0;
 
//Adding Each App to Site
 foreach (var app in dApps)
 {
 
string strDisplayName = app["DisplayName"];
string strAppKey = app["AppKey"];
string strIcon = app["Icon"];
 
AddUrls(strAppKey, strDisplayName, strIcon, iCount);
 
iCount++;
 }

AddUrls is defined in the sample Default.aspx.cs file:

AddUrls definition

protected void AddUrls(string strAppKey, string strName, string strIcon, int count)
    {
        HyperLink link = new HyperLink();
        link.ID = "CentrifyApp" + count;
        link.NavigateUrl = PodURL + CentRunAppURL + strAppKey;
        link.Text = strName;
 
        //If image is unsecured use direct URL for icon
        if (strIcon.Contains("vfslow"))
        {
            link.ImageUrl = PodURL + strIcon;
        }
        else//If image needs a cookie or header to access use secure image helper
        {
            link.ImageUrl = "Helpers/GetSecureImage.aspx?Icon=" + strIcon;
        }
 
        link.ImageHeight = 75;
        link.ImageWidth = 75;
 
        //Organizing icons in a grid
        if (count % 7 == 0)
        {
            Apps.Controls.Add(new LiteralControl("<br>"));
        }
        else
        {
            Apps.Controls.Add(new LiteralControl("         "));
 
        }
 
        Apps.Controls.Add(link);
    }

For each application, AddUrls does the following:

  • Creates a hyperlink to launch the application. link.NavigateUrl combines PodURL + CentRunAppURL with strAppKey to pass "run?appkey=keyName" through to the browser to launch the application identified by the key name.
  • Displays the application icon in a seven-item wide grid. link.imageUrl

Add back-end code to retrieve error messages

The code in the catch statement handles exceptions from the code in the try statement. Recall that in Default.aspx, which defines the UI for the website, there are two sections:

  • A section with ID=Apps to display the applications that the call to UPRest/GetUPData returns.
  • A section with ID=Error to display error message text if any of the REST calls fail.

The code in the catch statement is called only if the code in the try statement throws an exception.

catch (Exception ex)
       {
           Apps.Visible = false;
           Error.InnerText = ex.ToString();
           Error.Visible = true;          
       }

When the code is called, it does the following:

  • Apps.Visible = false; hides the Apps section of the website that contains the introductory text ("Here are the applications...").
  • Error.Visible = true; makes the Error section of the website that contains the error text visible.
  • Error.InnerText = ex.ToString(); retrieves the error message to display in the Error section of the website.