Skip to content

Instantly share code, notes, and snippets.

@tomas-chudjak
Last active February 16, 2024 15:39
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save tomas-chudjak/d0123ca5f6461bc8e220063e8a4e60c8 to your computer and use it in GitHub Desktop.
Save tomas-chudjak/d0123ca5f6461bc8e220063e8a4e60c8 to your computer and use it in GitHub Desktop.
How to parse and validate AWS Cognito token with go
// AWS Cognito public keys are available at address:
// https://cognito-idp.{region}.amazonaws.com/{userPoolId}/.well-known/jwks.json
publicKeysURL := "https://cognito-idp.{region}.amazonaws.com/{userPoolId}/.well-known/jwks.json"
// Start with downloading public keys information
// The .Fetch method is used from https://github.com/lestrrat-go/jwx package
publicKeySet, err := jwk.Fetch(publicKeysURL)
if err != nil{
log.Printf("failed to parse key: %s", err)
}
// Get access token as string from *principal
// Access token is Base64-encoded JSON that contains user details - called "claims".
// ---
// Token is separated into 3 sections - header, payload and signature
// You can test and validate your token with jwt.io
tokenString := fmt.Sprintf("%s", *principal)
// We want to get details from the access token: client_id and unique user identifier.
// Let's add client_id. We can verify, if it match our App cliet ID in AWS Cognito User Pool
// We can also add user identifier (f.e. "username") to use it with our App
type AWSCognitoClaims struct{
Client_ID string `json:client_id`
Username string `json:username`
jwt.StandardClaims
}
// JWT Parse - it's actually doing parsing, validation and returns back a token.
// Use .Parse or .ParseWithClaims methods from https://github.com/dgrijalva/jwt-go
token, err := jwt.ParseWithClaims(tokenString, &AWSCognitoClaims{}, func(token *jwt.Token) (interface{}, error){
// Verify if the token was signed with correct signing method
// AWS Cognito is using RSA256 in my case
_, ok := token.Method.(*jwt.SigningMethodRSA);
if !ok {
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
}
// Get "kid" value from token header
// "kid" is shorthand for Key ID
kid, ok := token.Header["kid"].(string)
if !ok {
return nil, errors.New("kid header not found")
}
// Check client_id attribute from the access token
claims, ok := token.Claims.(*AWSCognitoClaims)
if !ok {
return nil, errors.New("There is problem to get claims")
}
log.Printf("client_id: %v", claims.Client_ID)
// "kid" must be present in the public keys set
keys := publicKeySet.LookupKeyID(kid);
if len(keys) == 0 {
return nil, fmt.Errorf("key %v not found", kid)
}
// In our case, we are returning only one key = keys[0]
// Return token key as []byte{string} type
var tokenKey interface{}
if err := keys[0].Raw(&tokenKey); err != nil {
return nil, errors.New("failed to create token key")
}
return tokenKey, nil
})
if err != nil{
// This place can throw expiration error
log.Printf("token problem: %s", err)
}
// Check if token is valid
if !token.Valid {
log.Println("token is invalid")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment