Amazon web services 如何在Go中验证来自AWS Cognito的JWT令牌?
如何验证从Amazon Cognito收到的JWT并从中获取信息 我在Cognito中设置了Google身份验证,并将重定向uri设置为hit API Gateway,然后我收到一个代码,并将其发布到该端点: 以RS256格式接收JWT令牌。我现在正在努力验证和解析Golang中的令牌。我曾尝试使用jwt go对其进行解析,但它似乎默认支持HMAC,并阅读了一些他们建议使用前端验证的内容。我尝试了其他一些软件包,也遇到了类似的问题 我在这里找到了这个答案:但是假设代码已经过时了,因为上面只说了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,并阅读了一些他们建议使用前端验证的内容。我尝试了其他一些软件包,也遇到了类似的问题 我在这里找到了这个答案:但是假设代码已经过时了,因为上面只说了
恐慌:找不到键
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部分: 然后你可以:
keySet, err := jwk.Fetch(THE_COGNITO_URL_DESCRIBED_ABOVE)
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 Lambdagin
)。如果您使用不同的方法管理请求,请确保将其替换为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)