Token Validation

Published: (December 14, 2025 at 11:42 AM EST)
3 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.
0 views
Back to Blog

Related posts

Read more »

YES I AM THE ONE WHO REQUESTED THE ACCESS

NOTE: This is my first post, so apologies in advance if I’ve misunderstood something. I’m open to discussions and corrections. What is JWT? JWT stands for JSON...

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...