aspnet / AspNetKatana

Microsoft's OWIN implementation, the Katana project
Apache License 2.0
963 stars 332 forks source link

Callback's question mark is escaped in GoogleOAuth2AuthenticationHandler. #403

Closed SeminDM closed 3 years ago

SeminDM commented 3 years ago

Hello! I registered this redirect URI https://localhost:44315/Home/FederationLogin?channel=channel.2 in Google Dev Console. I added Google middleware:

Startup.cs
var callbackPath = "/Home/FederationLogin?channel=channel.2";
var options = new GoogleOAuth2AuthenticationOptions
{
    ...
    CallbackPath = new PathString(callbackPath)
};

and this method:

HomeController.cs
public void FederationLogin(string channel)
{
}

When GoogleOAuth2AuthenticationHandler builds redirect URI it doesn't use CallbackPath.Value. In result redirect Uri has this value: https://localhost:44315/Home/FederationLogin%3Fchannel=channel.2 and this error Error400: redirect_uri_mismatch The redirect URI in the request, https://localhost:44315/Home/FederationLogin%3Fchannel=channel.2, does not match the ones authorized for the OAuth client. To update the authorized redirect URIs, visit: https://console.developers.google.com/apis/credentials/oauthclient/${your_client_id}?project=${your_project_number}

Did I configure something wrong? Could I workaround this error?

Thanks!

Tratcher commented 3 years ago

The CallbackPath property does not support query strings. What is this query parameter for?

Remember that the CallbackPath must not refer to a controller in your application, it refers to a path handled exclusively by the google auth middleware. That's why the default value is "/signin-google". The request is redirected to your return url specified in the original challenge after the initial processing completes.

SeminDM commented 3 years ago

@Tratcher thank you for reply. You raised a issue that I want to better understand.

My app has two common ways to authenticate accounts: built-in authentication for local accounts (app manages password's storing and validation) and ldap authentication for accounts imported from AD. Also app makes a possibility to configure auth channels for federated authentication. Auth channel may have different Identity Provider (AD FS, AzureAD, Google, Okta) and different federation Protocol (OpenId Connect, WS-Federation). It means what number of channels may vary.

In Startup I get all configured auth channels and use appropriate middleware. In HomeController I have three methods for all channels (these methods I add as redirect URI in Developer Console).

// 1. Method for auth challenge. I call it from login page. 
public voidFederationLoginChallenge(string channel)
{
    HttpContext.GetOwinContext().Authentication.Challenge(channel);
}

// 2. Method which used as login redirect URI.
public void FederationLogin(string channel)
{
    if (Request.IsAuthenticated)
    {
        var claimsIdentity = User.Identity as ClaimsIdentity;
    }
}

// 3. Method which used as logout redirect URI.
public ActionResult FederationLogout(string channel)
{
}

Parameter "channel" is an auth channel id. It is unique and is used as value for AthenticationType in AuthenticationOptions. I use URI "host/Home/FederationLogin?channel={channel_id}" as value for WsFederationAuthenticationOptions.Wreply or OpenIdConnectAuthenticationOptions.RedirectUri or GoogleOAuth2AuthenticationOptions.CallbackPath. This parameter I use to understand which Idp sends request to "Home/FederationLogin". And I don't set AuthenticationProperties.RedirectUri before challenge beacouse I don't unanderstand how this value carrelates with Wreply in middleware's options?

HttpContext.GetOwinContext().Authentication.Challenge(new AuthenticationProperties { RedirectUri = "?" }, channel);

Also I don't completle understand how to redirect user to page which he/she tried to get before authentication?

Tratcher commented 3 years ago

In Startup I get all configured auth channels and use appropriate middleware.

What does this look like?

SeminDM commented 3 years ago

@Tratcher I've read carefully your answer on my old question another time and understood it clearly at last. Wreply/CallbackPath should be unique but not refer to a controller method. I use this interpolated string "/signin-{0}" and place channel's id in it and final value I set as Wreply/CallbackPath. All works correctly. Thank you very much!