.Net Core 3.+ OAuth Authentication for a distributed scenario in kubernetes

4/3/2020

I have an application where i'm currently using GitHub and GitLab as OAuth2.0 providers. For now the application has been a single instance which meant that once authorized the framework took care of the rest using httpContext, and seeing as there were only one instance i didn't have to care about synchronizing the context in a distributed setup.

Requirements for scalability means that we have taken the route into kubernetes and such in the future we will have additional replicas of the API. This has proven to be a problem, fx:

I have a 3 replica setup behind a loadbalancer, i authorized as usual and everything worked fine, until i made subsequent calls which where routed to another replica that did not share the authorization context.

What is the best practice for OAuth2.0 in .Net Core 3.+ in a distributed context with the tokens server side?

In my current setup both providers have been setup in the startup.cs configureServices method using the standard AddAuthentication and AddOAuth as seen below: (It is quite verbose, just to be explanatory and in order to help others if interested, Let me know if you want it to be downsized).

services.AddAuthentication(options =>
                {
                    options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                    options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                    options.DefaultChallengeScheme = "GitHub";
                })
                .AddCookie()
                .AddOAuth("GitHub", options =>
                {
                    options.ClientId = Configuration["GitHub:ClientId"];
                    options.ClientSecret = Configuration["GitHub:ClientSecret"];
                    options.CallbackPath = new PathString("/account/signin-github");

                    options.AuthorizationEndpoint = "https://github.com/login/oauth/authorize";
                    options.TokenEndpoint = "https://github.com/login/oauth/access_token";
                    options.UserInformationEndpoint = "https://api.github.com/user";

                    options.ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "id");
                    options.ClaimActions.MapJsonKey(ClaimTypes.Name, "name");
                    options.ClaimActions.MapJsonKey("urn:github:login", "login");
                    options.ClaimActions.MapJsonKey("urn:github:url", "html_url");
                    options.ClaimActions.MapJsonKey("urn:github:avatar", "avatar_url");

                    options.SaveTokens = true;

                    options.Events = new OAuthEvents
                    {
                        OnCreatingTicket = async context =>
                        {
                            var request =
                                new HttpRequestMessage(HttpMethod.Get, context.Options.UserInformationEndpoint);
                            request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                            request.Headers.Authorization =
                                new AuthenticationHeaderValue("Bearer", context.AccessToken);

                            var response = await context.Backchannel.SendAsync(request,
                                HttpCompletionOption.ResponseHeadersRead, context.HttpContext.RequestAborted);
                            response.EnsureSuccessStatusCode();

                            var user = JsonDocument.Parse(await response.Content.ReadAsStringAsync());

                            context.RunClaimActions(user.RootElement);
                        }
                    };
                })
                .AddOAuth("GitLab", options =>
                {
                    options.ClientId = Configuration["GitLab:ClientId"];
                    options.ClientSecret = Configuration["GitLab:ClientSecret"];
                    options.CallbackPath = new PathString("/account/signin-gitlab");

                    options.AuthorizationEndpoint = "https://gitlab.com/oauth/authorize";
                    options.TokenEndpoint = "https://gitlab.com/oauth/token";
                    options.UserInformationEndpoint = "https://gitlab.com/api/v4/user";

                    options.SaveTokens = true;

                    options.Events = new OAuthEvents
                    {
                        OnCreatingTicket = async context =>
                        {
                            var request =
                                new HttpRequestMessage(HttpMethod.Get, context.Options.UserInformationEndpoint);
                            request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                            request.Headers.Authorization =
                                new AuthenticationHeaderValue("bearer", context.AccessToken);
                            var response = await context.Backchannel.SendAsync(request,
                                HttpCompletionOption.ResponseHeadersRead, context.HttpContext.RequestAborted);
                            response.EnsureSuccessStatusCode();

                            var user = JsonDocument.Parse(await response.Content.ReadAsStringAsync());
                            context.RunClaimActions(user.RootElement);
                        }
                    };
                });
-- Ulrik. S
.net
c#
distributed-system
kubernetes
oauth-2.0

0 Answers