Amazon web services 如何在Go中验证来自AWS Cognito的JWT令牌?

Amazon web services 如何在Go中验证来自AWS Cognito的JWT令牌?,amazon-web-services,go,jwt,amazon-cognito,jwt-go,Amazon Web Services,Go,Jwt,Amazon Cognito,Jwt Go,如何验证从Amazon Cognito收到的JWT并从中获取信息 我在Cognito中设置了Google身份验证,并将重定向uri设置为hit API Gateway,然后我收到一个代码,并将其发布到该端点: 以RS256格式接收JWT令牌。我现在正在努力验证和解析Golang中的令牌。我曾尝试使用jwt go对其进行解析,但它似乎默认支持HMAC,并阅读了一些他们建议使用前端验证的内容。我尝试了其他一些软件包,也遇到了类似的问题 我在这里找到了这个答案:但是假设代码已经过时了,因为上面只说了

如何验证从Amazon Cognito收到的JWT并从中获取信息

我在Cognito中设置了Google身份验证,并将重定向uri设置为hit API Gateway,然后我收到一个代码,并将其发布到该端点:

以RS256格式接收JWT令牌。我现在正在努力验证和解析Golang中的令牌。我曾尝试使用jwt go对其进行解析,但它似乎默认支持HMAC,并阅读了一些他们建议使用前端验证的内容。我尝试了其他一些软件包,也遇到了类似的问题

我在这里找到了这个答案:但是假设代码已经过时了,因为上面只说了
恐慌:找不到键


io可以很容易地解码密钥,也可能验证密钥。我不确定在Amazon生成令牌时公钥/密钥在哪里,但据我所知,我也需要使用JWK URL进行验证?我已经找到了一些AWS特定的解决方案,但它们似乎都有数百行。在Golang,它肯定没有那么复杂吧?

Amazon Cognito的公钥

正如您已经猜到的,您需要公钥来验证JWT令牌

为您的用户池下载并存储相应的公共JSON Web密钥(JWK)。它是JSON Web密钥集(JWKS)的一部分。 你可以在 https://cognito-idp.{region}.amazonaws.com/{userPoolId}/.well-known/jwks.json

解析密钥并验证令牌

JSON文件结构记录在web中,因此您可以手动解析该文件结构,生成公钥,等等

但是仅仅使用一个库可能会更容易,例如:

然后jwt去处理jwt部分:

然后你可以:

  • 使用第一个库下载并解析公钥JSON

     keySet, err := jwk.Fetch(THE_COGNITO_URL_DESCRIBED_ABOVE)
    
  • 当使用jwt go解析令牌时,使用jwt头中的“kid”字段查找要使用的正确密钥

     token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
     if _, ok := token.Method.(*jwt.SigningMethodRS256); !ok {
         return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
     }
     kid, ok := token.Header["kid"].(string)
     if !ok {
         return nil, errors.New("kid header not found")
     }
     keys := keySet.LookupKeyID(kid);
     if !ok {
         return nil, fmt.Errorf("key with specified kid is not present in jwks")
     }
     var publickey interface{}
     err = keys.Raw(&publickey)
     if err != nil {
         return nil, fmt.Errorf("could not parse pubkey")
     }
     return publickey, nil
    

  • 欧金尼欧的回答对我不起作用了,因为。我最后用这样的东西来修理

    token,err:=jwt.Parse(tokenString,func(token*jwt.token)(接口{},错误){
    令牌,err:=jwt.Parse(令牌字符串,func(令牌*jwt.token)(接口{},错误){
    如果,确定:=token.Method.(*jwt.SigningMethodRS256);!确定{
    返回nil,fmt.Errorf(“意外的签名方法:%v”,token.Header[“alg”])
    }
    kid,ok:=令牌.头[“kid”]。(字符串)
    如果!好的{
    返回nil,errors.New(“未找到kid头”)
    }
    keys:=keySet.LookupKeyID(kid);
    如果len(键)=0{
    返回nil,fmt.Errorf(“未找到键%v”,kid)
    }
    //键[0]。Materialize()不再存在
    var原始接口{}
    返回原始,键[0]。原始(&raw)
    })
    
    eugenioy和Kevin Wydler提供的代码中的类型断言对我不起作用:
    *jwt.SigningMethodRS256不是类型

    *jwt.SigningMethodRS256
    是初始提交中的一种类型。从2014年7月的第二次提交开始,它被抽象并替换为一个全局变量(请参阅)

    以下代码适用于我:

    func验证(令牌字符串,键集*jwk.Set){
    tkn,err:=jwt.Parse(tokenString,func(token*jwt.token)(接口{},错误){
    如果token.Method.Alg()!=“RSA256”{//jwa.RS256.String()也可以工作
    返回nil,fmt.Errorf(“意外的签名方法:%v”,token.Header[“alg”])
    }
    kid,ok:=令牌.头[“kid”]。(字符串)
    如果!好的{
    返回nil,errors.New(“未找到kid头”)
    }
    keys:=keySet.LookupKeyID(kid)
    如果len(键)=0{
    返回nil,fmt.Errorf(“未找到键%v”,kid)
    }
    var原始接口{}
    返回原始,键[0]。原始(&raw)
    })
    }
    
    使用以下依赖项版本:

    github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1
    github.com/lestrrat-go/jwx v1.0.4
    

    这就是我的工作原理:

    import (
        "errors"
        "fmt"
        "github.com/dgrijalva/jwt-go"
        "github.com/gin-gonic/gin"
        "github.com/lestrrat-go/jwx/jwk"
        "net/http"
        "os"
    )
    
    func verifyToken(token *jwt.Token) (interface{}, error) {
        // make sure to replace this with your actual URL
        // https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-verifying-a-jwt.html#amazon-cognito-user-pools-using-tokens-step-2
        jwksURL := "COGNITO_JWKS_URL" 
        set, err := jwk.FetchHTTP(jwksURL)
        if err != nil {
            return nil, err
        }
    
        keyID, ok := token.Header["kid"].(string)
        if !ok {
            return nil, errors.New("expecting JWT header to have string kid")
        }
    
        keys := set.LookupKeyID(keyID)
        if len(keys) == 0 {
            return nil, fmt.Errorf("key %v not found", keyID)
        }
    
        if key := set.LookupKeyID(keyID); len(key) == 1 {
            return key[0].Materialize()
        }
    
        return nil, fmt.Errorf("unable to find key %q", keyID)
    }
    
    在我的例子中,我这样称呼它(使用AWS Lambda
    gin
    )。如果您使用不同的方法管理请求,请确保将其替换为http.Request或您可能使用的任何其他框架:

    func JWTVerify() gin.HandlerFunc {
        return func(c *gin.Context) {
            tokenString := c.GetHeader("AccessToken")
            _, err := jwt.Parse(tokenString, verifyToken)
            if err != nil {
                c.AbortWithStatus(http.StatusUnauthorized)
            }
        }
    }
    
    这是我的
    go.mod

    module MY_MODULE_NAME
    go 1.12
    
    require (
        github.com/aws/aws-lambda-go v1.20.0
        github.com/aws/aws-sdk-go v1.36.0
        github.com/awslabs/aws-lambda-go-api-proxy v0.9.0
        github.com/dgrijalva/jwt-go v3.2.0+incompatible
        github.com/gin-gonic/gin v1.6.3
        github.com/google/uuid v1.1.2
        github.com/lestrrat-go/jwx v0.9.2
        github.com/onsi/ginkgo v1.14.2 // indirect
        github.com/onsi/gomega v1.10.3 // indirect
        golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
    )
    

    这是我用最新的(
    v1.0.8
    github.com/lestrrat go/jwx
    所做的。请注意,
    github.com/dgrijalva/jwt go
    似乎不再被维护,人们正在使用它来进行他们需要的更新

    主程序包
    进口(
    ...
    “github.com/lestrrat go/jwx/jwk”
    “github.com/lestrrat go/jwx/jwt”
    )
    ...
    密钥集,错误:=jwk.Fetch(“https://cognito-idp.“+region+”.amazonaws.com/“+userPoolID+”/.well-known/jwks.json”)
    parsedToken,err:=jwt.Parse(
    bytes.NewReader(token),//token是一个[]字节
    jwt.带键集(键集),
    jwt.WithValidate(真),
    jwt.与发行人(…),
    jwt.WithClaimValue(“键”,值),
    )
    //照常检查错误
    //在这里,您可以调用parsedToken上的方法来获取声明值
    ...
    

    由于Materialize已重命名为Raw,此操作不再有效。Raw也采用键类型。从
    lestrrat go/jwx v1.x.y
    )开始:您可以替换
    返回键[0]。Materialize()
    方法为:
    var pubkey interface{}
    ,然后
    err:=key[0]。Raw(&pubkey)
    返回pubkey,nil
    。确保对err(原始方法的返回值)进行错误检查这在为密钥集添加github.com/lestrrat-go/jwx/jwk包的情况下起到了作用。谢谢。我不确定我是否能理解这段代码。您是否对令牌进行了两次解析?另外,
    keySet
    来自何处?这对我很有效;一些注意事项:(1)AWS中的发行人必须是:cognito-idp.us-east-1.amazonaws.com/,(2)根据前面的链接,您应该使用WithClaimValue验证“token_use”是“id”还是“access”。(3)第一个token参数应该是原始的base64编码的id token,如果您使用的是valida的单元测试,则最后一个(4)