I am trying to set the redirect_uri for the facebook login with Asp.Net Identity. However, the GetExternalLogin
REST method in the AccountController
is only triggered if the redirect_uri is '/'. If I add anything else it does not trigger GetExternalLogin
, the browser only shows error: invalid_request.
However the url contains the redirected parameter as it should e.g. if I add the redirect_uri as http://localhost:25432/testing
the response URL looks like this:
http://localhost:25432/api/Account/ExternalLogin?provider=Facebook&response_type=token&client_id=self&redirect_uri=http%3A%2F%2Flocalhost%3A25432%2Ftesting&state=0NctHHGq_aiazEurHYbvJT8hDgl0GJ_GGSdFfq2z5SA1
and the browser window shows: error: invalid_request
Any idea why this works only when redirecting to '/' but not to any other url´s?
The problem is that GetExternalLogin
registered as OAuthOptions.AuthorizeEndpointPath
which used for app.UseOAuthBearerTokens(OAuthOptions)
. This configuration puts validation on arguments of endpoint.
if (!Uri.TryCreate(authorizeRequest.RedirectUri, UriKind.Absolute, out validatingUri))
{
// The redirection endpoint URI MUST be an absolute URI
}
else if (!String.IsNullOrEmpty(validatingUri.Fragment))
{
// The endpoint URI MUST NOT include a fragment component.
}
else if (!Options.AllowInsecureHttp &&
String.Equals(validatingUri.Scheme, Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase))
{
// The redirection endpoint SHOULD require the use of TLS
}
And you should pass "Authorize endpoint request missing required response_type parameter" and "Authorize endpoint request contains unsupported response_type parameter"
For anyone else that might run into this issue: the problem is when you take (copy) the ApplicationOAuthProvider.cs
from the Visual Studio SPA template and it is there where this code is:
public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
{
if (context.ClientId == _publicClientId)
{
var expectedRootUri = new Uri(context.Request.Uri, "/");
if (expectedRootUri.AbsoluteUri == context.RedirectUri)
{
context.Validated();
}
}
return Task.FromResult<object>(null);
}
This will obviously block any redirect_uri
that doesn't look like http://localhost/
or http://example.com/
so for instance http://example.com/home
won't work.
Now this below is the source for InvokeAuthorizeEndpointAsync
in Katana which does all the work and you can see it calls into any custom OAuthProvider
that might be registered for this MVC/Web API application (this registration typically happens in Startup.Auth.cs
):
private async Task<bool> InvokeAuthorizeEndpointAsync()
{
var authorizeRequest = new AuthorizeEndpointRequest(Request.Query);
var clientContext = new OAuthValidateClientRedirectUriContext(
Context,
Options,
authorizeRequest.ClientId,
authorizeRequest.RedirectUri);
if (!String.IsNullOrEmpty(authorizeRequest.RedirectUri))
{
bool acceptableUri = true;
Uri validatingUri;
if (!Uri.TryCreate(authorizeRequest.RedirectUri, UriKind.Absolute, out validatingUri))
{
// The redirection endpoint URI MUST be an absolute URI
// http://tools.ietf.org/html/rfc6749#section-3.1.2
acceptableUri = false;
}
else if (!String.IsNullOrEmpty(validatingUri.Fragment))
{
// The endpoint URI MUST NOT include a fragment component.
// http://tools.ietf.org/html/rfc6749#section-3.1.2
acceptableUri = false;
}
else if (!Options.AllowInsecureHttp &&
String.Equals(validatingUri.Scheme, Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase))
{
// The redirection endpoint SHOULD require the use of TLS
// http://tools.ietf.org/html/rfc6749#section-3.1.2.1
acceptableUri = false;
}
if (!acceptableUri)
{
clientContext.SetError(Constants.Errors.InvalidRequest);
return await SendErrorRedirectAsync(clientContext, clientContext);
}
}
await Options.Provider.ValidateClientRedirectUri(clientContext);
if (!clientContext.IsValidated)
{
_logger.WriteVerbose("Unable to validate client information");
return await SendErrorRedirectAsync(clientContext, clientContext);
}
var validatingContext = new OAuthValidateAuthorizeRequestContext(
Context,
Options,
authorizeRequest,
clientContext);
if (string.IsNullOrEmpty(authorizeRequest.ResponseType))
{
_logger.WriteVerbose("Authorize endpoint request missing required response_type parameter");
validatingContext.SetError(Constants.Errors.InvalidRequest);
}
else if (!authorizeRequest.IsAuthorizationCodeGrantType &&
!authorizeRequest.IsImplicitGrantType)
{
_logger.WriteVerbose("Authorize endpoint request contains unsupported response_type parameter");
validatingContext.SetError(Constants.Errors.UnsupportedResponseType);
}
else
{
await Options.Provider.ValidateAuthorizeRequest(validatingContext);
}
if (!validatingContext.IsValidated)
{
// an invalid request is not processed further
return await SendErrorRedirectAsync(clientContext, validatingContext);
}
_clientContext = clientContext;
_authorizeEndpointRequest = authorizeRequest;
var authorizeEndpointContext = new OAuthAuthorizeEndpointContext(Context, Options);
await Options.Provider.AuthorizeEndpoint(authorizeEndpointContext);
return authorizeEndpointContext.IsRequestCompleted;
}
This is key:
await Options.Provider.ValidateClientRedirectUri(clientContext);
So your solution is to change how the ValidateClientRedirectUri
performs the validation - the default SPA implementation is, as you can see, very naive.
There's lots of ppl having issues with SPA mainly because it lacks any kind of useful information and I mean that both for ASP.NET Identity and OWIN stuff and with regards to what is going on within KnockoutJS implementation.
I wish Microsoft would provide more comprehensive docs for these templates because anyone who will try to do anything a bit more complex will run into issues.
I've spent hours on this, digging into OWIN (Katana) source code thinking it is the above implementation that blocks my redirect URIs but it was not, hopefully helps someone else too.
HTH
Based on the other answers, I changed the Validation code in ApplicationOAuthProvider.cs to just ensure that the redirect uri is on the same domain like so:
public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
{
if (context.ClientId == _publicClientId)
{
Uri expectedRootUri = new Uri(context.Request.Uri, "/");
if (context.RedirectUri.StartsWith(expectedRootUri.AbsoluteUri))
{
context.Validated();
}
}
return Task.FromResult<object>(null);
}
来源:https://stackoverflow.com/questions/20693082/setting-the-redirect-uri-in-asp-net-identity