我正在尝试使用authorization_code
授权流程设置我的身份验证。之前我曾与grant_type=password
合作,所以我知道这些东西应该如何工作。但是在使用grant_type=authorization_code
时,除了invalid_grant
这是我的设置:
app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/auth/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(5),
Provider = new SampleAuthProvider()
});
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions
{
AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Active,
AuthenticationType = "Bearer"
});
SampleAuthProvider是以下类:https://gist.github.com/anonymous/8a0079b705423b406c00
基本上,它只是记录每一步并验证它。我试过了这个请求:
POST http://localhost:12345/auth/token
grant_type=authorization_code&code=xxxxxx&client_id=xxxxx&redirect_uri=https://xxxx.com/
Content-Type: application/x-www-form-urlencoded
它正在经历:
OnMatchEndpoint
OnValidateClientAuthentication
就是这样。我希望下次拨打OnValidateTokenRequest
和OnGrantAuthorizationCode
,但事实并非如此。我不知道为什么。
请求中的xxxx
不是占位符,我是这样试过的。也许中间件会自行进行一些检查并因此拒绝请求?我尝试使用redirect_uri
http
的变体,没有任何协议,没有尾随斜杠......
它也适用于自定义grant_type
。因此,如果我太绝望了,我想我可以用它来模拟authorization_code
,但我宁愿不必这样做。
TL; DR
我的OAuthAuthorizationServerProvider
在使用{"error":"invalid_grant"}
后OnValidateClientAuthentication
后返回grant_type=authorization_code
。
感谢您的帮助!
修改
正如RajeshKannan所指出的,我在配置上犯了一个错误。我没有提供AuthorizationCodeProvider
实例。但是,这并没有完全解决问题,因为在我的情况下,代码不是由AuthorizationCodeProvider
发出的,我不能只是反序列化它。我接受了工作的解决方法。
答案 0 :(得分:9)
这是我的工作。我对这个解决方案并不完全满意,但它有效,应该帮助其他人解决问题。
所以,问题是我没有设置AuthorizationCodeProvider
属性。收到grant_type=authorization_code
的请求时,代码必须由该代码提供程序验证。该框架假定代码是由该代码提供者发布的,但这不是我的情况。我从另一台服务器获取它,并且必须将代码发送回它以进行验证。
在标准情况下,您也是发布代码的人,RajeshKannan提供的链接描述了您必须做的一切。
您必须在此处设置属性:
app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString(Paths.TokenPath),
Provider = new SampleAuthProvider(),
AuthorizationCodeProvider = new MyAuthorizationCodeProvider ()
}
MyAuthorizationCodeProvider
类的声明:
internal class MyAuthorizationCodeProvider : AuthenticationTokenProvider
{
public override async Task ReceiveAsync(
AuthenticationTokenReceiveContext context)
{
object form;
// Definitely doesn't feel right
context.OwinContext.Environment.TryGetValue(
"Microsoft.Owin.Form#collection", out form);
var redirectUris = (form as FormCollection).GetValues("redirect_uri");
var clientIds = (form as FormCollection).GetValues("client_id");
if (redirectUris != null && clientIds != null)
{
// Queries the external server to validate the token
string username = await MySsoService.GetUserName(context.Token,
redirectUris[0]);
if (!string.IsNullOrEmpty(username))
{
var identity = new ClaimsIdentity(new List<Claim>()
{
// I need the username in GrantAuthorizationCode
new Claim(ClaimTypes.NameIdentifier, username)
}, DefaultAuthenticationTypes.ExternalBearer);
var authProps = new AuthenticationProperties();
// Required. The request is rejected if it's not provided
authProps.Dictionary.Add("client_id", clientIds[0]);
// Required, must be in the future
authProps.ExpiresUtc = DateTimeOffset.Now.AddMinutes(1);
var ticket = new AuthenticationTicket(identity, authProps);
context.SetTicket(ticket);
}
}
}
}
答案 1 :(得分:1)
确保您已配置授权服务器选项。 我认为你应该提供你的授权终点细节:
AuthorizeEndpointPath = new PathString(Paths.AuthorizePath)
在下面的链接中,将详细解释授权代码授权,并列出授权代码授予生命周期中涉及的方法。
答案 2 :(得分:1)
我有同样的错误。我失踪的事情:
OAuthAuthorizationServerOptions.AuthorizationCodeProvider
client_id
,就像收到authorization_code
时一样。OAuthAuthorizationServerProvider.ValidateClientAuthentication
,在此方法中调用context.TryGetFormCredentials
。这会将属性context.ClientId
设置为client_id
GET参数中的值。此属性must be set,否则您将收到invalid_grant
错误。另外,请致电context.Validated()
。完成上述所有操作后,我终于可以在令牌端点将authorization_code
交换为access_token
。
答案 3 :(得分:0)
感谢方案,我的代码缺少以下两个必需值。发布在这里以防其他人发现它有用:
// Required. The request is rejected if it's not provided
authProps.Dictionary.Add("client_id", clientIds[0]);
// Required, must be in the future
authProps.ExpiresUtc = DateTimeOffset.Now.AddMinutes(1);
答案 4 :(得分:0)
我用以下最简单的例子解决了这个问题,并想分享一下。希望有人觉得它有帮助。
--
中间件似乎会检查键 redirect_uri
是否存在于 AuthenticationProperties
的字典中,将其删除,一切正常(使用经过验证的上下文)。
AuthorizationCodeProvider
的简化示例如下所示:
public class AuthorizationCodeProvider:AuthenticationTokenProvider {
public override void Create(AuthenticationTokenCreateContext context) {
context.SetToken(context.SerializeTicket());
}
public override void Receive(AuthenticationTokenReceiveContext context) {
context.DeserializeTicket(context.Token);
context.Ticket.Properties.Dictionary.Remove("redirect_uri"); // <-
}
}
并且不要忘记在重写的方法 OAuthAuthorizationServerProvider.ValidateClientAuthentication
中验证上下文。同样,这是一个从模板项目的 ApplicationOAuthProvider
类继承的简化示例:
public partial class DefaultOAuthProvider:ApplicationOAuthProvider {
public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context) {
if(null!=context.RedirectUri) {
context.Validated(context.RedirectUri);
return Task.CompletedTask;
}
return base.ValidateClientRedirectUri(context);
}
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) {
if(context.TryGetFormCredentials(out String clientId, out String clientSecret)) {
// Specify the actual expected client id and secret in your case
if(("expected-clientId"==clientId)&&("expected-clientSecret"==clientSecret)) {
context.Validated(); // <-
return Task.CompletedTask;
}
}
return base.ValidateClientAuthentication(context);
}
public DefaultOAuthProvider(String publicClientId) : base(publicClientId) {
}
}
请注意,如果您使用特定的客户端 ID 调用 context.Validated
,则必须将相同的 client_id 放入票证的属性中,您可以使用方法 AuthenticationTokenProvider.Receive