解析/验证golang中AzureAD的JWT令牌



我用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在这里列出的公钥中随机挑选kid声明https://login.microsoftonline.com/common/discovery/v2.0/keys所以我迷路了,因为这不起作用。

以前有人这样做过吗?或者有什么建议吗?

位于https://login.microsoftonline.com/common/discovery/v2.0/keys的资产被称为JWKS、JSON Web密钥集。如果您只想验证此服务签名的令牌,则可以使用类似于以下代码片段的内容。我已经为这个用例编写了一个包:github.com/MicahParks/keyfunc

在引擎盖下,这个包将读取和解析JWKS中找到的加密密钥,然后根据它们的密钥IDkid将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.KeyfuncnError: %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()
}

令人烦恼的是,这是一个Azure AD配置问题,开箱即用,它将为MS Graph生成JWT令牌,整个身份验证过程成功,但当您尝试验证令牌时,由于某种原因失败了。一旦你为你的应用程序正确设置了Azure AD并具有正确的作用域,它就会正确验证。我在博客上写了一些细节——https://blog.jonathanchannon.com/2022-01-29-azuread-golang/

最新更新