AWS Cognito token validation with Go

3 min. read

Recently, I was struggling with “How to verify and validate AWS Cognito user JWT with the Go backend”. I knew only basic concepts of how JWT token works and I couldn’t find any comprehensive guide for implementation. Nevertheless, sometimes its better to do it the hard way, and learn everything by yourself.

Short background

The client is sending user token with every request. API backend is written with go/go-swagger and is using API key authentication. In such scenario, every authenticated request has argument *principal, holding the client user JWT.


Here is my github gist:

// AWS Cognito public keys are available at address:
// https://cognito-idp.{region}{userPoolId}/.well-known/jwks.json
publicKeysURL := "https://cognito-idp.{region}{userPoolId}/.well-known/jwks.json"

// Start with downloading public keys information
// The .Fetch method is used from package
publicKeySet, err := jwk.Fetch(publicKeysURL)
if err != nil{
	log.Printf("failed to parse key: %s", err)

// Get JWT as string from *principal
// 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
tokenString := fmt.Sprintf("%s", *principal)

// We want to get details from the JWT: 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 Parse - it's actually doing parsing, validation and returns back a token.
// Use .Parse or .ParseWithClaims methods from
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 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")

Docs to read and tools to use:

6. May 2020
Posted in Gists

Software developer, lives in Zilina, Slovakia. Fan of modern web technologies, digitalization, cloud and education. Also co-owner of a local coffee brand - Kava Doppio