cancel
Showing results for 
Search instead for 
Did you mean: 
Reply
IsankaPT
Regular Visitor

How to generate JWT token in Power App Portals

I'm trying to implement SSO (Single sign-on) in Power App Portals.

I have another e-commerce web application that includes customers from the master Power App. When the user logged into the Power app, I'm going to display a link to redirect to the separate e-commerce web application and need to skip the login part, and the customer should be signed in.

SO, I'm planning to display a link button that includes the JWT token. Then I can deserialize the redirect URL and trigger the customer sign-in part in the e-commerce application.

This is the first time I'm trying power app development and couldn't find a proper reference to generate JWT token in Power App Portal.

It's a big help if anyone can instruct me with some steps or provide some resource URLs.

1 ACCEPTED SOLUTION

Accepted Solutions
IsankaPT
Regular Visitor

I have referred @chleverenz link and I have solved it like below, 

 
Used HTML button to trigger goto GoToCommerceApp()
 

This is the javascript code to generate the JWT token.

 

<script type="text/javascript">    
// Define Callback function and call window.auth.getAuthenticationToken to fetch the token.
   function callback (data, err) { 
      if(data) {
        //Token received
        console.log('Token received',data);
        window.open("http://localhost:15536/api/ExternalWebApi/ValidateToken?userToken="+data);
      }
      else {
        console.log("Error");
        if(err){
           // 401 is returned for anonymous users.
            if(err.status == 401){
                console.log("Login required");
                redirectToLogin();
            }

           // To handle any other error
            else{
                 console.log(err.responseJSON.ErrorId + " : " + err.responseJSON.ErrorMessage);
              }
            }
        }
    }

    function getAuthenticationToken() {
       	let clientId = "56756948ec-22041987612021"; //Add the Client ID registered on CRM.     
        $.ajax({
            type: 'GET',
            url: `/_services/auth/token?client_id=${clientId}`,
            cache: false,
            success: handleGetAuthenticationTokenSuccess,
            error: jqXHR => callback(null, jqXHR)
        });
    }

    function handleGetAuthenticationTokenSuccess(data, status, jqXHR) { 

        var jsonResult = JSON.parse(jqXHR.getResponseHeader('X-Responded-JSON'));
        if (jsonResult && jsonResult.status == 401) {           
                // If the user is not logged in, redirect to login page
                //redirectToLogin();          
                callback(null,jsonResult); //Run callback method with error message          
        } else {
            // Pass the token to the callback function
            callback(data,null);
        }
    }

    function redirectToLogin() {
        var redirectUrl = window.location;
        var loginUrl = window.location.origin + '/SignIn?returnUrl=' + encodeURIComponent(redirectUrl);
        window.location = loginUrl;
    }
    
   function GoToCommerceApp(){// Fetch Token Call    
      getAuthenticationToken();
   }  
</script>

 

 

asp .net class to decode JWT token

 

 

	public class ExternalWebApiController : ApiController
	{
		[AllowAnonymous]
		[HttpGet]
		[HttpsRequirement(SslRequirement.Yes)]
		[Route("api/ExternalWebApi/PowerAppPortalLogin")]
		public virtual IActionResult PowerAppPortalLogin(string userToken)
		{
			try
			{
				var email = DynamicsPortalBearerAuthenticationProvider.DecodePowerAppPortalToken(userToken);
				return Json(email);
			}
			catch (Exception ex)
			{
				return Json(ex.Message);
			}
		}


		public class DynamicsPortalBearerAuthenticationProvider
		{
			public static string DecodePowerAppPortalToken(string userToken)
			{
				try
				{
					// Retrieve the JWT token 
					var jwt = userToken;
					var handler = new JwtSecurityTokenHandler();
					var token = new JwtSecurityToken(jwt);
					var claimIdentity = new ClaimsIdentity(token.Claims, DefaultAuthenticationTypes.ExternalBearer);
					var _signingKey = DynamicsPortalBearerAuthenticationProvider.GetSigningKey();
					var param = new TokenValidationParameters
					{
						ValidateAudience = true, // Make this false if token was generated without clientId
						ValidAudience = "6578954f948ec-544556920212021", //Replace with Client Id Registered on CRM. Token should have been fetched with the same clientId.
						ValidateIssuer = true,
						IssuerSigningKey = _signingKey,
						IssuerValidator = (issuer, securityToken, parameters) =>
						{
							var allowed = DynamicsPortalBearerAuthenticationProvider.GetAllowedPortal().Trim().ToLowerInvariant();
							if (issuer.ToLowerInvariant().Equals(allowed))
							{
								return issuer;
							}

							throw new Exception("Token Issuer is not a known Portal");
						}
					};

					SecurityToken validatedToken = null;
					handler.ValidateToken(token.RawData, param, out validatedToken);


					string email = ((JwtSecurityToken)validatedToken).Payload["preferred_username"].ToString();
					return email;
				}
				catch (Exception exception)
				{
					System.Diagnostics.Debug.WriteLine(exception);
					return "";
				}
			}

			public static string GetAllowedPortal()
			{
				return "abc.powerappsportals.com";// ConfigurationManager.AppSettings["Microsoft.Dynamics.AllowedPortal"];
			}
			
			private static string GetAllowedPortalSigningKey()
			{
				return "";// ConfigurationManager.AppSettings["Microsoft.Dynamics.AllowedPortalSigningKey"];
			}

			public static RsaSecurityKey GetSigningKey()
			{
				var val = GetAllowedPortalSigningKey();
				if (string.IsNullOrEmpty(val))
				{
					var portalUrl = GetAllowedPortal();
					WebClient client = new WebClient();
					var url = "https://" + portalUrl + "/_services/auth/publickey";
					val = client.DownloadString(url);
				}

				var x = new PemReader(new StringReader(val));
				var y = (RsaKeyParameters)x.ReadObject();
				var rsaInfo = new RSAParameters
				{
					Modulus = y.Modulus.ToByteArrayUnsigned(),
					Exponent = y.Exponent.ToByteArrayUnsigned()
				};

				var rsa = new RSACryptoServiceProvider();
				rsa.ImportParameters(rsaInfo);
				return new RsaSecurityKey(rsa);
			}
		}
	}

 

 

 

 

 

View solution in original post

2 REPLIES 2
chleverenz
Skilled Sharer
Skilled Sharer

Hi @IsankaPT ,

i think what you request is something like https://docs.microsoft.com/en-us/powerapps/maker/portals/oauth-implicit-grant-flow . This to my understanding somehow enables the portal to act as an oauth provide one the user is logged in (or requires a login if not). 

Well, i am not to judge, but may be let me ask the following: when i got it right i think, this is not what you really want (doesn't lucifer always ak this? 🙂 ). When i talk to customers about single sign on, i usually recommend seperating the authenticatioon from the other stuff. So, for example if you use azure b2c as an authentication provider, the user effectively logs in into azure b2c and is known there. Whenever any application needs a signin it sends the user to the azure b2c and if the user is already logged it the application directly receives a token.

So, if you set up the portals using azure b2c and switching off local authentication, most of the single singn-on should be handled automatically.
Just my thought about it, hopefully it helps you finding a good solution for you.

 

Have fun and happy portalling,

  Christian

IsankaPT
Regular Visitor

I have referred @chleverenz link and I have solved it like below, 

 
Used HTML button to trigger goto GoToCommerceApp()
 

This is the javascript code to generate the JWT token.

 

<script type="text/javascript">    
// Define Callback function and call window.auth.getAuthenticationToken to fetch the token.
   function callback (data, err) { 
      if(data) {
        //Token received
        console.log('Token received',data);
        window.open("http://localhost:15536/api/ExternalWebApi/ValidateToken?userToken="+data);
      }
      else {
        console.log("Error");
        if(err){
           // 401 is returned for anonymous users.
            if(err.status == 401){
                console.log("Login required");
                redirectToLogin();
            }

           // To handle any other error
            else{
                 console.log(err.responseJSON.ErrorId + " : " + err.responseJSON.ErrorMessage);
              }
            }
        }
    }

    function getAuthenticationToken() {
       	let clientId = "56756948ec-22041987612021"; //Add the Client ID registered on CRM.     
        $.ajax({
            type: 'GET',
            url: `/_services/auth/token?client_id=${clientId}`,
            cache: false,
            success: handleGetAuthenticationTokenSuccess,
            error: jqXHR => callback(null, jqXHR)
        });
    }

    function handleGetAuthenticationTokenSuccess(data, status, jqXHR) { 

        var jsonResult = JSON.parse(jqXHR.getResponseHeader('X-Responded-JSON'));
        if (jsonResult && jsonResult.status == 401) {           
                // If the user is not logged in, redirect to login page
                //redirectToLogin();          
                callback(null,jsonResult); //Run callback method with error message          
        } else {
            // Pass the token to the callback function
            callback(data,null);
        }
    }

    function redirectToLogin() {
        var redirectUrl = window.location;
        var loginUrl = window.location.origin + '/SignIn?returnUrl=' + encodeURIComponent(redirectUrl);
        window.location = loginUrl;
    }
    
   function GoToCommerceApp(){// Fetch Token Call    
      getAuthenticationToken();
   }  
</script>

 

 

asp .net class to decode JWT token

 

 

	public class ExternalWebApiController : ApiController
	{
		[AllowAnonymous]
		[HttpGet]
		[HttpsRequirement(SslRequirement.Yes)]
		[Route("api/ExternalWebApi/PowerAppPortalLogin")]
		public virtual IActionResult PowerAppPortalLogin(string userToken)
		{
			try
			{
				var email = DynamicsPortalBearerAuthenticationProvider.DecodePowerAppPortalToken(userToken);
				return Json(email);
			}
			catch (Exception ex)
			{
				return Json(ex.Message);
			}
		}


		public class DynamicsPortalBearerAuthenticationProvider
		{
			public static string DecodePowerAppPortalToken(string userToken)
			{
				try
				{
					// Retrieve the JWT token 
					var jwt = userToken;
					var handler = new JwtSecurityTokenHandler();
					var token = new JwtSecurityToken(jwt);
					var claimIdentity = new ClaimsIdentity(token.Claims, DefaultAuthenticationTypes.ExternalBearer);
					var _signingKey = DynamicsPortalBearerAuthenticationProvider.GetSigningKey();
					var param = new TokenValidationParameters
					{
						ValidateAudience = true, // Make this false if token was generated without clientId
						ValidAudience = "6578954f948ec-544556920212021", //Replace with Client Id Registered on CRM. Token should have been fetched with the same clientId.
						ValidateIssuer = true,
						IssuerSigningKey = _signingKey,
						IssuerValidator = (issuer, securityToken, parameters) =>
						{
							var allowed = DynamicsPortalBearerAuthenticationProvider.GetAllowedPortal().Trim().ToLowerInvariant();
							if (issuer.ToLowerInvariant().Equals(allowed))
							{
								return issuer;
							}

							throw new Exception("Token Issuer is not a known Portal");
						}
					};

					SecurityToken validatedToken = null;
					handler.ValidateToken(token.RawData, param, out validatedToken);


					string email = ((JwtSecurityToken)validatedToken).Payload["preferred_username"].ToString();
					return email;
				}
				catch (Exception exception)
				{
					System.Diagnostics.Debug.WriteLine(exception);
					return "";
				}
			}

			public static string GetAllowedPortal()
			{
				return "abc.powerappsportals.com";// ConfigurationManager.AppSettings["Microsoft.Dynamics.AllowedPortal"];
			}
			
			private static string GetAllowedPortalSigningKey()
			{
				return "";// ConfigurationManager.AppSettings["Microsoft.Dynamics.AllowedPortalSigningKey"];
			}

			public static RsaSecurityKey GetSigningKey()
			{
				var val = GetAllowedPortalSigningKey();
				if (string.IsNullOrEmpty(val))
				{
					var portalUrl = GetAllowedPortal();
					WebClient client = new WebClient();
					var url = "https://" + portalUrl + "/_services/auth/publickey";
					val = client.DownloadString(url);
				}

				var x = new PemReader(new StringReader(val));
				var y = (RsaKeyParameters)x.ReadObject();
				var rsaInfo = new RSAParameters
				{
					Modulus = y.Modulus.ToByteArrayUnsigned(),
					Exponent = y.Exponent.ToByteArrayUnsigned()
				};

				var rsa = new RSACryptoServiceProvider();
				rsa.ImportParameters(rsaInfo);
				return new RsaSecurityKey(rsa);
			}
		}
	}

 

 

 

 

 

Helpful resources

Announcements
2022 Release Wave 1 760x460.png

2022 Release Wave 1 Plan

Power Platform release plan for the 2022 release wave 1 describes all new features releasing from April 2022 through September 2022.

Community Connections 768x460.jpg

Community & How To Videos

Check out the new Power Platform Community Connections gallery!

Users online (1,396)