'RFC - Adding Group Claims from OKTA to Role Claims in .net Framework Using OIDC

PROBLEM: The suggested method by Lee Brandt see: https://developer.okta.com/blog/2018/04/18/authorization-in-your-aspnet-mvc-4-application was not working due to notifications not being invoked. After a number of days researching and attempting various options, I came up with following solution.

UPDATE: it was a spelling error that it was not being called. duh.

But the solution I have seems to be easier.



Solution 1:[1]

BEHAVIOUR

It may be OK, though the standard behavior is all built around creating or restoring a useful ClaimsPrincipal. The Microsoft code does this:

  • When there is no auth cookie, do the OIDC redirect to sign the user in, to eventually get tokens

  • Create a ClaimsPrincipal from the ID token, then serialize tokens to an encrypted HTTP only cookie

  • On all subsequent requests, the stack just deserializes the ClaimsPrincipal from the ID token in the cookie

  • Occasionally a token refresh may occur, which also rewrites the cookie, to store new tokens

CLAIMS PRINCIPAL CUSTOMIZATION

AddOpenIDConnect has an options object with a property called TokenValidationParameters. This has a RoleClaimType which could be set to Groups in your case.

It is recommended to customize claims programmatically when tokens are first received, so your custom code is fine - though it should only run when there is no auth cookie yet.

There has always been an action such as OnTokenValidated for customizing the ClaimsPrincipal. I first used this in around 2014 with the .Net framework and it has not really changed since. It occurs after the ID token is validated and before the ClaimsPrincipal is finalized. Ideally put your code here.

Solution 2:[2]

SOLUTION

Setup OKTA using procedure in Lee's article.

In Startup.cs, instead of using 'UseOpenIdConnectAuthentication' use:


app.UseCookieAuthentication(...);
           
app.UseOktaMvc(...**

app.Use(async (context, next) =>
{
  if (context.Authentication.User.Identity.IsAuthenticated)
  {
    // Get claims from access_token
    var accessToken = 
      ((ClaimsIdentity)context.Authentication.User.Identity)
      .Claims
      .Where(x => x.Type == "access_token")
      .FirstOrDefault().Value;
    var handler = new JwtSecurityTokenHandler();
    var token = handler.ReadJwtToken(accessToken);

    // Add roles from claims
    var claims = new List<Claim>();
    foreach (var group in token.Claims
      .Where(x => x.Type == "allgroups")) 
    {
      claims.Add(new Claim(ClaimTypes.Role, group.Value));
    }

    if (claims.Count > 0)
    {
      var identity = new ClaimsIdentity();
      identity.AddClaims(claims);

      var identities = new List<ClaimsIdentity> { identity };
                    
      context.Authentication.User.AddIdentities(identities);
    }
  }

  await next.Invoke();
});

The OKTA Groups claims are added as Role claims allowing the controller authorize attributes to be utilized.

However, I notice when I reload the page the claims are not retained but must be re-added. Is this correct?

Any suggestions or problems?

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Gary Archer
Solution 2