使用Go Fiber中间件进行AWS Cognito JWT验证(获得“密钥类型无效”)

8zzbczxx  于 2023-03-10  发布在  Go
关注(0)|答案(2)|浏览(283)

当我尝试在中间件中验证基于Cognito的JWT时,我得到“key is of invalid type”(密钥类型无效)。目前,我在安装Fiber应用程序时是这样设置中间件的:

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

然后我的中间件看起来像这样,其中大部分来自Go Fiber的JWT示例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})
    }
}

在得到答案后,我尝试使用“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",
    })
}

但是,现在我的错误是“意外的jwt密钥ID=XXXXXXXXXXXX”

bxfogqkk

bxfogqkk1#

事实证明fiber有一个内置的功能,如果你给它一个键的url,它就可以拉取jwks.json数据。也许还有一个方法可以让它加载一个本地文件,但是对于AWS键,你通常不会这样做--键可能会根据你在生产或测试中的环境而改变。
您需要知道您的AWS用户池区域和该用户池的ID。这通常在用户池设置视图中提供,但您可以根据AWS文档中提供的以下示例轻松找到它:
https://cognito-idp.{region}.amazonaws.com/{userPoolId}/.well-known/jwks.json
更多信息请参见:AWS: Verifying a 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",
        },
    })

    // Match any route
    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。
另见:

6l7fqoea

6l7fqoea2#

github.com/gofiber/jwt项目使用github.com/MicahParks/keyfunc作为JWK集客户端。它也has not updated它,因为github.com/MicahParks/keyfunc是在预发布。我建议不要使用该项目在其目前的状态,由于已知的错误。
下面是一个从AWS Cognito解析JWK集合,然后使用该集合中的键直接从github.com/Micahparks/keyfunc项目解析JWT的示例:
下面是github.com/MicahParks/keyfunc项目中此示例的链接: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()
}

相关问题