Microsoft OIDC library OnAuthorizationCodeReceived getting fired twice

srijith 6 Reputation points
2021-11-04T14:27:53.097+00:00

I have an OIDC application, which was giving me correlation error for some time. We have managed to resolve it by passing correlation cookie properly. Our infrastructure has the following network topology.

Openshift Container(OIDC App) -> Reverser Proxy/Api gateway -> Identity Provider.

If we use boiler plate code, the callback path picks up the host of openshift, hence the redirection not working correctly. We are now building the url on the event onRedirectToIdentitityProvider.

We are also using OnAuthorizationCodeReceived event to make the /Token call as the default configuration was not working out.

We get callback properly with Authorization code but we see onAuthorizationCodeRecieved getting fired twice. This is happening only in the test environment with the reverse proxy set up. In development box, there is no issue but it does not have the reverse proxy set up(Now, reverse proxy is just a passthrough so not sure how that could break things).

Code Snippet Below

 public void ConfigureServices(IServiceCollection services)
        {
            string _authorityAPI = Configuration.GetValue<string>("authority");
            string _org = Configuration.GetValue<string>("orgDomain");
            string clientId = Configuration.GetValue<string>("ClientId");
            string clientSecret = Configuration.GetValue<string>("ClientSecret");


            services.Configure<CookiePolicyOptions>(options =>
            {
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
                options.Secure = CookieSecurePolicy.Always;
                options.HandleSameSiteCookieCompatibility();



            services.AddAuthentication(auth =>
            {
                auth.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                auth.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
                auth.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            })
            .AddCookie(options =>
            {
                options.LoginPath = "/Profile/Index/";
                options.LogoutPath = "/Profile/Logout/";
            })

            .AddOpenIdConnect(options =>
            {

                options.ClientId = clientId;
                options.ClientSecret = clientSecret;
                options.Authority = Configuration.GetValue<string>("Authority");
                options.CallbackPath = "/web/auth/callback";
                options.ResponseType = OpenIdConnectResponseType.Code;
                options.MetadataAddress = string.Format("{0}/.well-known/openid-configuration", _authorityAPI);
                options.TokenValidationParameters.ValidateIssuer = false;
                //options.GetClaimsFromUserInfoEndpoint = true;
                options.RequireHttpsMetadata = true;
                options.SaveTokens = true;
                options.Scope.Add("openid");
                options.Scope.Add("email");
                options.Scope.Add("profile");
                options.NonceCookie.SameSite = SameSiteMode.None;
                options.CorrelationCookie.SameSite = SameSiteMode.None;
                options.NonceCookie.Path = "/";
                options.CorrelationCookie.Path = "/";

                options.ConfigurationManager = new ConfigurationManager<OpenIdConnectConfiguration>(options.MetadataAddress, new ApiGatewayRetriever(_authorityAPI, _org));
                options.Events = new OpenIdConnectEvents()
                {
                    OnRedirectToIdentityProvider = (context) =>
                    {
                        if (context.ProtocolMessage.RequestType == OpenIdConnectRequestType.Authentication)
                        {

                                context.ProtocolMessage.Parameters.Clear();
                                context.ProtocolMessage.IssuerAddress = centralLogin + "?RedirectURL=https://" + GetRequestHostName(context, Configuration);
                                return Task.FromResult(0);

                        }

                        return Task.FromResult(0);
                    }
                    ,
                    OnAuthorizationCodeReceived = authorizationCtx =>
                    {
                        try
                        {
                            BellLogger.WriteLog("OnAuthorizationCodeReceived start:" + authorizationCtx.TokenEndpointRequest.Code, Framework.Common.LogType.Info);

                            HttpClient httpClient = new HttpClient();
                            TokenClientOptions tokenClientOptions = new TokenClientOptions()
                            {
                                ClientId = clientId,
                                ClientSecret = clientSecret,
                                Address = string.Format("{0}/v1/token", _authorityAPI)
                            };
                            var tokenClient = new TokenClient(httpClient, tokenClientOptions);

                            var tokenResponse = await tokenClient.RequestAuthorizationCodeTokenAsync(authorizationCtx.TokenEndpointRequest.Code, authorizationCtx.TokenEndpointRequest.RedirectUri).Result;
                    authorizationCtx.HandleCodeRedemption(tokenResponse.AccessToken, tokenResponse.IdentityToken);


                        }
                        catch (Exception ex)
                        {
                            Logger.WriteLog(ex, Framework.Common.LogType.Error);
                        }

                        return Task.FromResult(0);
                    }
                };

            });
            services.AddSession(options =>
            {
                options.Cookie.Name = ".lLogin.Session";
                options.IdleTimeout = TimeSpan.FromSeconds(10);
                options.Cookie.IsEssential = true;
            });
            services.ConfigureApplicationCookie(options =>
            {
                options.Cookie.HttpOnly = true;
                options.Cookie.SecurePolicy = Microsoft.AspNetCore.Http.CookieSecurePolicy.Always;
            });
            services.Configure<ForwardedHeadersOptions>(options =>
            {
                options.ForwardedHeaders = ForwardedHeaders.All;
            });
            services.AddControllersWithViews();
            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
        }


        private void CheckSameSite(HttpContext httpContext, CookieOptions options)
        {
            if (options.SameSite == SameSiteMode.None)
            {
                var userAgent = httpContext.Request.Headers["User-Agent"].ToString();
                // TODO: Use your User Agent library of choice here.
                if (userAgent.Contains("Chrome/5") || userAgent.Contains("Chrome/6") || userAgent.Contains("Chrome/9"))
                {
                    // For .NET Core < 3.1 set SameSite = (SameSiteMode)(-1)
                    options.SameSite = SameSiteMode.Unspecified;
                }
            }
        }



            app.UseHttpsRedirection();
            app.UseStaticFiles();
            //app.UseAntiXssMiddleware();
            app.UseCookiePolicy();
            app.UseAuthentication();
            app.UseRouting();
            app.UseSession();
            app.UseAuthorization();
            app.UseForwardedHeaders();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }
Microsoft Identity Manager
Microsoft Identity Manager
A family of Microsoft products that manage a user's digital identity using identity synchronization, certificate management, and user provisioning.
605 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,362 questions
0 comments No comments
{count} votes