簡體   English   中英

Owin.Security.OAuth 上的 Authorization_code 授予流程:返回 invalid_grant

[英]Authorization_code grant flow on Owin.Security.OAuth: returns invalid_grant

我正在嘗試使用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

就這樣。 我希望它接下來調用OnValidateTokenRequestOnGrantAuthorizationCode ,但它沒有。 我不知道為什么。

請求中的xxxx不是占位符,我是這樣嘗試的。 也許中間件會自行進行一些檢查並因此拒絕請求? 我用http嘗試了redirect_uri變體,沒有任何協議,沒有尾部斜杠......

它也可以與自定義grant_type一起正常工作。 所以如果我太絕望了,我想我可以用它來模擬authorization_code ,但我寧願不必那樣做。

TL; 博士

當使用grant_type=authorization_code時,我的OAuthAuthorizationServerProviderOnValidateClientAuthentication之后返回{"error":"invalid_grant"}

  • 為什么會停在那里?
  • 我怎樣才能讓整個該死的事情奏效?

謝謝你的幫助!


編輯

正如 RajeshKannan 所指出的,我在配置中犯了一個錯誤。 我沒有提供AuthorizationCodeProvider實例。 但是,這並沒有完全解決問題,因為在我的情況下,代碼不是由AuthorizationCodeProvider發布的,我不能只是反序列化它。 我回答了我開始工作的解決方法。

這是我的工作。 我對該解決方案並不完全滿意,但它有效並且應該可以幫助其他人解決他們的問題。


所以,問題是我沒有設置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);
            }
        }
    }
}

我有同樣的錯誤。 我缺少的東西:

  • 根據文檔指定OAuthAuthorizationServerOptions.AuthorizationCodeProvider
  • 向令牌端點發出請求時,將相同的client_id指定為 GET 參數,就像您在收到authorization_code時所做的那樣。
  • 覆蓋OAuthAuthorizationServerProvider.ValidateClientAuthentication並在此方法中調用context.TryGetFormCredentials 這將屬性context.ClientId設置為來自client_id GET 參數的值。 必須設置此屬性,否則您將收到invalid_grant錯誤。 另外,調用context.Validated()

完成上述所有操作后,我終於可以將authorization_code交換為令牌端點處的access_token

謝謝方案,我的代碼缺少以下兩個必需值。 發布在這里以防其他人發現它有用:

            // 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); 

確保您已配置授權服務器選項。 我認為您應該提供您的授權端點詳細信息:

 AuthorizeEndpointPath = new PathString(Paths.AuthorizePath)

在下面的鏈接中,將詳細解釋授權碼授予,並列出了授權碼授予生命周期中涉及的方法。

Owin Oauth 授權服務器

我用以下最簡單的例子解決了這個問題,並想分享它。 希望有人覺得它有幫助。

——

中間件似乎會檢查AuthenticationProperties字典中是否存在 key redirect_uri ,將其刪除,一切正常(使用經過驗證的上下文)。

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方法來做到這一點

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM