4

I set up a very simple ASP.NET MVC 5 application that tries to authenticate a user through an OpenID provider in Authorization Code mode.

I'm able to log in and the server returns a code in the redirect URL querystring and a nonce cookie. However, back on the client application the user is not authenticated (User.Identity.IsAuthenticated false), has no claims and called controller Action that has an Authorize attribute is never carried out. Browser stays on the redirect URL page which is the home page.

I think something happens during the execution of the OpenID Connect middleware which makes it stop halfway through, but can't quite figure out how to debug it.

  • No exceptions are thrown even in "break on all CLR exceptions" mode.

  • When connecting an EventListener to IdentityModelEventSource.Logger at Verbose level I only get one logged event that says "Generating nonce for openIdConnect message", once per authentication attempt.

  • No Notification hooks are reached except RedirectToIdentityProvider, so it looks like no authorization code or security token is received, but the authentication doesn't fail either.

How can I get more info on what happens so that I can debug my problem?

Here's the code:

        public void Configuration(IAppBuilder app)
        {
            var clientSecret = "secret";
            var authenticationOptions = new OpenIdConnectAuthenticationOptions
            {
                ClientId = "id",
                ClientSecret = clientSecret,
                Authority = "https://theauthority",
                RedirectUri = "https://localhost/MyApp/",
            };

            authenticationOptions.ResponseType = OpenIdConnectResponseType.Code; // Authorization code
            authenticationOptions.TokenValidationParameters.IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(clientSecret));
            authenticationOptions.TokenValidationParameters.RequireSignedTokens = true;
            authenticationOptions.TokenValidationParameters.ValidAudience = "katanaclient";
            authenticationOptions.SignInAsAuthenticationType = "Cookies";
            authenticationOptions.Configuration = new OpenIdConnectConfiguration
            {
                Issuer = "https://theissuer",
                AuthorizationEndpoint = "https://theendpoint",
                TokenEndpoint = "https://theendpoint/api/v1/token",
                UserInfoEndpoint = "https://theendpoint/api/v1/userinfo",
                EndSessionEndpoint = "https://theendpoint/api/v1/logout",
                ScopesSupported = { "openid", "profile"},
            };

            authenticationOptions.Notifications = new OpenIdConnectAuthenticationNotifications
            {
                RedirectToIdentityProvider = async n =>
                {
                    // here it goes
                    if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.Authentication)
                    {
                        n.ProtocolMessage.EnableTelemetryParameters = false;
                    }
                },
                AuthorizationCodeReceived = async notification =>
                {
                    // doesn't go through here
                    Debug.WriteLine($"{notification.Response.Body}");

                },
                SecurityTokenReceived = async notification =>
                {
                    // doesn't go through here
                    Debug.WriteLine($"{notification.Response.Body}");
                },
                AuthenticationFailed = async notification =>
                {
                    // doesn't go through here
                    Debug.WriteLine($"{notification.Response.Body}");
                },
                SecurityTokenValidated = async n =>
                {
                    // doesn't go through here
                    Debug.WriteLine($"{n.Response.Body}");
                },
                MessageReceived = async notification =>
                {
                    // doesn't go through here
                    Debug.WriteLine($"{notification.Response.Body}");
                }
            };

            app.UseCookieAuthentication(new CookieAuthenticationOptions()
            );
            app.UseOpenIdConnectAuthentication(authenticationOptions);

            Microsoft.IdentityModel.Logging.IdentityModelEventSource.Logger.LogLevel = EventLevel.Verbose;
            Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = true;

            var listener = new EventListener();
            listener.EnableEvents(Microsoft.IdentityModel.Logging.IdentityModelEventSource.Logger, EventLevel.LogAlways);
            listener.EventWritten += Listener_EventWritten; // Only thing this ever logs is "generating nonce"
        }

[Edit]

I found out that in an ASP.NET Core project with GetClaimsFromUserInfoEndpoint = true it works perfectly. But that property is sadly missing from the older Microsoft.Owin.Security.OpenIdConnect implementation...

guillaume31
  • 13,738
  • 1
  • 32
  • 51
  • Which OpenID provider are you using? – Jeshwel Nov 28 '18 at 14:42
  • You should use the `code` to get the `access token` from the token endpoint. It will contain the claims you are looking for. – RrR- Dec 03 '18 at 06:12
  • You can also use OpenIdConnectResponseType.CodeIdToken to cut out the middle man and get it all back in one go. Assuming your OpenId Connect provider allows that response type combination at least. – Matthew Brubaker Dec 03 '18 at 20:04
  • @RakihthaRR Why would the middleware's Configuration expose a `TokenEndpoint` setting if you have to manually send a request to the endpoint yourself? What does it use it for? – guillaume31 Dec 04 '18 at 08:27
  • @MatthewBrubaker my provider doesn't seem to support Code ID Token: `unsupported_response_type` in the redirect URI querystring – guillaume31 Dec 04 '18 at 08:30
  • @guillaume31 if you want to directly get the `access token`, you can use the implicit grant. It is designed to be used with single-page applications and returns the tokens from authorization endpoint rather than the token endpoint. But authorization code grant is more secure due to the extra step in the flow. – RrR- Dec 04 '18 at 08:48
  • @RakihthaRR thanks, does it correspond to `OpenIdConnectGrantTypes.RefreshToken`? I can only guess it since there's no `OpenIdConnectGrantTypes.Implicit`. Any idea why the `OpenIdConnectGrantTypes` enum in OpenIdConnect 5.3.0 doesn't reflect the grant types defined in [RFC 6749](https://tools.ietf.org/html/rfc6749#section-1.3)? MSDN doc is anemic on the subject. – guillaume31 Dec 04 '18 at 17:01
  • @guillaume31 `refresh_token` grant type is used only when the existing tokens are expired and you want to request another token without asking a user to authorize again. Btw, what's the identity provider you are using? – RrR- Dec 05 '18 at 02:39
  • @RakihthaRR My provider is none of the big public ones. You said *"use the implicit grant"*. How can I use it? I figure by setting the `Configuration.GrantTypesSupported`property of my `OpenIdConnectAuthenticationOptions`, right? – guillaume31 Dec 05 '18 at 07:48
  • You might have to enable implicit grant in your IdP as well. `response_type` in this case would be `token` or `token id_token`. – RrR- Dec 05 '18 at 09:27
  • To be clear, it is not *my* IdP ;) I have no control over it. From what I gather, `response_type` doesn't determine the authorization grant but the authentication flow (see difference [here](https://medium.com/@robert.broeckelmann/when-to-use-which-oauth2-grants-and-oidc-flows-ec6a5c00d864)). Did you mean implicit Grant or implicit Flow? – guillaume31 Dec 05 '18 at 10:46
  • Most OpenId Connect providers have a .well-known/openid-configuration endpoint you can hit as an anonymous user that will tell you what the possible interactions it supports are. Can you provide the results returned by that endpoint? – Matthew Brubaker Dec 05 '18 at 16:50
  • @MatthewBrubaker `.well-known/openid-configuration` is nowhere to be found. – guillaume31 Dec 06 '18 at 11:10
  • @MatthewBrubaker If I want to use an Authorization Code flow, do you agree with @RakihthaRR that I should write my own code to *manually* get an access token from the token endpoint after I received the code? Shouldn't Microsoft's `OpenIdConnectAuthenticationMiddleware` take care of that? Since the API always provides a `TokenEndpoint` property and only has a very general `UseOpenIdConnectAuthentication()` middleware enabler, I would have thought it encompasses all steps of all authentication flows... – guillaume31 Dec 06 '18 at 11:14
  • He is correct, the authorization code is essentially just that. It says "yep, this person is who he says he is and you're allowed to know that". What you would need in order to be able to actually create a "useful" Identity within your application would be either an access token (response type=token), or an identity token (response type=id_token) as those will include claims about the user. – Matthew Brubaker Dec 06 '18 at 17:13

0 Answers0