“使用 Google 登录”JWT 发送到我的服务器时无效
Posted
技术标签:
【中文标题】“使用 Google 登录”JWT 发送到我的服务器时无效【英文标题】:"Log in with Google" JWT is invalid when sent to my server 【发布时间】:2020-11-25 04:23:08 【问题描述】:设置:React 前端和 Golang 后端。
我的 React 前端成功从 Google 获取令牌:
<GoogleLogin
clientId="<client-id>.apps.googleusercontent.com"
onSuccess=response => responseGoogle(response)
>
</GoogleLogin>
我有一个突变可以发送我需要的信息:
initiateTestMutation(
variables:
idToken: response.getAuthResponse().id_token,
email: response.profileObj.email,
givenName: response.profileObj.givenName,
familyName: response.profileObj.familyName,
然后它发送一个我可以用 jwt.io 解码的令牌,但它显示“无效签名”。它包含我的正确信息,但同样无效。
在我的服务器端,我也尝试验证它,但失败了。
// This is the token as a string
unencodedToken := *input.IDToken
fmt.Println(unencodedToken)
token, err := jwt.Parse(unencodedToken, func(token *jwt.Token) (interface, error)
return []byte("What goes here?"), nil
)
if err != nil
fmt.Println("Could not decode the token")
fmt.Println(err)
if token.Valid
fmt.Println("Valid token")
else if ve, ok := err.(*jwt.ValidationError); ok
if ve.Errors&jwt.ValidationErrorMalformed != 0
fmt.Println("That's not even a token")
else if ve.Errors&(jwt.ValidationErrorExpired|jwt.ValidationErrorNotValidYet) != 0
// Token is either expired or not active yet
fmt.Println("Expired token")
else
fmt.Println("Couldn't handle this token:", err)
else
fmt.Println("Couldn't handle this token:", err)
其他信息:
这一切都在本地完成。app.localhost
是请求 JWT 的域,该域被添加为已批准的来源
【问题讨论】:
您必须使用令牌发布者的公钥来验证令牌。 “这里发生了什么”应该是那个公钥。 @BurakSerdar 很抱歉仍未得到它,但是我应该从googleapis.com/oauth2/v3/certs 传递什么来验证 JWT? 这能回答你的问题吗? How to verify a JWT Token from AWS Cognito in Go? @peko 是的,我在自己的答案中添加了以下内容。 【参考方案1】:靠近:https://***.com/a/61718113/12563520
我们的朋友here 写了如何正确验证 JWT。
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface, error)
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 len(keys) == 0
return nil, fmt.Errorf("key %v not found", kid)
// keys[0].Materialize() doesn't exist anymore
var raw interface
return raw, keys[0].Raw(&raw)
)
这是我的完整实现,以满足 Google 从此处提供的说明:https://developers.google.com/identity/sign-in/web/backend-auth
我可能比我需要的更频繁地验证事情,所以如果有人想编辑或评论,我会做出改变。
// Get the Key
unencodedToken := *input.IDToken
fetchedToken, err := jwk.FetchHTTP("https://www.googleapis.com/oauth2/v3/certs")
// Parse the token with standard claims
token, err := jwt.ParseWithClaims(unencodedToken, &jwt.StandardClaims, func(token *jwt.Token) (interface, error)
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok
return nil, gqlerror.Errorf("Unexpected token signing method", token)
kid, ok := token.Header["kid"].(string)
if !ok
fmt.Println("Could not find Key ID")
return nil, gqlerror.Errorf("Could not find key ID in token:", token)
keys := fetchedToken.LookupKeyID(kid)
if len(keys) == 0
fmt.Println("Could not find key in the signature")
return nil, gqlerror.Errorf("Could not find key in the signature: ", token)
var empty interface
return empty, keys[0].Raw(&empty)
)
if err != nil
fmt.Println("Could not decode the token")
fmt.Println(err)
return nil, gqlerror.Errorf("Could not decode the token: ", token)
// Check if the token is valid
if token.Valid
fmt.Println("Valid token")
else if ve, ok := err.(*jwt.ValidationError); ok
if ve.Errors&jwt.ValidationErrorMalformed != 0
fmt.Println("That's not even a token")
return nil, gqlerror.Errorf("Invalid Token")
else if ve.Errors&(jwt.ValidationErrorExpired|jwt.ValidationErrorNotValidYet) != 0
// Token is either expired or not active yet
fmt.Println("Expired token")
return nil, gqlerror.Errorf("Expired Token:", token)
else
fmt.Println("Couldn't handle this token", token, err)
return nil, gqlerror.Errorf(err.Error())
else
fmt.Println("Couldn't handle this token", token, err)
return nil, gqlerror.Errorf(err.Error())
// Check if the claims are valid
err = token.Claims.Valid()
if err != nil
fmt.Println("Failed validity check", err)
return nil, gqlerror.Errorf("Failed validity check on token", token, err.Error())
// Check the custom claims
if claims, ok := token.Claims.(*jwt.StandardClaims); ok && token.Valid
audienceVerified := claims.VerifyAudience("773117128619-kfrd500nf8bfaq7anl7ee1ae7ucg5kp5.apps.googleusercontent.com", true)
if !audienceVerified
// TODO Handle failure
fmt.Println("Audience unverified")
return nil, gqlerror.Errorf("Audience unverified on token", token)
if claims, ok := token.Claims.(*jwt.StandardClaims); ok && token.Valid
netloc := claims.VerifyIssuer("accounts.google.com", true)
httpVersion := claims.VerifyIssuer("accounts.google.com", true)
if !netloc && !httpVersion
// TODO Handle failure
fmt.Println("Can't verify issuer")
return nil, gqlerror.Errorf("Can't verify issuer on token", token)
【讨论】:
以上是关于“使用 Google 登录”JWT 发送到我的服务器时无效的主要内容,如果未能解决你的问题,请参考以下文章
在 Google OAuth2 授权流程之后如何将 JWT 从我的后端服务器发送到我的前端
如何从 expressjs 发送 jwt 令牌以响应应用程序? [关闭]