【问题标题】:Parse/Validate JWT token from AzureAD in golang在 golang 中解析/验证来自 AzureAD 的 JWT 令牌
【发布时间】:2022-02-19 23:19:24
【问题描述】:

我使用 OAuth2 设置了 Azure AD,并让它为我的 Web 应用程序发出 JWT。在后续请求中,我想验证发出的 JWT。我正在使用github.com/dgrijalva/jwt-go 这样做,但它总是失败。

token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
    if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
        return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
    }
    return []byte("bW8ZcMjBCnJZS-ibX5UQDNStvx4"), nil
})
if err != nil {
    return nil, err
}

我从 MS 在此处列出的公钥 https://login.microsoftonline.com/common/discovery/v2.0/keys 中随机选择 kid 声明,所以我迷路了,因为这不起作用。

以前有没有人这样做过或者有什么指点?

【问题讨论】:

  • 您的 keyfunc 正在返回 kid(密钥 ID),而您需要将密钥(在 JWK 文件中的 x5c 节点中)作为 *rsa.PublicKey 返回。 This example 可能会有所帮助。我建议使用new repothese libraries(包括解析您链接的JWK 文件的代码)。
  • 谢谢,我似乎到了某个地方,但是当它创建 PublicKey 时,库现在出现“crypto/rsa:验证错误”错误
  • 抱歉 - 您需要使用您现在使用的代码更新您的问题,然后才能提供进一步的帮助。

标签: go azure-active-directory


【解决方案1】:

您正在使用的存储库不再按照自述文件中的说明进行维护。

我一直在使用它的官方替代品https://github.com/golang-jwt/jwt,我从未遇到任何问题。你应该试试看。

【讨论】:

  • 谢谢,目前正在尝试让 github.com/coreos/go-oidc/v3/oidc 也能正常工作
  • 但是我认为我的主要问题是 JWT 令牌和它使用的签名密钥,lib 可能已经过时但它通常应该可以工作,我认为 AzureAD 有一些特别之处
  • 输入token是如何生成的?你确定它是用 RSA 签名的吗?
【解决方案2】:

令人讨厌的是,这是一个 Azure AD 配置问题,开箱即用,它会为 MS Graph 生成一个 JWT 令牌,并且整个身份验证过程会成功,但是当您尝试验证令牌时,由于某种原因它会失败。为您的应用正确设置 Azure AD 并使用正确的范围后,它会正确验证。我在这里写了关于细节的博客 - https://blog.jonathanchannon.com/2022-01-29-azuread-golang/

【讨论】:

    【解决方案3】:

    位于https://login.microsoftonline.com/common/discovery/v2.0/keys 的资产是所谓的 JWKS,JSON Web 密钥集。如果您只想验证由该服务签名的令牌,您可以使用类似于以下代码 sn-p 的内容。我为这个用例编写了一个包:github.com/MicahParks/keyfunc

    在后台,此包将读取并解析在 JWKS 中找到的加密密钥,然后根据其密钥 ID kid 将 JWT 与这些密钥相关联。它还有一些关于自动刷新远程 JWKS 资源的逻辑。

    package main
    
    import (
        "context"
        "log"
        "time"
    
        "github.com/golang-jwt/jwt/v4"
    
        "github.com/MicahParks/keyfunc"
    )
    
    func main() {
    
        // Get the JWKS URL.
        jwksURL := "https://login.microsoftonline.com/common/discovery/v2.0/keys"
    
        // Create a context that, when cancelled, ends the JWKS background refresh goroutine.
        ctx, cancel := context.WithCancel(context.Background())
    
        // Create the keyfunc options. Use an error handler that logs. Refresh the JWKS when a JWT signed by an unknown KID
        // is found or at the specified interval. Rate limit these refreshes. Timeout the initial JWKS refresh request after
        // 10 seconds. This timeout is also used to create the initial context.Context for keyfunc.Get.
        options := keyfunc.Options{
            Ctx: ctx,
            RefreshErrorHandler: func(err error) {
                log.Printf("There was an error with the jwt.Keyfunc\nError: %s", err.Error())
            },
            RefreshInterval:   time.Hour,
            RefreshRateLimit:  time.Minute * 5,
            RefreshTimeout:    time.Second * 10,
            RefreshUnknownKID: true,
        }
    
        // Create the JWKS from the resource at the given URL.
        jwks, err := keyfunc.Get(jwksURL, options)
        if err != nil {
            log.Fatalf("Failed to create JWKS from resource at the given URL.\nError: %s", err.Error())
        }
    
        // Get a JWT to parse.
        //
        // This wasn't signed by Azure AD.
        jwtB64 := "eyJraWQiOiJlZThkNjI2ZCIsInR5cCI6IkpXVCIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiJXZWlkb25nIiwiYXVkIjoiVGFzaHVhbiIsImlzcyI6Imp3a3Mtc2VydmljZS5hcHBzcG90LmNvbSIsImlhdCI6MTYzMTM2OTk1NSwianRpIjoiNDY2M2E5MTAtZWU2MC00NzcwLTgxNjktY2I3NDdiMDljZjU0In0.LwD65d5h6U_2Xco81EClMa_1WIW4xXZl8o4b7WzY_7OgPD2tNlByxvGDzP7bKYA9Gj--1mi4Q4li4CAnKJkaHRYB17baC0H5P9lKMPuA6AnChTzLafY6yf-YadA7DmakCtIl7FNcFQQL2DXmh6gS9J6TluFoCIXj83MqETbDWpL28o3XAD_05UP8VLQzH2XzyqWKi97mOuvz-GsDp9mhBYQUgN3csNXt2v2l-bUPWe19SftNej0cxddyGu06tXUtaS6K0oe0TTbaqc3hmfEiu5G0J8U6ztTUMwXkBvaknE640NPgMQJqBaey0E4u0txYgyvMvvxfwtcOrDRYqYPBnA"
    
        // Parse the JWT.
        var token *jwt.Token
        if token, err = jwt.Parse(jwtB64, jwks.Keyfunc); err != nil {
            log.Fatalf("Failed to parse the JWT.\nError: %s", err.Error())
        }
    
        // Check if the token is valid.
        if !token.Valid {
            log.Fatalf("The token is not valid.")
        }
        log.Println("The token is valid.")
    
        // End the background refresh goroutine when it's no longer needed.
        cancel()
    
        // This will be ineffectual because the line above this canceled the parent context.Context.
        // This method call is idempotent similar to context.CancelFunc.
        jwks.EndBackground()
    }
    

    【讨论】:

      猜你喜欢
      • 2020-10-12
      • 2022-09-25
      • 2020-11-21
      • 2020-09-13
      • 1970-01-01
      • 1970-01-01
      • 2019-11-16
      • 2019-06-02
      • 2019-06-23
      相关资源
      最近更新 更多