dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.43k stars 10.01k forks source link

Google Auth- Asp.net core 3.1 #18858

Closed kishorpise closed 3 years ago

kishorpise commented 4 years ago

For Social outh integration, I am trying some simple implementation. I am always getting The auth state was missing or invalid. There is no much documentation available.

I read the post. https://github.com/dotnet/aspnetcore/issues/6486 it looks its out dated and not true. when I just add nuget reference, I have yellow mark on owin and others. and If I see release date of Microsoft.AspNetCore.Authentication.Google, It looks its released recently. how ever in my case I get only error without any hint for such a simple usecase.

Any hint will be appreciated.

Regards Kishor

blowdart commented 4 years ago

You're adding the wrong references. OWIN is not for Core. Follow the Core 2 or Core 3 instructions.

kishorpise commented 4 years ago

Yes.. you are correct. That is what I also mentioned.

blowdart commented 4 years ago

OK, what error are you getting? Can we see your startup class (remove any secrets first) and your csproj

kishorpise commented 4 years ago

Error what I am getting is -----The oauth state was missing or invalid. Below is entire startup class. (asp.net core 3.1.1) and configuration in json.

 "ClientId": "XXXX",
    "ClientSecret": "XXXXXX",
    "CallbackPath": "/Account/GoogleCallBack",
    "project_id": "XXXXXXX",
    "auth_uri": "https://accounts.google.com/o/oauth2/auth",
    "token_uri": "https://oauth2.googleapis.com/token",
    "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs"

StartupClass

    public class Startup
    {

        ILoggerFactory loggerFactory;
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<AppDbContext>(config =>
            {
                config.UseInMemoryDatabase("Memory");
            });

            // AddIdentity registers the services
            services.AddIdentity<IdentityUser, IdentityRole>(config =>
            {
                config.Password.RequiredLength = 4;
                config.Password.RequireDigit = false;
                config.Password.RequireNonAlphanumeric = false;
                config.Password.RequireUppercase = false;
                config.SignIn.RequireConfirmedEmail = true;
            })
                .AddEntityFrameworkStores<AppDbContext>()
                .AddDefaultTokenProviders();

            services.ConfigureApplicationCookie(config =>
            {
                config.Cookie.Name = "Identity.Cookie";
                config.LoginPath = "/Home/Index";

            });

            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
            services.AddSingleton(Configuration);

            services.AddAuthentication(options =>
            {
                options.DefaultChallengeScheme =  CookieAuthenticationDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = AzureADDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = GoogleDefaults.AuthenticationScheme;
            }

            ).AddCookie(config =>
            {
                config.Cookie.Name = "Identity.Cookie";
                config.LoginPath = "/Home/Index";

            }).

            AddGoogle("Google",

                options =>
                {
                    options.ClaimActions.Clear();
                    options.Scope.Add("profile");

                    options.Events = new OAuthEvents
                    {
                        OnRemoteFailure = context =>
                        {
                            context.HandleResponse();
//Here I get Error
                            var error = context.Failure.Message;
                            return Task.FromResult(0);
                        }
                    };

                    Configuration.Bind("google", options);
                }  )  ;

            services.AddCors(options =>
            {
                options.AddPolicy("AllowAll", p =>
                {
                    p.AllowAnyOrigin()
                    .AllowAnyHeader()
                    .AllowAnyMethod()
                    .AllowCredentials();
                });
            });

            services.AddAuthorization(options =>
            {

                options.AddPolicy("Cookies",
                    authBuilder =>
                    {
                        authBuilder.RequireRole("Administrators");
                    });

            });

            services.AddControllersWithViews();

            services.AddTransient<IEmailSender, EmailSender>();

        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env,  ILoggerFactory loggerFactory)
        {

            this.loggerFactory = loggerFactory;

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }
            app.UseHttpsRedirection();
            app.UseStaticFiles();

            app.UseRouting();
            app.UseAuthentication();
            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
                endpoints.MapControllerRoute(
                    name: "Api",
                    pattern: "{controller=Account}/{action?}");
            });
        }
    }
}
blowdart commented 4 years ago

@HaoK @Tratcher

blowdart commented 4 years ago

Oh what browser are you using?

kishorpise commented 4 years ago

Chrome- Ideally this should not be problem. I just checked on IE. same issue. Below is signature in controller. [HttpGet] [Route("[controller]/[action]")]
public IActionResult GoogleCallBack() {.....

blowdart commented 4 years ago

Ah ok that rules out out the samesite changes chrome made.

Tratcher commented 4 years ago

You're trying to combine things in a confusing way. You already have AddIdentity so you don't need any of this code:

            services.AddAuthentication(options =>
            {
                options.DefaultChallengeScheme =  CookieAuthenticationDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = AzureADDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = GoogleDefaults.AuthenticationScheme;
            }

            ).AddCookie(config =>
            {
                config.Cookie.Name = "Identity.Cookie";
                config.LoginPath = "/Home/Index";

            }).

So the simplified code looks like this:

            services.AddAuthentication()
                .AddGoogle(options =>
                {
                   // ...
                    Configuration.Bind("google", options);
                })  ;

See https://docs.microsoft.com/en-us/aspnet/core/security/authentication/social/google-logins?view=aspnetcore-3.1#configure-google-authentication

Also, CallbackPath does not refer to an MVC endpoint, it's a path that's handled internally by the auth middleware. The default is "/signin-google". Identity provides its own MVC callbacks in the Account controller that are called after the middleware completes.

Why are you trying to chang the token endpoint? Note Configuration.Bind isn't picking up that config line because the keys don't match.

That said, none of this explains your error message. Sharing a Fiddler trace would help track the affected fields.

kishorpise commented 4 years ago

Yes code is tried for getting solution for the resolution of error. I have removed the block and tried same error is occurring. -Regarding fidler output I can see, all responses are correct. (Redirect etc.) below is -(little modified request and response)

I can see state, redirect url, scope is correct.

GET /o/oauth2/v2/auth?response_type=code&client_id=XXXXXX&redirect_uri=https%3A%2F%2Flocalhost%3A44361%2FAccount%2FExternalLoginCallback&scope=profile&state=CfDJ8MlYFAXWQwBGo4IKoVfdgvOLbG8GxfxMyGCK6-uEj2yQRZ8OWsMCkdjaZKZ5J3twa7rpsGkdTio6Ey5884X5ySTxHHLIWSTQlpZxoRd7C4Tbv78CuItmssnZI_j_EC3oVNNe1DwpzSAZsUDwZQgaygKe3Bk7gp0hL9ITBtqpkXpKgn_cridNbAOaeZKtS7qhjMd_83RjYApXeixa-yWhKebBqOyK52TsRs__ZeiJuJclxu9M1EPIyadjsmv2R8gMjlvbaN8BwHNh5lvCmFev2y4 HTTP/1.1

I can see its response passed as

https://localhost:44361/Account/ExternalLoginCallback?state=CfDJ8MlYFAXWQwBGo4IKoVfdgvOLbG8GxfxMyGCK6-uEj2yQRZ8OWsMCkdjaZKZ5J3twa7rpsGkdTio6Ey5884X5ySTxHHLIWSTQlpZxoRd7C4Tbv78CuItmssnZI_j_EC3oVNNe1DwpzSAZsUDwZQgaygKe3Bk7gp0hL9ITBtqpkXpKgn_cridNbAOaeZKtS7qhjMd_83RjYApXeixa-yWhKebBqOyK52TsRs__ZeiJuJclxu9M1EPIyadjsmv2R8gMjlvbaN8BwHNh5lvCmFev2y4&code=4/wQEYFHjOZmI2BNBC0iB7rkOo-O94lecpVN9TUr5Ycg9Gvb1s8SggluvPzpGQv3bkxj0lAfzay9B1v5TeYfTAbEI&scope=profile+https://www.googleapis.com/auth/userinfo.profile Content-Language

Again passed- another request - GET /Account/ExternalLoginCallback?state=CfDJ8MlYFAXWQwBGo4IKoVfdgvOLbG8GxfxMyGCK6-uEj2yQRZ8OWsMCkdjaZKZ5J3twa7rpsGkdTio6Ey5884X5ySTxHHLIWSTQlpZxoRd7C4Tbv78CuItmssnZI_j_EC3oVNNe1DwpzSAZsUDwZQgaygKe3Bk7gp0hL9ITBtqpkXpKgn_cridNbAOaeZKtS7qhjMd_83RjYApXeixa-yWhKebBqOyK52TsRs__ZeiJuJclxu9M1EPIyadjsmv2R8gMjlvbaN8BwHNh5lvCmFev2y4&code=4/wQEYFHjOZmI2BNBC0iB7rkOo-O94lecpVN9TUr5Ycg9Gvb1s8SggluvPzpGQv3bkxj0lAfzay9B1v5TeYfTAbEI&scope=profile+https://www.googleapis.com/auth/userinfo.profile HTTP/1.1

I can see external cookie is set properly. and passed to next request. but in asp.net server. I am getting exception at

OnRemoteFailure = context => { context.HandleResponse(); var error = context.Failure.Message; return Task.FromResult(0); }

Since this is not handled, page is always blank. Now my doubt is on controller. as I can see lot of param passed, but I think that is handled by middle ware, before it reaches to call back.

Below is my signature.

[HttpGet] [Route("[controller]/[action]")]
public IActionResult ExternalLoginCallback() {

my redirect url is correctly reflected and it looks. /signin-google has nothing to do with it.

Thanks and appreciate your help. Issue is not resolved yet.

Regards Kishor

Tratcher commented 4 years ago

You've set CallbackPath to /Account/ExternalLoginCallback and that's interfering with Identity. Change CallbackPath back to /signin-google and let the middleware take care of it. The middleware will redirect to /Account/ExternalLoginCallback when it's done.

kishorpise commented 4 years ago

Thanks for your hint. I take /signin-google is hardcoding, not a good practice... any way it worked. but now, I can see _signInManager.GetExternalLoginInfoAsync( ) is always returning null. I checked in fidler, I can see /Account/ExternalLoginCallback has Identity.External cookie.

Tratcher commented 4 years ago

Thanks for your hint. I take /signin-google is hardcoding, not a good practice...

It doesn't have to be /signin-google, that's only the default. The requirement is that it not conflict with any other endpoint in your app.

but now, I can see _signInManager.GetExternalLoginInfoAsync( ) is always returning null. I checked in fidler, I can see /Account/ExternalLoginCallback has Identity.External cookie.

Enable the debug application logs to find out why. Sharing an updated version of your Startup would also be a good sanity check.

kishorpise commented 4 years ago

There is something interesting going on. Below is truncated/tampered log. -- No candidates found for the request path '/Account/signin-google' seems exception.

Log.--

Microsoft.AspNetCore.Mvc.ChallengeResult: Information: Executing ChallengeResult with authentication schemes (Google).
Microsoft.AspNetCore.Authentication.Google.GoogleHandler: Debug: HandleChallenge with Location: https://accounts.google.com/o/oauth2/v2/auth?XXXXXXXXXXXXXXXXXpath=/Account/signin-google; secure; samesite=none; httponly.
Microsoft.AspNetCore.Authentication.Google.GoogleHandler: Information: AuthenticationScheme: Google was challenged.
Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker: Information: Executed action KWebServer.Controllers.AccountController.SignIn (KWebServer) in 221.1968ms
Microsoft.AspNetCore.Routing.EndpointMiddleware: Information: Executed endpoint 'KWebServer.Controllers.AccountController.SignIn (KWebServer)'
Microsoft.AspNetCore.Hosting.Diagnostics: Information: Request finished in 284.3989ms 302 
Microsoft.AspNetCore.Server.IIS.Core.IISHttpServer: Debug: Connection ID "18158513712053354613" disconnecting.
Microsoft.AspNetCore.Hosting.Diagnostics: Information: Request starting HTTP/2.0 GET https://localhost:44361/Account/signin-google?state=XXXXXXXXXXXqM&scope=profile+https://www.googleapis.com/auth/userinfo.profile  
Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware: Debug: The request path /Account/signin-google does not match a supported file type
Microsoft.AspNetCore.Routing.Matching.DfaMatcher: Debug: No candidates found for the request path '/Account/signin-google'
Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware: Debug: Request did not match any endpoints

Startup --

services.AddDbContext<AppDbContext>(config =>
            {
                config.UseInMemoryDatabase("Memory");
            });

            // AddIdentity registers the services
            services.AddIdentity<IdentityUser, IdentityRole>(config =>
            {

            })
                .AddEntityFrameworkStores<AppDbContext>()
                .AddDefaultTokenProviders();

            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
            services.AddSingleton(Configuration);

            services.AddAuthentication().AddGoogle("Google", "Google",

                options =>
                {
                    Configuration.Bind("google", options);

                    options.ClaimActions.Clear();
                    options.SignInScheme = "Identity.External";
                    options.Scope.Clear();
                    options.Scope.Add("profile");
                    options.Events = new OAuthEvents
                    {
                        OnRemoteFailure = context =>
                        {
                            context.HandleResponse();
                            var error = context.Failure.Message;
                            return Task.FromResult(0);
                        }
                    };

                }

                )
            ;

            services.AddControllersWithViews();
            services.AddMvc();
            services.AddTransient<IEmailSender, EmailSender>();
Tratcher commented 4 years ago

Microsoft.AspNetCore.Routing.Matching.DfaMatcher: Debug: No candidates found for the request path '/Account/signin-google' That's fine, the auth middleware doesn't use endpoint routing, it does its own path matching later.

What was the response for the https://localhost:44361/Account/signin-google request?

kishorpise commented 4 years ago

1) Response for the https://localhost:44361/Account/signin-google This localhost page can’t be found

2) I added another provider, and I found same error.

Microsoft.AspNetCore.Authentication.Google.GoogleHandler: Information: Error from RemoteAuthentication: The oauth state was missing or invalid..

but the interesting point, in both provider, Google and AzureAD- exception is going to below block. OnRemoteFailure - statement var error = context.Failure.Message;

 services.AddAuthentication().AddGoogle("Google", "Google",

                options =>
                {
                    Configuration.Bind("google", options);

                    options.SignInScheme = "Identity.External";
                    options.UserInformationEndpoint = "https://www.googleapis.com/oauth2/v1/certs";
                    options.Scope.Clear();
                    options.Scope.Add("email");

                    options.Events = new OAuthEvents
                    {
                        OnRemoteFailure = context =>
                        {
                            context.HandleResponse();
                            var error = context.Failure.Message;
                            return Task.FromResult(0);
                        }
                    };

                }

                ).AddAzureAD(options =>
              {
                  Configuration.Bind("AzureAd", options);

              })
            ;
Tratcher commented 4 years ago

These partial code samples are getting hard to keep track of without context. Can you share a complete sample as a github repo?

kishorpise commented 4 years ago

Hello, I have created repo. Please download and confirm, I would delete later on and would update thread in detailed manner with solution

https://github.com/kishorpise/18858

Regards Kishor

Tratcher commented 4 years ago

What's with Startup1? It's not being used at the moment.

Do you actually want to map remote accounts to local accounts, or only use remote accounts like AAD and Google? This could be simplified if you do not need local accounts and can remove Identity. AddAzureAD doesn't combine well with Identity because it tries to mange its own cookie. https://github.com/dotnet/aspnetcore/blob/3da1b107efb01c4fc7577a4abcda3fa099e830a7/src/Azure/AzureAD/Authentication.AzureAD.UI/src/AzureADAuthenticationBuilderExtensions.cs#L128-L129

Remove this line, SignInScheme is set automatically: https://github.com/kishorpise/18858/blob/de304be322d78275ddf9516abe5af3fc9d222c69/Startup.cs#L59 Why are you setting UserInformationEndpoint? That doesn't look like the right value. https://github.com/kishorpise/18858/blob/de304be322d78275ddf9516abe5af3fc9d222c69/Startup.cs#L60 https://github.com/dotnet/aspnetcore/blob/f7dc095e9a10329f1cbecdb795680c65008bcb2d/src/Security/Authentication/Google/src/GoogleDefaults.cs#L23

Move UseAuthentication from here to here. It needs the routing endpoints to be calculated before it can pick up their policies.

Remove AAD's CallbackPath from config, or set it to /signin-aad. Having it set to /Account/ExternalLoginCallback conflicts with a later step in the login flow. https://github.com/kishorpise/18858/blob/de304be322d78275ddf9516abe5af3fc9d222c69/appsettings.json#L18 https://github.com/kishorpise/18858/blob/de304be322d78275ddf9516abe5af3fc9d222c69/appsettings.Development.json#L17

The same for Google's CallbackPath. https://github.com/kishorpise/18858/blob/de304be322d78275ddf9516abe5af3fc9d222c69/appsettings.json#L24 https://github.com/kishorpise/18858/blob/de304be322d78275ddf9516abe5af3fc9d222c69/appsettings.Development.json#L23

What's with all of these other config settings for Google? They're not being used for anything because the key names don't match. https://github.com/kishorpise/18858/blob/de304be322d78275ddf9516abe5af3fc9d222c69/appsettings.json#L25-L28

kishorpise commented 4 years ago

Thanks for your prompt reply. Answers for each and every point as below. Objective is just get the email address from both provider in same application, -if possible name, and picture. rest would be managed within application. No mapping is needed.

1) Startup1 was just backup. I removed and didn't get chance to delete before upload on repo. 2) AddAzureAD doesn't combine well with Identity because it tries to mange its own cookie. there has to be option for modifying the cookie.

3)SignInScheme removed. This was placed after reading other blogs. 4) UserInformationEndpoint placed to get the info from google, after reading other blogs to see if that could be reason. 5) UseAuthentication moved to earlier place as suggested. this was tried as it signed in but info object was null. 6) for changing signin-aad and call back. I need to change registration for both provider. will make 2 call backs for each and let you know.

7) Other key names are kept for future functionality, it will not cause any issue. will remove.

I am making changes and will get back to you.

Regards Kishor.

kishorpise commented 4 years ago

I have made those changes... still same error. Below is log.

[14:59:35 INF] Request starting HTTP/2.0 GET https://localhost:44361/Account/SignIn/Google
[14:59:35 INF] Executing endpoint 'KlickITWebServer.Controllers.AccountController.SignIn .......

[14:59:36 INF] Executing action method KlickITWebServer.Controllers.AccountController.SignIn (KlickITWebServer) - Validation state: "Valid" [14:59:36 INF] Executed action method KlickITWebServer.Controllers.AccountController.SignIn (KlickITWebServer), returned result Microsoft.AspNetCore.Mvc.ChallengeResult in 1.1912ms. [14:59:36 INF] Executing ChallengeResult with authentication schemes (["Google"]). [14:59:36 INF] AuthenticationScheme: Google was challenged. ..... [14:59:36 INF] Request starting HTTP/2.0 GET https://localhost:44361/Account/ExternalLoginCallback?state=CfDJ8MlYFAXWQwBGo4IKoVfdgvNAprTKL3VuDGUsKD3uv4ZF3pOkb4aztwxPIgXOUpRCyXqVJCcOhcL7LTXHEdSotw5s7XCTf21Ii8wqOKCz-I7kmDkGhzC-RDNifoDlO1t975wxSasqsgNOY94VWe6Cn0ln2tajS4w1fCz1JrnhByvCiNUHsOLCzDXseyrXwlUF3vdtp06knrPNTmmEiE59RyJq727sYWKYcenOS_FcDfdjdnec_2ocTalRyv0P8E39FXNMjHi5HenYJAJWqSDX-r8&code=4/wQGaCipKJymkgQSmb_wk9c5vmIO6KJP-TY1CIC2Bu_YvmHzKLXkjFtvai_qWcPkivGkk27NzL3EFs9_hz3WNx-Q&scope=email+openid+https://www.googleapis.com/auth/userinfo.email&authuser=0&prompt=none&session_state=a53c2c14ac7ac1bbdfdfef66850fa9a02ce56f4a..ee68
.... [14:57:29 INF] AuthenticationScheme: Identity.External signed in. [14:57:29 INF] Request starting HTTP/2.0 GET https://localhost:44361/Account/ExternalLoginCallback
[14:57:29 INF] Error from RemoteAuthentication: The oauth state was missing or invalid..

For Azure --- [15:03:38 INF] Request starting HTTP/2.0 POST https://localhost:44361/Account/signin-aad [15:03:38 INF] AuthenticationScheme: AzureADCookie signed in. [15:03:38 INF] Request starting HTTP/2.0 GET https://localhost:44361/Account/ExternalLoginCallback
[15:03:38 INF] Error from RemoteAuthentication: The oauth state was missing or invalid.. [15:03:38 INF] Request finished in 7.986ms 200

I found browser Jumps to https://localhost:44361/Account/ExternalLoginCallback but there is no event fired - so page is just blank and if I put breakpoint. in both cases it come here

OnRemoteFailure = context => { }

I again confirm- commenting one provider also does not work.

Regards Kishor

kishorpise commented 4 years ago

Any update ?

Regards Kishor

Tratcher commented 4 years ago

Those logs don't match the code/config in https://github.com/kishorpise/18858/blob/master/Startup.cs. The errors imply you're still setting CallbackPath to conflicting values. I suggest "/signin-google" and "/signin-aad" without any Account prefix.

RE AddAzureAD and getting it work well with Identity... I'd suggest pealing it back a layer and using AddOpenIdConnect instead. https://github.com/dotnet/aspnetcore/blob/b6698757a85f9b6d6c63311e64dd6ac9e734ef56/src/Security/Authentication/OpenIdConnect/samples/OpenIdConnectSample/Startup.cs#L107-L112 You won't need "AddCookie from that example, Identity already added that. Most of the other settings are only given there for demonstration, you mainly need to set ClientId and Authority. Authority should be Instance + TenantId from your existing config.

kishorpise commented 4 years ago

Dear, as written to your first point- Those logs don't match the code/config in https://github.com/kishorpise/18858/blob/master/Startup.cs. The errors imply you're still setting CallbackPath to conflicting values. I suggest "/signin-google" and "/signin-aad" without any Account prefix.

Entire code is shared with you... this is not production code and we change as we do debug. only constant is configuration values and .net core framework along with port number. rest every thing is temporary to fix the issue. If you are just commenting log is not matching I wanted to know does it work on your side, with these configured values ? If removing controller Account//signin-aad and moving to /signin-aad I would call this as hard coding. I am confirming code does not work on 3.1 framework with latest nuget packages as per the documentation given. AddAzureAd ,AddAzureAD, AddOpenIdConnect and rest is all mess for simple one protocol with funny documentation.

If AddOpenIdConnect is to be used, we should remove from documentation, atleast from microsoft site, ->> https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-v2-aspnet-core-webapp

and are you sure as per the official documentation I have to use instead AddMicrosoftAccount of AddOpenIdConnect

https://docs.microsoft.com/en-us/aspnet/core/security/authentication/social/microsoft-logins?view=aspnetcore-3.1

Thanks confusion. Kishor

Tratcher commented 4 years ago

If AddOpenIdConnect is to be used, we should remove from documentation, atleast from microsoft site

Yes, we have plans to replace AddAzureAD with something more flexible. It works for basic scenarios but it does not adapt well to other scenarios like yours.

and are you sure as per the official documentation I have to use instead AddMicrosoftAccount of AddOpenIdConnect

AddMicrosoftAccount provides OAuth support for Microsoft Accounts (e.g. outlook.com or hotmail.com addresses). AddOpenIdConnect is the general purpose auth component that AddAzureAD is built on.

Unfortunately I think you need more interactive support than we can provide through Github. Every indication so far is that this is a configuration issue and not a product issue so your next option is to contact Microsoft Support so they can help you set this up correctly.

kishorpise commented 4 years ago

I read your thread carefully, I confirm this is product and documentation issue. AddAzureAD is going to be removed, so I should not be using this code, and there should not nuget updates for that.

https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-v2-aspnet-core-webapp?view=aspnetcore-3.1

Sample link for download has netcoreapp2.1

Thanks for your suggestion to contact microsoft support.

Regards Kishor

kmate95 commented 4 years ago

Sorry if off-topic but I just stumbled upon this thread. I had a similar problem with the scaffolded Identity ExternalLogin.cshtml.cs (Asp.Net Core 3.1.3) Where the provider logged in and called back with valid data but the process failed with invalid model error. Like:

[14:57:29 INF] AuthenticationScheme: Identity.External signed in. [14:57:29 INF] Request starting HTTP/2.0 GET https://localhost:44361/Account/ExternalLoginCallback [14:57:29 INF] Error from RemoteAuthentication: The oauth state was missing or invalid.

The problem was that I had Nullable context enabled for my project and it produced a ValidationError when no returnUrl was provided with the login attempt. I changed (string returnUrl = null) to (string? returnUrl = null) on every action (and in all scaffolded Identity pages) and the error was gone. The actual error happens in OnPostConfirmationAsync that's the first check for ModelState.IsValid.

thorkia commented 4 years ago

I am having a similar problem with the callback not being handled for google login.

Here is my sample code: https://github.com/thorkia/GoogleAuthTest

I can get it to work in Node using Passport - but I would prefer my backend to be C#

Here are the logs: [16:01:40 INF] Request starting HTTP/2 GET https://localhost:5001/authentication/external/google-login [16:01:40 INF] Executing endpoint 'GoogleAuthTest.WepAPI.Controllers.ExternalController.GoogleLogin (GoogleAuthTest.WepAPI)' [16:01:40 INF] Route matched with {action = "GoogleLogin", controller = "External", page = ""}. Executing controller action with signature Microsoft.AspNetCore.Mvc.IActionResult GoogleLogin(System.String) on controller GoogleAuthTest.WepAPI.Controllers.ExternalController (GoogleAuthTest.WepAPI). [16:01:40 INF] Executing ChallengeResult with authentication schemes (["Google"]). [16:01:40 INF] AuthenticationScheme: Google was challenged. [16:01:40 INF] Executed action GoogleAuthTest.WepAPI.Controllers.ExternalController.GoogleLogin (GoogleAuthTest.WepAPI) in 38.7567ms [16:01:40 INF] Executed endpoint 'GoogleAuthTest.WepAPI.Controllers.ExternalController.GoogleLogin (GoogleAuthTest.WepAPI)' [16:01:40 INF] Request finished in 132.6938ms 302 [16:01:40 INF] Request starting HTTP/2 GET https://localhost:5001/google-login?response_type=code&client_id=myClientID&redirect_uri=https%3A%2F%2Flocalhost%3A5001%2Fsignin-google&scope=openid%20profile%20email&state=CfDJ8LpsmeNzTMhEjLSTbnpic6Qf19ppL5G0kA3uywH_0fNf-f3EHIFuJdRT-Cmug_5G4WRh7wMKY0tVlg6vUt_ajqj7hvfbX_s4kGvTVVQU3mciC0FwVQ5PXiXzdJiGdHss55t0M5FfYpsyopeiXo1iXhPgLJj9vKMzKaWKr1rZ9_3FQ7ecfHdwtu5cIqrkax8IHr7Y10JGsMrrr4kelPgRq2b7B26fWIPdKmwFk8ej4OI-4rQ0fxr53sH-hsrVeYzNaQ [16:01:40 INF] Request finished in 4.0323ms 404

And in Chrome I see the following error: No webpage was found for the web address: https://localhost:5001/google-login?response_type=code&client_id=myClientId&redirect_uri=https%3A%2F%2Flocalhost%3A5001%2Fsignin-google&scope=openid%20profile%20email&state=CfDJ8LpsmeNzTMhEjLSTbnpic6Qf19ppL5G0kA3uywH_0fNf-f3EHIFuJdRT-Cmug_5G4WRh7wMKY0tVlg6vUt_ajqj7hvfbX_s4kGvTVVQU3mciC0FwVQ5PXiXzdJiGdHss55t0M5FfYpsyopeiXo1iXhPgLJj9vKMzKaWKr1rZ9_3FQ7ecfHdwtu5cIqrkax8IHr7Y10JGsMrrr4kelPgRq2b7B26fWIPdKmwFk8ej4OI-4rQ0fxr53sH-hsrVeYzNaQ

Tratcher commented 4 years ago

Your AuthorizationEndpoint is wrong, it should point to Google's servers with this value: https://github.com/dotnet/aspnetcore/blob/d807f5ca67d3ea81cc93484ffaaaaafe4d640e33/src/Security/Authentication/Google/src/GoogleDefaults.cs#L18

thorkia commented 4 years ago

@Tratcher That worked partially. Thank you. Now I am getting an error about cookie correlation:

[18:01:53 INF] Request starting HTTP/2 GET https://localhost:5001/authentication/external/google-login [18:01:53 INF] Executing endpoint 'GoogleAuthTest.WepAPI.Controllers.ExternalController.GoogleLogin (GoogleAuthTest.WepAPI)' [18:01:53 INF] Route matched with {action = "GoogleLogin", controller = "External", page = ""}. Executing controller action with signature Microsoft.AspNetCore.Mvc.IActionResult GoogleLogin(System.String) on controller GoogleAuthTest.WepAPI.Controllers.ExternalController (GoogleAuthTest.WepAPI). [18:01:53 INF] Executing ChallengeResult with authentication schemes (["Google"]). [18:01:53 INF] AuthenticationScheme: Google was challenged. [18:01:53 INF] Executed action GoogleAuthTest.WepAPI.Controllers.ExternalController.GoogleLogin (GoogleAuthTest.WepAPI) in 8.1016ms [18:01:53 INF] Executed endpoint 'GoogleAuthTest.WepAPI.Controllers.ExternalController.GoogleLogin (GoogleAuthTest.WepAPI)' [18:01:53 INF] Request finished in 30.3892ms 302 [18:01:53 INF] Request starting HTTP/2 GET https://localhost:5001/signin-google?state=CfDJ8LpsmeNzTMhEjLSTbnpic6RZFAADFv1dgmVdfCKESet5Pncoru1fIOlvUSBt1IVRwrVN0ZgKGNtqn9GGmEKIXbfBWRu_uizcWj-rXnS3KvvTjikXvrTI8Bx5agAV9a5iWiZYc2K8dI_YmfsFP26bSzlojv-ytpxT9hk2AZr7v6qmxrIvTjNTUkXw1uzsCC9lP4Spw0dmlqCRDTe7xPpAQImmLcm9rf4Q1VvtnOJuIUbKdgPUChqal0mZlaaeUf3Z5A&code=4%2F0AFjzwaVqSMQhPX4Bn0QZt_L-R4BeGRqB59Kkbga0vb58dj3KvNdaMT9GrZ5z4WO4LpVyQEPOxJTBMfz1s3G9qQ&scope=email+profile+openid+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email&authuser=0&prompt=none [18:01:53 INF] AuthenticationScheme: APIScheme signed in. [18:01:53 INF] Request finished in 156.4635ms 302 [18:01:53 INF] Request starting HTTP/2 GET https://localhost:5001/signin-google?state=CfDJ8LpsmeNzTMhEjLSTbnpic6RZFAADFv1dgmVdfCKESet5Pncoru1fIOlvUSBt1IVRwrVN0ZgKGNtqn9GGmEKIXbfBWRu_uizcWj-rXnS3KvvTjikXvrTI8Bx5agAV9a5iWiZYc2K8dI_YmfsFP26bSzlojv-ytpxT9hk2AZr7v6qmxrIvTjNTUkXw1uzsCC9lP4Spw0dmlqCRDTe7xPpAQImmLcm9rf4Q1VvtnOJuIUbKdgPUChqal0mZlaaeUf3Z5A&code=4%2F0AFjzwaVqSMQhPX4Bn0QZt_L-R4BeGRqB59Kkbga0vb58dj3KvNdaMT9GrZ5z4WO4LpVyQEPOxJTBMfz1s3G9qQ&scope=email+profile+openid+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email&authuser=0&prompt=none [18:01:53 WRN] '.AspNetCore.Correlation.Google.B79ES2TpzMIbrVQ6aDocRyYMEDPTTCDW3VHA0gJYU30' cookie not found. [18:01:53 INF] Error from RemoteAuthentication: Correlation failed.. [18:01:53 ERR] An unhandled exception has occurred while executing the request. System.Exception: An error was encountered while handling the remote login. ---> System.Exception: Correlation failed. --- End of inner exception stack trace --- at Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler`1.HandleRequestAsync() at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context) [18:01:54 INF] Request finished in 176.8483ms 500 text/html; charset=utf-8

Tratcher commented 4 years ago

Sharing a Fiddler trace is the best way to troubleshoot cookie issues. Also, turning the logs up to debug level.

thorkia commented 4 years ago

@Tratcher I have attached the debug logs and the Fiddler trace. I removed my clientId from the items

10_Full.txt debugLogs.txt

Tratcher commented 4 years ago

Looks like you're stuck in a loop here...

https://github.com/thorkia/GoogleAuthTest/blob/ee6dd36788269fab3c104623315abbb114f26a46/GoogleAuthTest.WepAPI/Startup.cs#L59 Your redirect here is getting overwritten because you don't tell the framework that you handled the request. It does its default redirect back to the url that issued the initial challenge, which issues another challenge and around you go again. ctx.HandleResponse() Add this after your redirect above.

Tratcher commented 4 years ago

Also, that url format doesn't work here. Redirect("~/Index"); ~ is an MVC link generation specific feature. It should probably be just Redirect("/Index");

thorkia commented 4 years ago

@Tratcher That worked perfectly thanks!

I had tried the Redirect("~/Index"); as an alternative to the Redirect("/Index"); to see if that would work.

Adding the ctx.HandleResponse() was the perfect solution! Thanks!

I will update my Git sample. Feel free to use it as a simple sample!

lagonell commented 4 years ago

I have the same problem and I cannot find the solution, in google I found that the problem is due to chrome, see the following photo:

Error_Google

Does someone know how to solve this problem?

kishorpise commented 4 years ago

I stopped as there was no solution… now I would work again to see if it can be fixed.

Regards Kishor

From: Edgar Lagonell Drayer notifications@github.com Sent: Friday, August 7, 2020 5:03 PM To: dotnet/aspnetcore aspnetcore@noreply.github.com Cc: kishorpise kpise@hotmail.com; Author author@noreply.github.com Subject: Re: [dotnet/aspnetcore] Google Auth- Asp.net core 3.1 (#18858)

I have the same problem and I cannot find the solution, in google I found that the problem is due to chrome, see the following photo:

[Error_Google]https://user-images.githubusercontent.com/15655547/89688193-f6fa1280-d901-11ea-9fb0-d1681c20fa19.png

Does someone know how to solve this problem?

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHubhttps://github.com/dotnet/aspnetcore/issues/18858#issuecomment-670712000, or unsubscribehttps://github.com/notifications/unsubscribe-auth/ALRUELYSK3U5OGNQYUERANDR7RTY3ANCNFSM4KRBYBZQ.

Tratcher commented 4 years ago

I have the same problem and I cannot find the solution, in google I found that the problem is due to chrome, see the following photo:

Error_Google

Does someone know how to solve this problem?

You must access the site using https.

lagonell commented 4 years ago

Accessing with https does not solve the problem.

Error_Google_https

Tratcher commented 4 years ago

Can you share a fiddler trace file and your Startup config?

lagonell commented 4 years ago

I hope it can help

145_Full.txt

` public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddSeo(Configuration);

        services.AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlServer(
                Configuration.GetConnectionString("DefaultConnection")));

        services.AddIdentity<ApplicationUser, IdentityRole>(
            o =>
            {
                o.Password.RequireDigit = false;
                o.Password.RequireLowercase = false;
                o.Password.RequireNonAlphanumeric = false;
                o.User.RequireUniqueEmail = true;

            })
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddErrorDescriber<SpanishIdentityErrorDescriber>()
            .AddDefaultTokenProviders();

        services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
            .AddCookie(options =>
            {
                options.Cookie.SameSite = SameSiteMode.None;
                options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
                options.Cookie.IsEssential = true;
            })
            .AddGoogle(googleOptions =>
            {
                googleOptions.ClientId = Configuration["Authentication:Google:ClientId"];
                googleOptions.ClientSecret = Configuration["Authentication:Google:ClientSecret"];
                googleOptions.Events.OnRemoteFailure = (context) =>
                {
                    var error = context.Failure.Message;
                    return Task.CompletedTask;
                };
            });

        services.AddDistributedMemoryCache();

        services.AddSession(options =>
        {
            options.Cookie.Name = ".QuinielaVirtual.Session";
            // Set a short timeout for easy testing.
            options.IdleTimeout = TimeSpan.FromSeconds(10);
            options.Cookie.HttpOnly = true;
            // Make the session cookie essential
            options.Cookie.IsEssential = true;
        });

        services.AddHttpClient<QuinielaVirtualService>();

        // Adding MediatR for Domain Events and Notifications
        //services.AddMediatR(typeof(Startup));

        services.AddTransient<IEmailSender, EmailSender>();
        services.AddTransient<ICipherService, CipherService>();
        services.AddTransient<IContentEmail, ContentEmail>();
        services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

        services.Configure<EmailSettings>(Configuration.GetSection("EmailSettings"));

        services.AddAntiforgery(o => o.HeaderName = "XSRF-TOKEN");

        services.AddRazorPages();

        services.AddLanguageSetup();

        services.AddSignalR();

        services.AddDataProtection()
            .UseCryptographicAlgorithms(
                new AuthenticatedEncryptorConfiguration()
                {
                    EncryptionAlgorithm = EncryptionAlgorithm.AES_256_CBC,
                    ValidationAlgorithm = ValidationAlgorithm.HMACSHA256
                });
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory, IWebHostEnvironment env)
    {
        app.AddCustomExceptionHandlingPipeline(loggerFactory, env);
        app.UseHttpsRedirection();

        FileExtensionContentTypeProvider contentTypes2 = new FileExtensionContentTypeProvider(new Dictionary<string, string>()
        {
            [".apk"] = "application/vnd.android.package-archive"
        });

        FileExtensionContentTypeProvider provider = new FileExtensionContentTypeProvider();
        provider.Mappings.Add(".apk", "application/vnd.android.package-archive");

        app.UseStaticFiles(new StaticFileOptions
        {
            ContentTypeProvider = provider,
            OnPrepareResponse = ctx =>
           {
               const int durationInSeconds = 60 * 60 * 24 * 365;
               ctx.Context.Response.Headers[Microsoft.Net.Http.Headers.HeaderNames.CacheControl] =
                   "public,max-age=" + durationInSeconds;
           }
        });
        app.UseSession();
        app.UseCookiePolicy();
        app.UseRouting();
        app.UseAuthentication();
        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapRazorPages();
            endpoints.MapHub<Services.Chat>("/default");
            endpoints.MapHub<HubTChat>("/hubTChat");
        });
    }
}`
Tratcher commented 4 years ago

The client isn't sending back the correlation cookie. Can you also share the fiddler trace for the prior request so we can see the cookies the server sent to the client?

lagonell commented 4 years ago

The truth is that I do not understand, what does that have to do with that I deactivate a flag from chrome and everything works correctly.

48_Full.txt

It is the same code deployed on the server.

This is the flag that you disabled and everything works: Cookies without SameSite must be secure If enabled, cookies without SameSite restrictions must also be Secure. If a cookie without SameSite restrictions is set without the Secure attribute, it will be rejected. This flag only has an effect if "SameSite by default cookies" is also enabled. – Mac, Windows, Linux, Chrome OS, Android

cookies-without-same-site-must-be-secure

Tratcher commented 4 years ago

Sorry, you need to go back one further. We need to see the request that redirected from android.quinielavirtual.com to accounts.google.com.

lagonell commented 4 years ago

Forgive me, here it is:

12_Full.txt

lagonell commented 4 years ago

This is from has the call from android.quinielavirtual.com and the following from google.

12_Full.txt

Tratcher commented 4 years ago

Set-Cookie: .AspNetCore.Correlation.Google.0HDdxeOq92yhxAG1ddlI2SAL--ggdgWZeL-6mxjh_0o=N; expires=Wed, 12 Aug 2020 21:01:02 GMT; path=/signin-google; samesite=none; httponly

The cookie is marked as samesite=none but doesn't have the secure attribute.

The redirect_uri=http%3A%2F%2Fandroid.quinielavirtual.com%2Fsignin-google is also using http. You must be using a TLS terminating proxy?

See our proxy docs https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer?view=aspnetcore-3.1. That will get your request scheme fixed up so your redirect_uri will be correct and your cookies will get the secure attribute.

lagonell commented 4 years ago

Man, you're a crack, it works.

Works

I am logged in. :)

kishorpise commented 4 years ago

Hey .. Let me see .. I ca n see lot many emails… Just collecting it.

From: Edgar Lagonell Drayer notifications@github.com Sent: Wednesday, August 12, 2020 5:46 PM To: dotnet/aspnetcore aspnetcore@noreply.github.com Cc: kishorpise kpise@hotmail.com; Author author@noreply.github.com Subject: Re: [dotnet/aspnetcore] Google Auth- Asp.net core 3.1 (#18858)

Man, you're a crack, it works.

[Works]https://user-images.githubusercontent.com/15655547/90071407-ca753a80-dcf5-11ea-98c9-80dc8eae971c.png

I am logged in. :)

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHubhttps://github.com/dotnet/aspnetcore/issues/18858#issuecomment-673126571, or unsubscribehttps://github.com/notifications/unsubscribe-auth/ALRUEL5SSG4CY2NQG565JWDSAMEQXANCNFSM4KRBYBZQ.

ghost commented 3 years ago

Thank you for contacting us. Due to a lack of activity on this discussion issue we're closing it in an effort to keep our backlog clean. If you believe there is a concern related to the ASP.NET Core framework, which hasn't been addressed yet, please file a new issue.

This issue will be locked after 30 more days of inactivity. If you still wish to discuss this subject after then, please create a new issue!