Friday 10 December 2021

UiPath Authorizing External Applications Using OAuth 2.0 With Asp.net MVC Core & Consuming Data Service Web API

OAuth 2.0 is a security standard where you give one application permission to access your data in another application. The steps to grant permission, or consent, are often referred to as authorization or even delegated authorization. You authorize one application to access your data, or use features in another application on your behalf, without giving them your password.

Registering an external application, meaning an application that is external to your UiPath platform, is a way to share your UiPath resources without having to also share your credentials. Instead, using the OAuth framework, you can delegate your UiPath authorization to external applications.

You can register third-party applications so that they can obtain authorization through the Cloud Portal to gain access to your UiPath resources. Once registered, these applications can make API calls to UiPath applications to access the resources you include in the registration scope.

For more details click here https://docs.uipath.com/automation-cloud/docs/about-external-applications .

In this blog I’ll show in detail how to build an ASP.NET MVC core Web App that integrated with UiPath platform using OAuth and how to consume UiPath Data Service Web API.

I’ve divided the blog into four sections:

·         Registerexternal application in UiPath cloud portal.

·         Creating ASP.NET Core App.

·         Set up ASP.NET Core OAuth 2.0 middleware.

·         Consume Data Service Web API from the application you created in section 1. 


Register external application in UiPath cloud portal

 To register an external application so that it can use OAuth 2.0 to access your UiPath resources follow the following steps:

In Automation Cloud, go to Admin > External Applications and click Add Application :

·         Name: OAuth Demo

·         Redirect URL:  http://localhost:5200/authorization-code/callback

·         Scopes (Figure 2)

o    DataService.Schema.Read

o    DataService.Data.Read

o    DataService.Data.Write 




Add External Application




 After registering the App, you will receive a notification message with App ID & App Secret:

App Secret

xxx

App ID: 

yyyy

 Please store the IDs in a safe place, we will use them to setup OAuth in ASP.NET MVC core app in next step.


CreatingASP.NET Core App

Open visual studio and create a new project:

·         Template: Asp.net Core Web App

·         Language: C#

·         Target Framework: .Net 5.0

·         Authentication Type: None

·         Configure Https: No



 

After creating the project:

·         Add [Authorize] attribute to IndexModel to prevent anonymous access.

·         Change the port (to match the Redirect URL in registering application section) in launchSettings.json >> iisExpress >> applicationUrl >> 5200

·         Add _LoginPartial.cshtml view to display logged in username.

·         Add Section to configuration file appsettings.json to include (AppSecret, AppID, OrgName…) 

In the next step we will configure the OAuth middleware to integrate with UiPath platform.



Set upASP.NET OAuth 2.0 Authentication Middleware

ASP.NET Core comes with OAuth authentication middleware, that makes it easy to use a third-party OAuth 2.0 server for login.

For this tutorial, you will use UiPath OAuth service to protect our app. The ASP.NET OAuth Middleware will be connected to UiPath and use it as an Identity Provider.

First you’ll need to open up Startup.cs and add this line right above app.UseAuthorization() in the Configure method: 


app.UseAuthentication();

 Then create a method ConfigureUiPathAuth as below 

private void ConfigureUiPathAuth(IServiceCollection services)
{

}


Now In order to complete the setup we need some information from OAuth endpoint like (AuthorizationEndpoint,Scope,TokenEndpoint and UserInformationEndpoint)

To get these information you should access URL:  https://cloud.uipath.com/identity_/.well-known/openid-configuration which displays the configuration information based on OAuth protocol:




So we need the following information to complete the setup:

·         AuthorizationEndpoint = https://cloud.uipath.com/identity_/connect/authorize

·         TokenEndpoint = https://cloud.uipath.com/identity_/connect/token

·         serInformationEndpoint = https://cloud.uipath.com/identity_/connect/userinfo

·         Claims = country, name, sub, last_name, first_name, email

·         ClientId = xxx

·         ClientSecret = yyy

·         Scopes = "DataService.Schema.Read DataService.Data.Read DataService.Data.Write" also we must use this scope “openid” otherwise you will not be able to get the access token based on OAuth protocol.


So, after filling all required information the final version of ConfigureUiPathAuth looks like this:  


private void ConfigureUiPathAuth(IServiceCollection services)
        {
            services.AddAuthentication(options =>
            {
                // If an authentication cookie is present, use it to get authentication information
                options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                // If authentication is required, and no cookie is present, use Okta (configured below) to sign in
                options.DefaultChallengeScheme = "UiPath";
            })
           .AddCookie() // cookie authentication middleware first
           .AddOAuth("UiPath", options =>
           {
               // Oauth authentication middleware is second
 
               // When a user needs to sign in, they will be redirected to the authorize endpoint
               options.AuthorizationEndpoint = $"https://cloud.uipath.com/identity_/connect/authorize";
 
               // scopes when redirecting to the authorization endpoint
               options.Scope.Add("DataService.Schema.Read DataService.Data.Read DataService.Data.Write");
               options.Scope.Add("openid");
 
               //After the user signs in, an authorization code will be sent to a callback
               // in this app. The OAuth middleware will intercept it
               options.CallbackPath = new PathString("/authorization-code/callback");
 
               // The OAuth middleware will send the ClientId, ClientSecret, and the
               // authorization code to the token endpoint, and get an access token in return
               options.ClientId = Configuration["AppID"];
               options.ClientSecret = Configuration["AppSecret"];
               options.TokenEndpoint = $"https://cloud.uipath.com/identity_/connect/token";
 
               //Below we call the userinfo endpoint to get information about the user
               options.UserInformationEndpoint = "https://cloud.uipath.com/identity_/connect/userinfo";
 
               //Describe how to map the user info we receive to user claims
               options.ClaimActions.MapJsonKey(ClaimTypes.Country, "country");
               options.ClaimActions.MapJsonKey(ClaimTypes.Name, "name");
               options.ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "sub");
               options.ClaimActions.MapJsonKey(ClaimTypes.Surname, "last_name");
               options.ClaimActions.MapJsonKey(ClaimTypes.GivenName, "first_name");
               options.ClaimActions.MapJsonKey(ClaimTypes.Email, "email");
 
               options.SaveTokens = true;
 
 
               options.Events = new OAuthEvents
               {
                   OnCreatingTicket = async context =>
                   {
                       //Get user info from the userinfo endpoint and use it to populate user claims
                       var request = new HttpRequestMessage(HttpMethod.Get, context.Options.UserInformationEndpoint);
                       request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                       request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", context.AccessToken);
                       var response = await context.Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, context.HttpContext.RequestAborted);
                       response.EnsureSuccessStatusCode();
                       var data = JsonSerializer.Deserialize<JsonElement>(await response.Content.ReadAsStringAsync());
                       context.RunClaimActions(data);
                   }
               };
           });
        }


Don’t forget to call ConfigureUiPathAuth Inside ConfigureServices as below



Before we run the application we should add below code into function Configure to fix a common issue related to Https (Correlation failed. Unknown location …) especially if you are using self-signed certificate or no certificate. 

   public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            // To Fix the error Exception: Correlation failed. Unknown location
            app.UseCookiePolicy(new CookiePolicyOptions()
            {
                MinimumSameSitePolicy = SameSiteMode.Lax
            });

           ……



Now run the application by pressing  (F5)  , you will be redirected to UiPath login page then enter your login info after that browser will redirect you back to the home page (http://localhost:5200) , you should see this screen:



By completing the above steps, you have integrated with UiPath Platform, and you can login using your organization credentials. 

Next, I’ll show you how to consume data service API (Insert & Query).


Consume Data Service Web API from External Application

We need the following to proceed with the demo:



     ·         Create a ChoiceSet called DemoType with tow values (Oauth , API)  

                        


·         Create an entity called OAuthDemo with two fields:

o    Name of type string and make it required

o    Type of type DemoType and make it required



·         Create an entity called OAuthDemo with two fields:

o    Name of type string and make it required

o    Type of type DemoType and make it required

Assign DataReader & DataWriter roles to target user otherwise you will receive Unauthorized error when calling the API 


As per UiPath documentation https://docs.uipath.com/data-service/reference/add-entity-endpoint  

·         The endpoint to create a new record in OAuthDemo entity: https://cloud.uipath.com/{OrgName}/{TenantName}/dataservice_/api/EntityService/ OAuthDemo/insert

·         The endpoint to query all records in OAuthDemo entity: https://cloud.uipath.com/{ OrgName}/{TenantName}/dataservice_/api/EntityService/ OAuthDemo/query


To get the access token for the logged in user use this code:  

await HttpContext.GetTokenAsync("access_token")


In Demo project I have created a page called (http://localhost:5200/createentity) to post a record & display number of records (check code repo) as below image:



Download the entire source code of this article (Github)

Before running your code just double check to update the following configuration file values:



Notes:

1.      From 2021.10 onwards it works the same on-prem as well as in the Cloud