在Asp.Net Identity中设置redirect_uri

doo*_*man 14 asp.net-mvc facebook asp.net-mvc-5 asp.net-identity

我正在尝试使用Asp.Net Identity为facebook登录设置redirect_uri.但是,只有当redirect_uri为'/'时,才会触发GetExternalLoginREST中的REST方法AccountController.如果我添加其他任何不触发的内容GetExternalLogin,浏览器只会显示错误:invalid_request.

但是,url包含重定向参数,例如,如果我将redirect_uri添加为 http://localhost:25432/testing

响应URL如下所示:

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
Run Code Online (Sandbox Code Playgroud)

并且浏览器窗口显示:error: invalid_request
任何想法为什么只有在重定向到'/'而不是任何其他url时才有效?

mar*_*are 23

对于可能遇到此问题的任何其他人:问题是当您ApplicationOAuthProvider.cs从Visual Studio SPA模板中获取(复制)时,该代码位于以下位置:

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);
}
Run Code Online (Sandbox Code Playgroud)

这显然会阻止任何redirect_uri不样子http://localhost/还是http://example.com/那么例如http://example.com/home将无法正常工作.

现在,下面是InvokeAuthorizeEndpointAsyncKatana 的源代码,它完成了所有工作,您可以看到它调用任何OAuthProvider可能为此MVC/Web API应用程序注册的自定义(此注册通常发生在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;
}
Run Code Online (Sandbox Code Playgroud)

这是关键:

 await Options.Provider.ValidateClientRedirectUri(clientContext);
Run Code Online (Sandbox Code Playgroud)

因此,您的解决方案是更改ValidateClientRedirectUri执行验证的方式 - 正如您所见,默认的SPA实现非常幼稚.

有很多人对SPA有问题,主要是因为它缺乏任何有用的信息,我的意思是ASP.NET身份和OWIN的内容以及KnockoutJS实现中发生的事情.

我希望微软能够为这些模板提供更全面的文档,因为任何试图做更复杂的事情的人都会遇到问题.

我花了好几个小时,挖掘OWIN(Katana)源代码,认为上面的实现阻止了我的重定向URI,但它不是,希望也可以帮助其他人.

HTH


小智 4

问题是GetExternalLogin注册为OAuthOptions.AuthorizeEndpointPathwhich 用于app.UseOAuthBearerTokens(OAuthOptions). 此配置对端点参数进行验证。

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
}
Run Code Online (Sandbox Code Playgroud)

并且您应该传递“授权端点请求缺少所需的response_type参数”和“授权端点请求包含不支持的response_type参数”