4

I'm trying to use ASP.NET middleware to authenticate using Google OAuth. I understand the problem I am getting is due to CORS issues but I cannot seem to resolve them.

My Startup class config is as follows:

public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
         {
             options.AddPolicy("CorsPolicy",
                builder => builder
                .AllowAnyHeader()
                .AllowAnyMethod()
                .AllowAnyOrigin()
                .AllowCredentials()
                );
      ......
       services.AddGoogle(o =>
            {
                o.ClientId = Configuration["Authentication:Google:ClientId"];
                o.ClientSecret = Configuration["Authentication:Google:ClientSecret"];
                o.AuthorizationEndpoint += "?prompt=consent"; // Hack so we always get a refresh token, it only comes on the first authorization response
                o.AccessType = "offline";
                o.SaveTokens = true;
                o.Events = new OAuthEvents()
                {
                    OnRemoteFailure = ctx =>
                        {
                            ctx.Response.Redirect("/error?FailureMessage=" + UrlEncoder.Default.Encode(ctx.Failure.Message));
                            ctx.HandleResponse();
                            return Task.FromResult(0);
                        }
                };
                o.ClaimActions.MapJsonSubKey("urn:google:image", "image", "url");
                o.ClaimActions.Remove(ClaimTypes.GivenName);
            });
...........
 public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        loggerFactory.AddConsole();

        //if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseCors("CorsPolicy");

        app.Use(async (context, next) =>
            {
                await next();
                // Serve index file and allow Angular to take over routing if (NotFound)
                if (context.Response.StatusCode == 404 && !Path.HasExtension(context.Request.Path.Value))
                {
                    context.Request.Path = "/index.html";
                    await next();
                }

            });

        app.UseAuthentication();

        app.UseDefaultFiles();
        app.UseStaticFiles();

        app.UseMvc();
    }

In my Auth Controller:

// POST: api/auth/ExternalLogin
    [HttpPost("loginexternal")]
    [AllowAnonymous]
    public async Task<IActionResult> LoginExternal([FromBody]string provider)
    {
        // Clear the existing external cookie to ensure a clean login process
        await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);

        // Request a redirect to the external login provider to link a login for the current user
        var redirectUrl = Url.Action(nameof(ExternalLoginCallback));
        var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl, _userManager.GetUserId(User));
        return new ChallengeResult(provider, properties);
    }

My typescript angular code which calls this function:

 loginExternal() {

    const headers = new HttpHeaders({ 'Content-Type': 'application/json', 'Accept': 'application/json' });


    return this.http.post(this.baseUrl + '/auth/loginexternal', '"Google"', { headers: headers })
        .map((res: any) => {
            localStorage.setItem('auth_token', res.auth_token);
            this.loggedIn = true;
            this._authNavStatusSource.next(true);
            return true;
        })
        .catch(this.handleError);
}

And this is the response

405 response

The above response occures after the ChallengeResult is executed in my LoginExternal action.

5
  • Your code’s trying to post JSON data cross-origin to https://accounts.google.com/o/oauth2/auth, which causes your browser to first send a CORS preflight OPTIONS request to https://accounts.google.com/o/oauth2/auth to ask if it’s OK with getting that cross-origin POST. And https://accounts.google.com/o/oauth2/auth is responding to tell the browser, Nope. So the browser stops right there and never tries your POST request. … Anyway, you can’t “resolve” this—your code is trying to do something that https://accounts.google.com explicitly doesn’t want you doing. Commented Sep 9, 2017 at 17:41
  • ASP.NET middleware is performing that request via the controller though isn't it?. My post method only goes to my controller. Commented Sep 9, 2017 at 18:28
  • No, your frontend code is what’s trying to perform that POST request. If it were instead the ASP.NET middleware, it would just work—because ASP.NET middleware doesn’t enforce the same-origin policy and enforce cross-origin restrictions. Browsers do. That’s why it’s your browser that’s showing you that CORS error message. Commented Sep 9, 2017 at 18:34
  • Then why does this code work when using the default ASP.NET MVC template and views where the auth code was pulled from? Sorry I'm trying to find how to fix it... When stepping through the debugger the post method hits my controller the error only occurs after the middleware ChellengeResult method executes. Commented Sep 9, 2017 at 18:46
  • 1
    Any update on this? I have got the same issue. Commented Dec 18, 2018 at 4:58

1 Answer 1

2

Try to use this.document.location.href or even window.location.href to redirect to your google authentication page instead of making http request to your .net core controller action.

@Injectable()
export class LoginService {
    //...

    constructor(@Inject(DOCUMENT) private document: Document,...)

    login() {
        this.document.location.href = 'https://www.mywebsite.com/account/signInWithGoogle';
    }
}

Here’s how it looks like in a controller action:

public class AccountController : Controller
{
    private readonly SignInManager<IdentityUser> _signInManager;
    private readonly UserManager<IdentityUser> _userManager;
    public AccountController(SignInManager<IdentityUser> signInManager, UserManager<IdentityUser> userManager)
    {
        _signInManager = signInManager;
        _userManager = userManager;
    }

    public IActionResult SignInWithGoogle()
    {
        var authenticationProperties = _signInManager.ConfigureExternalAuthenticationProperties("Google", Url.Action(nameof(HandleExternalLogin)));
        return Challenge(authenticationProperties, "Google");
    }

    ...

Guide: https://www.blinkingcaret.com/2018/10/10/sign-in-with-an-external-login-provider-in-an-angular-application-served-by-asp-net-core/

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.