or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

claims.mdindex.mdkey-parsing.mdrequest-extraction.mdsigning-methods.mdtoken-creation.mdtoken-parsing.md
tile.json

token-parsing.mddocs/

Token Parsing and Validation

This document covers parsing JWT token strings and validating their signatures and claims.

Parsing Functions

Parse

func Parse(tokenString string, keyFunc Keyfunc, options ...ParserOption) (*Token, error)

Parses, validates, and verifies the signature of a JWT token string. Returns a parsed Token with MapClaims.

Parameters:

  • tokenString - The JWT string to parse
  • keyFunc - Callback function to supply the verification key
  • options - Parser options for validation configuration

Important: Always use WithValidMethods option to validate the algorithm claim and prevent algorithm confusion attacks.

ParseWithClaims

func ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc, options ...ParserOption) (*Token, error)

Parses and validates a token with custom claims implementation. The provided claims object will be populated with the token's claims.

Note: If your custom claims embed RegisteredClaims (or similar), either embed a non-pointer version or allocate memory before passing to avoid panics.

Keyfunc Type

type Keyfunc func(*Token) (any, error)

A callback function to supply the verification key. Receives the parsed but unverified token, allowing you to inspect the header (e.g., kid claim) to determine which key to use.

Returns: Either a single verification key or a VerificationKeySet containing multiple keys.

Parser Type

type Parser struct {
    // unexported fields
}

The Parser type provides more control over parsing behavior.

Parser Constructor

func NewParser(options ...ParserOption) *Parser

Creates a new Parser with the specified options.

Parser Methods

func (p *Parser) Parse(tokenString string, keyFunc Keyfunc) (*Token, error)

Parses, validates, and verifies the signature.

func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc) (*Token, error)

Parses with custom claims.

func (p *Parser) ParseUnverified(tokenString string, claims Claims) (token *Token, parts []string, err error)

Parses the token WITHOUT validating the signature.

WARNING: Only use when you know the signature will be checked elsewhere. This is dangerous if misused.

func (p *Parser) DecodeSegment(seg string) ([]byte, error)

Decodes a JWT-specific base64url encoded segment, respecting parser options like WithStrictDecoding or WithPaddingAllowed.

Parser Options

type ParserOption func(*Parser)

Algorithm Validation

func WithValidMethods(methods []string) ParserOption

Validates that the token's alg claim matches one of the specified methods. Strongly recommended to prevent algorithm confusion attacks.

Example:

jwt.Parse(tokenString, keyFunc, jwt.WithValidMethods([]string{"HS256", "HS384"}))

Audience Validation

func WithAudience(aud ...string) ParserOption

Requires that the aud claim contains at least one of the specified audiences. Validation fails if the aud claim is missing or doesn't match.

func WithAllAudiences(aud ...string) ParserOption

Requires that the aud claim contains ALL of the specified audiences.

Issuer Validation

func WithIssuer(iss string) ParserOption

Requires the iss claim to match the specified issuer exactly. Validation fails if the claim is missing or doesn't match.

Subject Validation

func WithSubject(sub string) ParserOption

Requires the sub claim to match the specified subject exactly.

Time-Based Validation

func WithLeeway(leeway time.Duration) ParserOption

Sets a leeway window for time-based claims (exp, nbf, iat) to account for clock skew between systems.

Example:

jwt.Parse(tokenString, keyFunc, jwt.WithLeeway(5*time.Second))
func WithIssuedAt() ParserOption

Enables verification of the iat (issued at) claim.

func WithExpirationRequired() ParserOption

Makes the exp claim required. By default, exp is optional.

func WithTimeFunc(f func() time.Time) ParserOption

Sets a custom time function for validation. Primarily useful for testing. For clock skew, use WithLeeway instead.

Claims Validation Control

func WithoutClaimsValidation() ParserOption

Disables all claims validation. Only use if you know exactly what you're doing. Signature verification still occurs.

JSON Parsing

func WithJSONNumber() ParserOption

Configures the JSON parser to use json.Number for numeric values instead of float64.

Encoding Options

func WithPaddingAllowed() ParserOption

Allows padding in base64url encoded segments. The JWT spec requires no padding, but some implementations include it.

func WithStrictDecoding() ParserOption

Enables strict base64url decoding that requires trailing padding bits to be zero (RFC 4648 section 3.5).

Verification Keys

type VerificationKey interface {
    crypto.PublicKey | []uint8
}

Represents a public key or secret key for signature verification.

type VerificationKeySet struct {
    Keys []VerificationKey
}

A set of verification keys. The parser will try each key until one successfully verifies the signature.

Validator Type

type Validator struct {
    // unexported fields
}

Standalone validator for validating already-parsed claims without signature verification.

func NewValidator(opts ...ParserOption) *Validator

Creates a standalone validator with the specified options.

func (v *Validator) Validate(claims Claims) error

Validates claims and executes custom validation if the claims implement ClaimsValidator.

Note: This does NOT perform signature verification. It only validates claim values.

Usage Examples

Basic Token Parsing with HMAC

import (
    "fmt"
    "github.com/golang-jwt/jwt/v5"
)

func parseToken(tokenString string) (*jwt.Token, error) {
    secretKey := []byte("your-256-bit-secret")

    token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
        // Validate the signing method
        if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
            return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
        }
        return secretKey, nil
    }, jwt.WithValidMethods([]string{"HS256"}))

    return token, err
}

// Use the token
token, err := parseToken("eyJhbGc...")
if err != nil {
    panic(err)
}

if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
    fmt.Println("Subject:", claims["sub"])
}

Parsing with Custom Claims

import (
    "github.com/golang-jwt/jwt/v5"
)

type CustomClaims struct {
    Username string   `json:"username"`
    Roles    []string `json:"roles"`
    jwt.RegisteredClaims
}

func parseWithCustomClaims(tokenString string) (*CustomClaims, error) {
    secretKey := []byte("your-secret-key")

    claims := &CustomClaims{}
    token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
        return secretKey, nil
    }, jwt.WithValidMethods([]string{"HS256"}))

    if err != nil {
        return nil, err
    }

    if !token.Valid {
        return nil, fmt.Errorf("invalid token")
    }

    return claims, nil
}

Parsing with RSA Public Key

import (
    "crypto/rsa"
    "github.com/golang-jwt/jwt/v5"
)

func parseRSAToken(tokenString string, publicKey *rsa.PublicKey) (*jwt.Token, error) {
    token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
        // Validate the signing method
        if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
            return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
        }
        return publicKey, nil
    }, jwt.WithValidMethods([]string{"RS256", "RS384", "RS512"}))

    return token, err
}

Parsing with Comprehensive Validation

import (
    "time"
    "github.com/golang-jwt/jwt/v5"
)

func parseWithValidation(tokenString string) (*jwt.Token, error) {
    secretKey := []byte("secret")

    token, err := jwt.Parse(tokenString,
        func(token *jwt.Token) (interface{}, error) {
            return secretKey, nil
        },
        jwt.WithValidMethods([]string{"HS256"}),
        jwt.WithIssuer("my-service"),
        jwt.WithAudience("my-app"),
        jwt.WithLeeway(5*time.Second),
        jwt.WithExpirationRequired(),
    )

    return token, err
}

Using Key ID (kid) to Select Key

import (
    "fmt"
    "github.com/golang-jwt/jwt/v5"
)

func parseWithKeyID(tokenString string, keyMap map[string][]byte) (*jwt.Token, error) {
    token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
        // Extract kid from header
        kid, ok := token.Header["kid"].(string)
        if !ok {
            return nil, fmt.Errorf("missing or invalid kid header")
        }

        // Look up key by kid
        key, ok := keyMap[kid]
        if !ok {
            return nil, fmt.Errorf("unknown key ID: %s", kid)
        }

        return key, nil
    }, jwt.WithValidMethods([]string{"HS256"}))

    return token, err
}

Using Parser for Multiple Tokens

import (
    "time"
    "github.com/golang-jwt/jwt/v5"
)

// Create parser once, reuse for multiple tokens
parser := jwt.NewParser(
    jwt.WithValidMethods([]string{"HS256"}),
    jwt.WithIssuer("my-service"),
    jwt.WithLeeway(5*time.Second),
)

// Parse multiple tokens with the same configuration
func parseMultiple(tokens []string, secretKey []byte) {
    keyFunc := func(token *jwt.Token) (interface{}, error) {
        return secretKey, nil
    }

    for _, tokenString := range tokens {
        token, err := parser.Parse(tokenString, keyFunc)
        if err != nil {
            fmt.Printf("Error: %v\n", err)
            continue
        }
        fmt.Printf("Valid token: %v\n", token.Valid)
    }
}

Parsing Unverified (Dangerous - Use with Caution)

import (
    "github.com/golang-jwt/jwt/v5"
)

// WARNING: Only use when signature is verified elsewhere
func inspectTokenWithoutVerifying(tokenString string) {
    parser := jwt.NewParser()
    token, parts, err := parser.ParseUnverified(tokenString, jwt.MapClaims{})
    if err != nil {
        panic(err)
    }

    // Can inspect claims and header, but DO NOT trust without verification
    fmt.Println("Header:", token.Header)
    fmt.Println("Claims:", token.Claims)
    fmt.Println("Parts:", parts)
}

Best Practices

Always Validate Algorithm

Use WithValidMethods to prevent algorithm confusion attacks:

jwt.Parse(tokenString, keyFunc, jwt.WithValidMethods([]string{"HS256"}))

Validate Required Claims

Always validate critical claims for your use case:

jwt.Parse(tokenString, keyFunc,
    jwt.WithValidMethods([]string{"HS256"}),
    jwt.WithIssuer("trusted-issuer"),
    jwt.WithAudience("my-app"),
    jwt.WithExpirationRequired(),
)

Handle Clock Skew

Use leeway to handle clock differences between systems:

jwt.Parse(tokenString, keyFunc, jwt.WithLeeway(30*time.Second))

Error Handling

Check specific error types to handle different failure scenarios:

token, err := jwt.Parse(tokenString, keyFunc)
if err != nil {
    switch {
    case errors.Is(err, jwt.ErrTokenExpired):
        // Token expired - maybe refresh
    case errors.Is(err, jwt.ErrTokenSignatureInvalid):
        // Invalid signature - reject
    case errors.Is(err, jwt.ErrTokenMalformed):
        // Malformed token - reject
    default:
        // Other error
    }
}

Common Errors

  • ErrTokenMalformed: Token string is not a valid JWT format
  • ErrTokenSignatureInvalid: Signature verification failed
  • ErrTokenExpired: Token has expired (exp claim)
  • ErrTokenNotValidYet: Token not yet valid (nbf claim)
  • ErrTokenInvalidAudience: Audience claim doesn't match expected
  • ErrTokenInvalidIssuer: Issuer claim doesn't match expected
  • ErrTokenRequiredClaimMissing: Required claim is missing