Token Validation

Published: (December 14, 2025 at 11:42 AM EST)
2 min read
Source: Dev.to

Source: Dev.to

Overview

The process of validating a JWT involves:

  1. Parsing the token string.
  2. Decoding the header and payload.
  3. Verifying the signature using the appropriate secret/key.
  4. Populating a custom Claims struct with the payload data.
  5. Returning the claims if the token is valid, otherwise returning an error.

Claims Struct

type Claims struct {
    UserID string `json:"user_id"`
    jwt.StandardClaims
}

The Claims struct embeds jwt.StandardClaims and adds a custom UserID field.

Secret Key

secretKey := GetJWTKey()
  • GetJWTKey() retrieves the global secret key used for signing and validating JWTs.
  • In this example the algorithm is HS256, so the key is a []byte.
  • The same key is used for all users.

Parsing the Token

token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
    return secretKey, nil
})

How ParseWithClaims Works

  1. Split the token into three parts: header, payload, and signature.
  2. Decode the header and payload into a temporary jwt.Token struct.
  3. Call the key function (the anonymous function above) to obtain the verification key (secretKey).
  4. Recalculate the expected signature using:
    • The decoded header and payload.
    • The secret key.
    • The algorithm specified in token.Method (e.g., HS256).
  5. Compare the recalculated signature with the signature from the token.
  6. If they match, set token.Valid = true and populate the provided Claims struct with the payload data.

Internal Token Representation

type Token struct {
    Raw       string                 // The original token string
    Method    jwt.SigningMethod      // Signing algorithm (e.g., HS256)
    Header    map[string]interface{} // Header fields (e.g., "alg")
    Claims    jwt.Claims             // Claims (populated with &Claims{})
    Signature string                 // Base64‑encoded signature part
    Valid     bool                   // Result of signature & claim validation
}

Using the Parsed Claims

if claims, ok := token.Claims.(*Claims); ok && token.Valid {
    return claims, nil
}
  • The generic token.Claims is type‑asserted to the concrete *Claims struct.
  • If the token is valid, the function returns the populated claims; otherwise, it returns an error.

TL;DR Flow

  1. jwt.ParseWithClaims receives tokenString.
  2. Splits the token → header, payload, signature.
  3. Decodes header + payload into a temporary jwt.Token.
  4. Calls the key function → returns secretKey.
  5. Recalculates the signature using secretKey and the algorithm from the header.
  6. Compares the recalculated signature with the token’s signature.
    • Match → token is valid, token.Valid = true.
    • No match → token is invalid.
  7. Fills the provided &Claims{} struct with payload data.
  8. Returns the claims (or an error) to the caller.
Back to Blog

Related posts

Read more »

Experimental Hono auth npm package

What I’m Building I’m creating an auth package that developers can drop into their app without writing the usual boilerplate login, register, JWT, email verifi...