我试图实现OAuth2 Authorization Code Grant Flow但是我遇到了一个问题,如果redirect_uri包含查询字符串参数且资源所有者必须进行身份验证,我的客户端会收到错误 .
客户端能够请求验证码,但是当它尝试交换访问令牌的代码时,授权服务器以HTTP状态“400 Bad Request”和以下响应正文进行响应 .
{"error":"invalid_grant"}
如果redirect_uri不包含查询字符串参数,或者如果客户端在请求授权代码之前将其删除,那么它将完美地运行 . 如果资源所有者已经使用授权服务器进行了身份验证,那么它也可以正常运行 .
我的客户端正在使用DotNetOpenAuth并使用Glimpse我可以看到redirect_uri在授权代码请求和访问令牌请求之间是一致的 .
Glimpse日志中的失败如下所示:
Prepared outgoing EndUserAuthorizationRequestC (2.0) message for http://localhost:61814/authorize:
client_id: localhost36618
redirect_uri: http://localhost:36618/login?redirectURL=%2FProfile
state: <state token>
scope: authentication
response_type: code
Processing incoming EndUserAuthorizationSuccessAuthCodeResponse (2.0) message:
code: <authorization code>
state: <state token>
redirectURL: /Profile
Prepared outgoing AccessTokenAuthorizationCodeRequestC (2.0) message for http://localhost:61814/token:
code: <authorization code>
redirect_uri: http://localhost:36618/login?redirectURL=%2FProfile
grant_type: authorization_code
http://localhost:61814/token returned 400 BadRequest: Bad Request
WebException from http://localhost:61814/token: {"error":"invalid_grant"}
但是,如果我从redirect_uri中省略了查询字符串参数,它可以工作:
Prepared outgoing EndUserAuthorizationRequestC (2.0) message for http://localhost:61814/authorize:
client_id: localhost36618
redirect_uri: http://localhost:36618/Login
state: <state token>
scope: authentication
response_type: code
Processing incoming EndUserAuthorizationSuccessAuthCodeResponse (2.0) message:
code: <authorization code>
state: <state token>
Prepared outgoing AccessTokenAuthorizationCodeRequestC (2.0) message for http://localhost:61814/token:
code: <authorization code>
redirect_uri: http://localhost:36618/Login
grant_type: authorization_code
Processing incoming AccessTokenSuccessResponse (2.0) message:
access_token: <access token>
token_type: bearer
expires_in: 3599
refresh_token: <refresh token>
同样,如果我在使用客户端之前登录授权服务器,它的工作原理如下:
Prepared outgoing EndUserAuthorizationRequestC (2.0) message for http://localhost:61814/authorize:
client_id: localhost36618
redirect_uri: http://localhost:36618/login?redirectURL=%2FProfile
state: <state token>
scope: authentication
response_type: code
Processing incoming EndUserAuthorizationSuccessAuthCodeResponse (2.0) message:
code: <authorization code>
state: <state token>
redirectURL: /Profile
Prepared outgoing AccessTokenAuthorizationCodeRequestC (2.0) message for http://localhost:61814/token:
code: <authorization code>
redirect_uri: http://localhost:36618/login?redirectURL=%2FProfile
grant_type: authorization_code
Processing incoming AccessTokenSuccessResponse (2.0) message:
access_token: <access token>
token_type: bearer
expires_in: 3599
refresh_token: <refresh token>
OWIN授权服务器的OAuthAuthorizationServerProvider在成功的访问令牌授权代码请求上执行以下方法:
-
Provider.OnMatchEndpoint
-
Provider.OnValidateClientAuthentication
-
AuthorizationCodeProvider.Receive
-
Provider.OnValidateTokenRequest
-
Provider.OnGrantAuthorizationCode
-
Provider.OnTokenEndpoint
-
AccessTokenProvider.OnCreate
-
RefreshTokenProvider.OnCreate
-
Provider.OnTokenEndpointResponse
但是,不成功的访问令牌授权代码请求仅触及以下方法:
-
Provider.OnMatchEndpoint
-
Provider.OnValidateClientAuthentication
-
AuthorizationCodeProvider.Receive
AuthorizationCodeProvider.OnReceive具有以下实现:
private void ReceiveAuthenticationCode(AuthenticationTokenReceiveContext context)
{
try
{
string ticket = _repo.RemoveTicket(context.Token);
if (!string.IsNullOrEmpty(ticket))
{
context.DeserializeTicket(ticket);
}
}
catch (Exception ex)
{
var wrapper = new Exception("Receive Authentication Code Error", ex);
Logger.Error(wrapper);
}
}
在调试器中,我可以看到一个有效的Token,从_repo成功检索序列化的Ticket,以及在方法完成之前的上下文对象中的反序列化的Ticket . 没有异常被抛出 . 成功和失败的请求之间的流程看起来相同,所以我不清楚什么是失败的,诊断工具事件日志在请求处理期间没有显示任何异常,只是在ReceiveAuthenticationCode完成后退出的线程 .
看起来问题出在我的测试客户端上,因为我能够使用Brent Shaffer's OAuth2 Demo PHP live demo重现完全相同的问题 .
测试客户端基于DotNetOpenAuth:
private static class Client
{
public const string Id = "username";
public const string Secret = "password";
}
private static class Paths
{
public const string AuthorizationServerBaseAddress = "http://localhost:61814";
public const string ResourceServerBaseAddress = "http://localhost:61814";
public const string AuthorizePath = "/authorize";
public const string TokenPath = "/token";
public const string ResourceServerApiMethodPath = "/getaccount";
}
public ActionResult Login(string code = "", string redirectURL = "/profile")
{
var authorizationServerUri = new Uri(Paths.AuthorizationServerBaseAddress);
var authorizationServer = new AuthorizationServerDescription
{
AuthorizationEndpoint = new Uri(authorizationServerUri, Paths.AuthorizePath),
TokenEndpoint = new Uri(authorizationServerUri, Paths.TokenPath)
};
var client = new WebServerClient(authorizationServer, Client.Id, Client.Secret);
if (!string.IsNullOrEmpty(code))
{
var apiResponse = null;
var authorizationState = client.ProcessUserAuthorization();
if (!string.IsNullOrEmpty(authorizationState?.AccessToken))
apiResponse = GetApiResponse(authorizationState, Paths.ResourceServerApiMethodPath);
if (apiResponse != null)
{
var identity = new ClaimsIdentity(new[] { new Claim("test", apiResponse),
new Claim(ClaimTypes.Role, "ResourceOwner")
}, "ApplicationCookie");
AuthMgr.SignIn(new AuthenticationProperties { IsPersistent = true }, identity);
return Redirect(redirectURL);
}
}
else
{
client.RequestUserAuthorization();
}
return View();
}
知道DotNetOpenAuth我可能做错了什么吗?为什么我在服务器端看不到无效请求?