JWT "Войти через Google" недействителен при отправке на мой сервер - PullRequest
0 голосов
/ 05 августа 2020

Настройка: клиентская часть 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, который добавлен в качестве утвержденного источника

1 Ответ

0 голосов
/ 05 августа 2020

Близко к: { ссылка }

Наш друг здесь пишет, как правильно проверить 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)
        }
    }
...