Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Middleware not handling 'signin-google' route after successful authentication in Asp.Net Core Web Api External Login Authentication #14169

Closed
1 task
Raghav-Kathuria opened this issue Sep 6, 2019 · 12 comments · Fixed by #28334
Assignees
Labels
doc-enhancement seQUESTered Identifies that an issue has been imported into Quest.
Milestone

Comments

@Raghav-Kathuria
Copy link

Raghav-Kathuria commented Sep 6, 2019

Middleware not handling 'signin-google' route after successful authentication.

Problem Statement:

While trying External Login (particularly Google) with Identity in Asp.Net Core 2.2 Web API I'm facing issue with the implementation and it's flow. I'm able to redirect the user screen to Google's login consent screen using the Challenge. Once the authentication is successful, google redirects the flow back to the application with the 'signin-google' route. For example:

https:///signin-google?state=&scope=email+profile+openid+https://www.googleapis.com/auth/userinfo.profile+https://www.googleapis.com/auth/userinfo.email&authuser=0&session_state=9f072e1b374a78ac5317af025c62fd7cd2699748..ff8f&prompt=none

#Expectation:

Middleware should handle this route and should call the api for which the path was given as AuthenticationProperties in the ExternalLogin call.

But we are getting the 500 Internal Server error and the request fails every time.

Please help with How to redirect the flow on successfully authenticating the request by google (or any other oauth provider) using .net core web api.

Software versions

Check the .NET target framework(s) being used, and include the version number(s).

  • [2.2 ] .NET Core
  • Identity

May be there's something missing from my side, or maybe there is some tricky part in the implementation. I'm stuck! Please HELP!


Document Details

Do not edit this section. It is required for learn.microsoft.com ➟ GitHub issue linking.


Associated WorkItem - 62988

@Raghav-Kathuria
Copy link
Author

@Tratcher If you can help on this

@Tratcher
Copy link
Member

Tratcher commented Sep 6, 2019

But we are getting the 500 Internal Server error and the request fails every time.

What's the exception that goes with this? It should show up on the developer exception page or in the server logs.

@Rick-Anderson Rick-Anderson added this to the Backlog milestone Sep 6, 2019
@Rick-Anderson
Copy link
Contributor

Why are you using Google signIn with a Web API app. Your code doesn't look anything like our sample docs. This sounds like a general question about using ASP.NET Core. While we try to look at and respond to all issues, for questions like this we recommend posting to a community support group like Stack Overflow with the asp.net-core tag.

@Raghav-Kathuria
Copy link
Author

@Rick-Anderson Sure, I will do that. Before that,

I am using Google signIn with the Web API because I'm using Angular as the Front-end, asp.net core 2.2 web api's as the middle layer. I want to make use of asp.Net's Identity for all the User login needs including the External Logins. I'm able to do Register, SignIn, ForgotPassword, Email verification but only the External Login is the stopper for me.

While I post this on stackoverflow and someone answers it, could you please clarify whether External Login is possible with .net core 2.2 Web Api app. If not, could you direct me towards a better solution for my use case.

@Rick-Anderson
Copy link
Contributor

Yes, but obviously Web API doesn't do the UI need to log in. Why don't you create a Razor Pages (RP) app with auth and add a simple Web API controller. Get that working, then migrate the RP app to Angular? Worst case, you could do the signin/out with RP and the rest of the app with Angular.

@Raghav-Kathuria
Copy link
Author

Thanks for the advice @Rick-Anderson.
My App's UI is in HTML and Javascript using few third party libraries, use-case here doesn't allow me much to separate the UI of the login module and rest of the app.

Is there any work around to achieve Login module's middle layer using Identity with External Logins in Web Api and keeping the frontend in Angular.

This is possible in .Net 4.7. Is there a reason why this is disallowed in .Net Core?

@MahdiKarimipour
Copy link

MahdiKarimipour commented Aug 7, 2021

@Tratcher

We are in the same situation with a react frontend and asp.net api (.net 5) supported by Asp.NET Identity. All the flows are fine except external authentication with Google failing with a 500 and the below details, upon redirection to the api hosted on azure after a successful google auth. On local everything is fine and it works end to end, only fails on Azure.

An error was encountered while handling the remote login. Correlation failed.
.AspNetCore.Correlation.xxx cookie not found.

I appreciate if you could share your thoughts on this.

Parameters:

Redirect Uri (behind a custom domain): https://xyz-api.azurewebsites.net/signin-google
React app talks to the custom domain through azure api manager

Pipeline:

      app.UseForwardedHeaders(new ForwardedHeadersOptions()
      {
          ForwardedHeaders = Microsoft.AspNetCore.HttpOverrides.ForwardedHeaders.XForwardedFor | Microsoft.AspNetCore.HttpOverrides.ForwardedHeaders.XForwardedProto
      });
      
      app.UseHttpsRedirection();
      app.UseRouting();
      app.UseCookiePolicy(new CookiePolicyOptions()
      {
          HttpOnly = HttpOnlyPolicy.Always,
          Secure = CookieSecurePolicy.Always,
          MinimumSameSitePolicy = SameSiteMode.Lax
      });
      
      app.UseAuthentication();
      app.UseAuthorization();
      
      app.UseEndpoints(endpoints =>
      {
          endpoints.MapControllerRoute(
          name: "default",
          pattern: "{controller=Home}/{action=Index}/{id?}");
      });

Services (have tried with/without cookie policy)

        services.AddAuthentication(x =>
        {
            x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        })
        .AddCookie()
        .AddJwtBearer(x =>
        {
            x.RequireHttpsMetadata = true;
            x.SaveToken = true;
            x.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuer = true,
                ValidIssuer = appSettings.AuthSettings.Issuer,
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(
                    Encoding.ASCII.GetBytes(
                        Configuration.GetSection(nameof(AppSecrets)).Get<AppSecrets>().AuthSecrets.Secret)),
                ValidAudience = appSettings.AuthSettings.Audience,
                ValidateAudience = true,
                ValidateLifetime = true,
                ClockSkew = TimeSpan.FromMinutes(1)
            };
            x.Events = new JwtBearerEvents
            {
                OnAuthenticationFailed = context =>
                {
                    if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
                    {
                        context.Response.Headers.Add("Token-Expired", "true");
                    }
                    return Task.CompletedTask;
                }
            };
        })
        .AddGoogle(options =>
        {
            options.ClientId = googleSecrets.AuthClientId;
            options.ClientSecret = googleSecrets.AuthClientSecret;
            options.SignInScheme = Microsoft.AspNetCore.Identity.IdentityConstants.ExternalScheme;
        });

        //Cookie Policy needed for External Auth
        services.Configure<CookiePolicyOptions>(options =>
        {
            // This lambda determines whether user consent for non-essential cookies is needed for a given request.
            options.CheckConsentNeeded = context => true;
            options.MinimumSameSitePolicy = SameSiteMode.Unspecified;
        });

        services.AddIdentityCore<ApplicationUser>()
            .AddRoles<ApplicationRole>()
            .AddEntityFrameworkStores<IdentityDBContext>()
            .AddSignInManager()
            .AddDefaultTokenProviders();

@MahdiKarimipour
Copy link

Fixed by explicitly defining our custom domain in our API middleware.

The fact the code works locally meant there was an issue with multiple domains we have for Azure App Service in production (one being the custom domain, and the other being xxx.azurewebsites.net. We confirmed this is the case by pointing everything to azurewebsites.net, and things started to work properly.

The reason cookie can not be found is the fact that before redirect to Google from our site, Asp.NET Identity generated the cookie for api.domain.com, however when Google wanted to redirect back to our API hosted on Azure, it would redirect back to xxx.azurewebsites.com, and obviously, no Cookie could be found there.

It seems like Azure doesn't pass the host header to the actual API, and that might be the reason Google points to xxx.azurewebsites.net and not the custom domain. This was the reason that Use Header Forwadders in the middleware was useless.

What I ended up doing was to define explicitly the hostname in the request pipeline, so every outgoing request would have the custom domain as the actual domain. So before every other function in the middleware, put:

        app.Use((context, next) =>
        {
            context.Request.Host = new HostString("api.domain.com);
            context.Request.PathBase = new PathString("/fragment/identity"); //if you need this
            context.Request.Scheme = "https";
            return next();
        });

@Rick-Anderson
Copy link
Contributor

@MahdiKarimipour thanks for the report. Can you write this up so I can add it to the doc or PR the doc itself? Perhaps a suggestion how to debug the problem too.

@Tratcher
Copy link
Member

Tratcher commented Aug 9, 2021

See https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer, especially #forwarded-headers and #troubleshoot

@MahdiKarimipour
Copy link

MahdiKarimipour commented Aug 10, 2021

@Tratcher

The documentation indeed addresses the problem at hand, however, the challenge is, you could still spend hours (if not days) understanding the problem if there is no mention of symptoms (what screen you see, when) along with the troubleshooting guides.

@Rick-Anderson

The SPA backed by Microsoft stack (API, Identity, Gateway, Hosting) is a common pattern, but I didn't find a doc explaining the whole flow (0-100) using the stack while addressing common problems you could face when developing such flow, along with indicating the symptoms to the problems. It might indeed exist, I just didn't find it with my lame search skills.

To clarify what I mean, and for anyone who ends up here in future, I wrote it up, and I hope it saves some hours for someone who is developing such flow and want to stay centred around Microsoft stack for such a purpose.

This is using React (with no google-login component), Asp.NET API, Asp.NET Identity, Azure App Services, and Azure API Manager:

https://mahdikarimipour.com/blog/google-auth-for-react-with-aspnet-identity
https://pellerex.com/blog/google-auth-for-react-with-aspnet-identity

@Rick-Anderson Rick-Anderson added the reQUEST Triggers an issue to be imported into Quest label Feb 8, 2023
@github-actions github-actions bot added seQUESTered Identifies that an issue has been imported into Quest. and removed reQUEST Triggers an issue to be imported into Quest labels Feb 8, 2023
@Rick-Anderson Rick-Anderson changed the title Middleware not handling 'signin-google' route after successful authentication in Asp.Net Core 2.2 Web Api External Login Authentication Middleware not handling 'signin-google' route after successful authentication in Asp.Net Core Web Api External Login Authentication Feb 8, 2023
@borrmann
Copy link

I am currently trying to implement this as well and stepped over this issue. According to the MAUI docs, using External Logins with .NET Core API is the suggested route instead doing the authentication on the client side or other UI like RP, since they have implemented this flow in the WebAuthenticator.
With the provided info here I was able to get it to work when I call the API from a web browser (either localhost or via browser from a different network). However, when I call the API from the WebAuthenticator or Chrome from an Android device, I still receive a 500, which I dont really understand.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
doc-enhancement seQUESTered Identifies that an issue has been imported into Quest.
Projects
No open projects
Development

Successfully merging a pull request may close this issue.

5 participants