英文:
AWS Cognito JWT verification using Go Fiber middleware (getting "key is of invalid type")
问题
当我尝试在中间件中验证基于Cognito的JWT时,我遇到了"key is of invalid type"的错误。目前,我在设置Fiber应用程序时设置了中间件,代码如下:
// 从本地读取我从AWS获取的 "jwks.json" 文件
signingKey, err := ioutil.ReadFile("./jwks.json")
if err != nil {
log.Fatal("Error when opening file: ", err)
}
// 在创建中间件时传入签名密钥
app.Get("/api", middleware.Protected(signingKey), handlers.ReadSomeData)
然后,我的中间件如下所示,其中大部分代码来自Go Fiber的JWT示例仓库:
func Protected(signingKey []byte) func(*fiber.Ctx) error {
return jwtware.New(jwtware.Config{
SigningKey: signingKey,
ErrorHandler: jwtError,
SigningMethod: "RS256",
})
}
func jwtError(c *fiber.Ctx, err error) error {
if err.Error() == "Missing or malformed JWT" {
c.Status(fiber.StatusBadRequest)
return c.JSON(fiber.Map{"status": "error", "message": err.Error(), "data": nil})
} else {
c.Status(fiber.StatusUnauthorized)
return c.JSON(fiber.Map{"status": "error", "message": err.Error(), "data": nil})
}
}
在得到答案后,我尝试使用"SigningKeys"参数,但出现了类型不匹配的问题,所以我最终通过以下方式读取了jwks json文件:
func Protected() func(*fiber.Ctx) error {
signingKey, err := os.ReadFile("./jwks.json")
if err != nil {
// 处理错误
}
x := make(map[string]interface{})
json.Unmarshal(signingKey, &x)
return jwtware.New(jwtware.Config{
SigningKeys: x,
ErrorHandler: jwtError,
SigningMethod: "RS256",
})
}
然而,现在我遇到了"Unexpected jwt key id=XXXXXXXXXXXX"的错误。
英文:
I am getting "key is of invalid type" when I try to verify a Cognito based JWT in my middleware. Currently I set up the middle ware like this when the Fiber app is being setup:
// read the "jwks.json" that I got from AWS locally
signingKey, err := ioutil.ReadFile("./jwks.json")
if err != nil {
log.Fatal("Error when opening file: ", err)
}
// pass in the signing key when middle ware is created
app.Get("/api", middleware.Protected(signingKey), handlers.ReadSomeData)
Then my middleware looks like this where most of it is from Go Fiber's JWT example repo.
func Protected(signingKey []byte) func(*fiber.Ctx) error {
return jwtware.New(jwtware.Config{
SigningKey: signingKey,
ErrorHandler: jwtError,
SigningMethod: "RS256",
})
}
func jwtError(c *fiber.Ctx, err error) error {
if err.Error() == "Missing or malformed JWT" {
c.Status(fiber.StatusBadRequest)
return c.JSON(fiber.Map{"status": "error", "message": err.Error(), "data": nil})
} else {
c.Status(fiber.StatusUnauthorized)
return c.JSON(fiber.Map{"status": "error", "message": err.Error(), "data": nil})
}
}
After an answer, I tried using the "SigningKeys" param but there was a type mismatch so I ended up reading in the jwks json file like so:
func Protected() func(*fiber.Ctx) error {
signingKey, err := os.ReadFile("./jwks.json")
if err != nil {
}
x := make(map[string]interface{})
json.Unmarshal(signingKey, &x)
return jwtware.New(jwtware.Config{
SigningKeys: x,
ErrorHandler: jwtError,
SigningMethod: "RS256",
})
}
However now my error is "Unexpected jwt key id=XXXXXXXXXXXX"
答案1
得分: 1
原文翻译如下:
原来,Fiber框架内置了一个功能,可以通过提供密钥的URL来获取jwks.json数据。可能还有一种方法可以加载本地文件,但是通常不建议这样做,因为密钥可能会根据所处的环境(生产环境或测试环境)而变化。
您需要知道您的AWS用户池区域和该用户池的ID。通常在用户池设置视图中提供这些信息,但您也可以根据AWS文档中提供的以下示例轻松获取这些信息:
https://cognito-idp.{region}.amazonaws.com/{userPoolId}/.well-known/jwks.json
更多信息请参见:AWS:验证JSON Web Token
以下是一个简单的示例,演示如何与AWS Cognito JWT URL配合使用:
authMiddleware := jwtware.New(jwtware.Config{
TokenLookup: "header:Authorization",
AuthScheme: "Bearer",
KeySetURLs: []string{
"https://cognito-idp.some-region-1.amazonaws.com/some-region-1_MYUserPoolId/.well-known/jwks.json",
},
})
// 匹配任何路由
app.Use(authMiddleware, func(c *fiber.Ctx) error {
return c.SendString("🎉 Yay!")
})
log.Fatal(app.Listen(":3000"))
现在,您可以使用类似以下的请求进行测试:
curl --location --request GET 'http://127.0.0.1:3000' \
--header 'Authorization: Bearer MyAWSJWTToken..'
或者使用任何HTTP客户端(如Postman)。您必须在Authorization头中提供您的JWT。
另请参阅:
英文:
It turns out fiber has a built-in functionality to pull the jwks.json data if you provide it a url to the keys. Probably there is also a method to make it load a local file, but with AWS keys you usually do not do it - keys may chnage depending on the environment you are in - production or test.
You need to know your AWS user pool region and that user pool's ID. That is normally provided in the user pool settings view, but you can easily come up with it based on the following example provided in AWS documentation:
https://cognito-idp.{region}.amazonaws.com/{userPoolId}/.well-known/jwks.json
For more see: AWS: Verifying a JSON web token
Here's a simple example to get it working with AWS Cognito JWT url:
authMiddleware := jwtware.New(jwtware.Config{
TokenLookup: "header:Authorization",
AuthScheme: "Bearer",
KeySetURLs: []string{
"https://cognito-idp.some-region-1.amazonaws.com/some-region-1_MYUserPoolId/.well-known/jwks.json",
},
})
// Match any route
app.Use(authMiddleware, func(c *fiber.Ctx) error {
return c.SendString("🥇 Yay!")
})
log.Fatal(app.Listen(":3000"))
You should now be able to test it with a request like this:
curl --location --request GET 'http://127.0.0.1:3000' \
--header 'Authorization: Bearer MyAWSJWTToken..'
Or with any HTTP client(like Postman). You must provide your JWT in a Authorization header.
See also:
答案2
得分: 0
github.com/gofiber/jwt
项目使用github.com/MicahParks/keyfunc
作为JWK Set客户端。自从github.com/MicahParks/keyfunc
处于预发布状态以来,该项目未进行更新。鉴于已知的错误,我建议不要在当前状态下使用该项目。
以下是从AWS Cognito解析JWK Set并直接使用该集合中的密钥解析JWT的示例,该示例来自github.com/MicahParks/keyfunc
项目:
这是来自github.com/MicahParks/keyfunc
项目的示例链接:链接
package main
import (
"fmt"
"log"
"time"
"github.com/golang-jwt/jwt/v4"
"github.com/MicahParks/keyfunc"
)
func main() {
// 从AWS区域和userPoolId获取JWKS URL。
//
// 在此处查看AWS文档:
// https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-verifying-a-jwt.html
regionID := "" // TODO 获取AWS Cognito实例的区域ID。
userPoolID := "" // TODO 获取AWS Cognito实例的用户池ID。
jwksURL := fmt.Sprintf("https://cognito-idp.%s.amazonaws.com/%s/.well-known/jwks.json", regionID, userPoolID)
// 创建keyfunc选项。使用记录错误的错误处理程序。当发现由未知KID签名的JWT或在指定的间隔时刷新JWKS。限制刷新速率。在初始JWKS刷新请求超时后,超时时间为10秒。此超时时间也用于创建keyfunc.Get的初始context.Context。
options := keyfunc.Options{
RefreshErrorHandler: func(err error) {
log.Printf("jwt.Keyfunc出现错误\n错误:%s", err.Error())
},
RefreshInterval: time.Hour,
RefreshRateLimit: time.Minute * 5,
RefreshTimeout: time.Second * 10,
RefreshUnknownKID: true,
}
// 从给定URL的资源创建JWKS。
jwks, err := keyfunc.Get(jwksURL, options)
if err != nil {
log.Fatalf("无法从给定URL的资源创建JWKS。\n错误:%s", err.Error())
}
// 获取要解析的JWT。
jwtB64 := "eyJraWQiOiJmNTVkOWE0ZSIsInR5cCI6IkpXVCIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiJLZXNoYSIsImF1ZCI6IlRhc2h1YW4iLCJpc3MiOiJqd2tzLXNlcnZpY2UuYXBwc3BvdC5jb20iLCJleHAiOjE2MTkwMjUyMTEsImlhdCI6MTYxOTAyNTE3NywianRpIjoiMWY3MTgwNzAtZTBiOC00OGNmLTlmMDItMGE1M2ZiZWNhYWQwIn0.vetsI8W0c4Z-bs2YCVcPb9HsBm1BrMhxTBSQto1koG_lV-2nHwksz8vMuk7J7Q1sMa7WUkXxgthqu9RGVgtGO2xor6Ub0WBhZfIlFeaRGd6ZZKiapb-ASNK7EyRIeX20htRf9MzFGwpWjtrS5NIGvn1a7_x9WcXU9hlnkXaAWBTUJ2H73UbjDdVtlKFZGWM5VGANY4VG7gSMaJqCIKMxRPn2jnYbvPIYz81sjjbd-sc2-ePRjso7Rk6s382YdOm-lDUDl2APE-gqkLWdOJcj68fc6EBIociradX_ADytj-JYEI6v0-zI-8jSckYIGTUF5wjamcDfF5qyKpjsmdrZJA"
// 解析JWT。
token, err := jwt.Parse(jwtB64, jwks.Keyfunc)
if err != nil {
log.Fatalf("无法解析JWT。\n错误:%s", err.Error())
}
// 检查令牌是否有效。
if !token.Valid {
log.Fatalf("令牌无效。")
}
log.Println("令牌有效。")
// 当不再需要时结束后台刷新goroutine。
jwks.EndBackground()
}
英文:
The github.com/gofiber/jwt
project uses github.com/MicahParks/keyfunc
as a JWK Set client. It also has not updated it since github.com/MicahParks/keyfunc
was in pre-release. I would recommend not using that project in its current state due to know bugs.
Here's an example of parsing a JWK Set from AWS Cognito and then using the keys in that set to parse a JWT directly from the github.com/Micahparks/keyfunc
project:
Here's a link to this example from the github.com/MicahParks/keyfunc
project: link
package main
import (
"fmt"
"log"
"time"
"github.com/golang-jwt/jwt/v4"
"github.com/MicahParks/keyfunc"
)
func main() {
// Get the JWKS URL from your AWS region and userPoolId.
//
// See the AWS docs here:
// https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-verifying-a-jwt.html
regionID := "" // TODO Get the region ID for your AWS Cognito instance.
userPoolID := "" // TODO Get the user pool ID of your AWS Cognito instance.
jwksURL := fmt.Sprintf("https://cognito-idp.%s.amazonaws.com/%s/.well-known/jwks.json", regionID, userPoolID)
// 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{
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.
jwtB64 := "eyJraWQiOiJmNTVkOWE0ZSIsInR5cCI6IkpXVCIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiJLZXNoYSIsImF1ZCI6IlRhc2h1YW4iLCJpc3MiOiJqd2tzLXNlcnZpY2UuYXBwc3BvdC5jb20iLCJleHAiOjE2MTkwMjUyMTEsImlhdCI6MTYxOTAyNTE3NywianRpIjoiMWY3MTgwNzAtZTBiOC00OGNmLTlmMDItMGE1M2ZiZWNhYWQwIn0.vetsI8W0c4Z-bs2YCVcPb9HsBm1BrMhxTBSQto1koG_lV-2nHwksz8vMuk7J7Q1sMa7WUkXxgthqu9RGVgtGO2xor6Ub0WBhZfIlFeaRGd6ZZKiapb-ASNK7EyRIeX20htRf9MzFGwpWjtrS5NIGvn1a7_x9WcXU9hlnkXaAWBTUJ2H73UbjDdVtlKFZGWM5VGANY4VG7gSMaJqCIKMxRPn2jnYbvPIYz81sjjbd-sc2-ePRjso7Rk6s382YdOm-lDUDl2APE-gqkLWdOJcj68fc6EBIociradX_ADytj-JYEI6v0-zI-8jSckYIGTUF5wjamcDfF5qyKpjsmdrZJA"
// Parse the JWT.
token, err := jwt.Parse(jwtB64, jwks.Keyfunc)
if 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.
jwks.EndBackground()
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论