Authentication ticket value is null in the AuthorizationCodeReceived event

Ed Brinkman 121 Reputation points
2021-02-27T11:30:23.69+00:00

I have been able to login to the identity provider, and get the access_token. My problem is with mapping the OpenID connect groups to roles. I am changing an MVC 4 website. The article post at ( https://developer.okta.com/blog/2018/04/18/authorization-in-your-aspnet-mvc-4-application ) gives a sample code for AuthorizationCodeReceived. The problem is that the Authentication ticket value is null in the AuthorizationCodeReceived event. . The article states that mapping the OpenIDConnect groups to roles is required to get authorization attributes to work. My website is not using Azure Active Directory. Do you have any advice?

ASP.NET
ASP.NET
A set of technologies in the .NET Framework for building web applications and XML web services.
3,288 questions
Microsoft Entra ID
Microsoft Entra ID
A Microsoft Entra identity service that provides identity management and access control capabilities. Replaces Azure Active Directory.
19,664 questions
0 comments No comments
{count} votes

Accepted answer
  1. Ed Brinkman 121 Reputation points
    2021-03-11T17:39:21.62+00:00

    I found a post that fixed the problem. The authentication works now. I wanted to post it for any future reference. https://stackoverflow.com/questions/20737578/asp-net-sessionid-owin-cookies-do-not-send-to-browser


12 additional answers

Sort by: Most helpful
  1. Ed Brinkman 121 Reputation points
    2021-03-08T18:39:03.88+00:00

    Please stop referencing the same article. I need an answer to my original question. My code does not work. My employer is making me use the authorization_code response not the response id_token. I do not know if that is a big factor. I am creating an authentication ticket and assigning it to n.AuthenticationTicket in the AuthorizationCodeReceived event. Below is my code. It is not working. That is why I opened this question. I have seen a lot on AAD. I found an article at https://leastprivilege.com/2016/08/21/why-does-my-authorize-attribute-not-work/ . I suspect there is a setup problem because no authentication ticket exists coming in to the event. The authentication ticket constructor takes in n.AuthenticationTicket.Properties which is null also so I had to hard code something. I am sure it is not correct. I have also seen code samples at https://brockallen.com/2013/10/24/a-primer-on-owin-cookie-authentication-middleware-for-the-asp-net-developer/ . Is there an authoritative source? The link you provided does not cover creating AuthenticationTickets. The example uses id_token response type which is not available to me. I can only use a response type of authorization_code.

    var id = new ClaimsIdentity();
    id.AddClaims(decodedAccessTokenValue.Claims);
    id.AddClaim(new Claim(System.Security.Claims.ClaimTypes.Name, userEmail));
    id.AddClaim(new Claim(ClaimTypes.Role, "AmerenAdmin"));
    id.AddClaim(new Claim("access_token", accessToken.ToString()));
    id.AddClaim(new Claim("expires_at", DateTime.Now.AddSeconds(double.Parse(expiresIn.ToString())).ToLocalTime().ToString(CultureInfo.InvariantCulture)));
    id.AddClaim(new Claim("id_token", idToken.ToString()));

                        n.AuthenticationTicket = new AuthenticationTicket(
                            new ClaimsIdentity(id.Claims, AuthenticationTypes.Password, "name", "role"),
                            new AuthenticationProperties { IsPersistent = true });
    

  2. Ed Brinkman 121 Reputation points
    2021-03-09T14:44:58.003+00:00

    The code in the MessageReceived event causes the AuthorizationCodeReceived event not to fire. However, in the event SecurityTokenValidated the variable notification.AuthenticationTicket.Identity.Name is correct and the variable notification.AuthenticationTicket.Identity.IsAuthenticated returns true. I add the claims that include the role because the claims do not include the role claim value. NO ERRORS ARE THROWN. It looks like the authentication works in the startup class file but does not flow to the controllers. I do not know why. HttpContext.User is unavailable inside the Startup.cs file. The callback method in the AccountController has a empty string for the HttpContext.User.Identity.Name. IsAuthenticated is false also. The claims are not correct in the HttpContext.User.Identity either. So the entire authentication flow has not completed but NO ERRORS ARE THROWN. Why? The redirect also occurs which appears to show that the authentication completed successfully. I did add code for app.UseErrorPage. NO ERRORS ARE THROWN.

    The article link is for asp.net core not asp.net MVC. The ticket received event is not available for MVC sites. I see only SecurityTokenReceived.

    Is there a way to diagnose the problem either on the client or server side? I need more information. I have included my code in the next post. These articles do not help with diagnosing the problem.

    0 comments No comments

  3. Ed Brinkman 121 Reputation points
    2021-03-09T15:36:18.463+00:00

    Below is the code from my startup.cs file. Do you have any advice?

    public class Startup
        {
            // The Client ID is used by the application to uniquely identify itself to Azure AD.
            string clientId = System.Configuration.ConfigurationManager.AppSettings["ClientId"];
            string clientSecret = System.Configuration.ConfigurationManager.AppSettings["ClientSecret"];
    
            // RedirectUri is the URL where the user will be redirected to after they sign in.
            string redirectUri = System.Configuration.ConfigurationManager.AppSettings["RedirectUri"];
    
            // Authority is the URL for authority, composed by Microsoft identity platform endpoint and the tenant name (e.g. https://login.microsoftonline.com/contoso.onmicrosoft.com/v2.0)
            string authority = System.Configuration.ConfigurationManager.AppSettings["Authority"];
    
    
            public void Configuration(IAppBuilder app)
            {
                IdentityModelEventSource.ShowPII = true;
                app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
                List<Claim> claims = new List<Claim>();
    
                // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=316888
    
                app.UseCookieAuthentication(new CookieAuthenticationOptions());
    
                app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
                {
                    ClientId = clientId,
                    ClientSecret = clientSecret,
                    Authority = authority,
                    RedirectUri = redirectUri,
                    ResponseType = OpenIdConnectResponseType.Code,
                    Scope = "openid profile ismemberof",
                    //UseTokenLifetime = false,
                    SignInAsAuthenticationType = "Cookies",
                    TokenValidationParameters = new TokenValidationParameters
                    {
                        NameClaimType = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier"
                    },
    
                    // OpenIdConnectAuthenticationNotifications configures OWIN to send notification of failed authentications to OnAuthenticationFailed method
                    Notifications = new OpenIdConnectAuthenticationNotifications
                    {
                        RedirectToIdentityProvider = n =>
                        {
                            if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.Authentication)
                            {
                                n.ProtocolMessage.AcrValues = "eapotplogin";
                            }
    
                            return Task.FromResult(0);
                        },
                        AuthorizationCodeReceived = n =>
                        {
                            return Task.CompletedTask;
                        },
                        AuthenticationFailed = OnAuthenticationFailed,
                        SecurityTokenValidated = async notification =>
                        {
                            var identity = notification.AuthenticationTicket.Identity;
                            //return Task.CompletedTask;
                            //return Task.FromResult(0);
                        },
                        MessageReceived = async context =>
                        {
                            var msg = context.ProtocolMessage.Code;
    
                            //swap authorization code for an access token
                            var token = GetToken(msg);
                            JObject jsonObj = JObject.Parse(token);
                            JToken accessToken = "";
                            jsonObj.TryGetValue("access_token", out accessToken);
                            JToken idToken = "";
                            jsonObj.TryGetValue("id_token", out idToken);
                            var handler = new JwtSecurityTokenHandler();
                            var decodedAccessTokenValue = handler.ReadJwtToken(accessToken.ToString());
                            var decodedIdValue = handler.ReadJwtToken(idToken.ToString());
    
                            context.ProtocolMessage.Code = null;
                            context.ProtocolMessage.IdToken = idToken.ToString();
                            context.ProtocolMessage.AccessToken = accessToken.ToString();
    
                        }
                    }
                });
            }
    
            /// <summary>
            /// Handle failed authentication requests by redirecting the user to the home page with an error in the query string
            /// </summary>
            /// <param name="context"></param>
            /// <returns></returns>
            private Task OnAuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> context)
            {
                context.HandleResponse();
                context.Response.Redirect("/?errormessage=" + context.Exception.Message);
                return Task.FromResult(0);
            }
    
            public String GetToken(String code)
            {
                // The Client ID is used by the application to uniquely identify itself to Azure AD.
    
                string clientId = System.Configuration.ConfigurationManager.AppSettings["ClientId"];
                string clientSecret = System.Configuration.ConfigurationManager.AppSettings["ClientSecret"];
    
                // RedirectUri is the URL where the user will be redirected to after they sign in.
                string redirectUri = System.Configuration.ConfigurationManager.AppSettings["RedirectUri"];
                string authority = System.Configuration.ConfigurationManager.AppSettings["Authority"];
                string tokenEndpoint = authority + "/access_token";
    
                var values = new Dictionary<string, string>
                {
                    { "grant_type", "authorization_code" },
                    { "code" , code },
                    { "client_id", clientId},
                    { "client_secret", clientSecret },
                    { "redirect_uri", redirectUri}
                };
    
                HttpClient tokenClient = new HttpClient();
    
                var content = new FormUrlEncodedContent(values);
    
                var response = tokenClient.PostAsync(tokenEndpoint, content).Result;
    
                if (response.IsSuccessStatusCode)
                {
                    var responseContent = response.Content;
    
                    return responseContent.ReadAsStringAsync().Result;
                }
    
                throw new Exception("Token request failed with status code: " + response.StatusCode);
            }
        }
    
    0 comments No comments

  4. Ed Brinkman 121 Reputation points
    2021-03-11T03:58:35.533+00:00

    I did learn today that the code works in a small demo website. The large existing website does not work. The cookie .AspNet.Cookies is not created in the browser. I do not know why.