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-creation.mddocs/

Token Creation and Signing

This document covers creating and signing JWT tokens using the jwt library.

Creating Tokens

Token Constructor Functions

func New(method SigningMethod, opts ...TokenOption) *Token

Creates a new Token with the specified signing method and an empty MapClaims. Additional options can be specified for future extensibility.

func NewWithClaims(method SigningMethod, claims Claims, opts ...TokenOption) *Token

Creates a new Token with the specified signing method and claims. This is the recommended way to create tokens with custom claims.

Parameters:

  • method - The signing method to use (e.g., jwt.SigningMethodHS256)
  • claims - Claims to embed in the token (can be RegisteredClaims, MapClaims, or custom)
  • opts - Optional token options (currently unused but reserved for future use)

Token Type

type Token struct {
    Raw       string
    Method    SigningMethod
    Header    map[string]any
    Claims    Claims
    Signature []byte
    Valid     bool
}

Fields:

  • Raw - The raw token string (populated when parsing)
  • Method - The signing method used or to be used
  • Header - The first segment of the token in decoded form (typically contains "typ" and "alg")
  • Claims - The second segment of the token in decoded form
  • Signature - The third segment of the token in decoded form (populated when parsing)
  • Valid - Specifies if the token is valid (populated when parsing/verifying)

Signing Tokens

SignedString Method

func (t *Token) SignedString(key any) (string, error)

Creates and returns a complete, signed JWT string. The token is signed using the SigningMethod specified in the token. The key type must match the signing method:

  • HMAC (HS256/384/512): []byte
  • RSA (RS256/384/512, PS256/384/512): *rsa.PrivateKey
  • ECDSA (ES256/384/512): *ecdsa.PrivateKey
  • EdDSA (EdDSA): ed25519.PrivateKey

Returns the complete JWT string in format: header.payload.signature

SigningString Method

func (t *Token) SigningString() (string, error)

Generates the signing string without the signature. This is used internally by SignedString but can be useful for custom signing implementations. Returns the string in format: header.payload

EncodeSegment Method

func (*Token) EncodeSegment(seg []byte) string

Encodes a JWT segment using base64url encoding with padding stripped, as required by the JWT specification.

Token Options

type TokenOption func(*Token)

A reserved type for future token creation options. Currently unused but allows for forward compatibility.

Usage Examples

Creating a Token with HMAC Signing

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

// Create claims
claims := jwt.MapClaims{
    "sub":  "user123",
    "name": "John Doe",
    "iat":  time.Now().Unix(),
    "exp":  time.Now().Add(24 * time.Hour).Unix(),
}

// Create token
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

// Sign with secret key
secretKey := []byte("your-256-bit-secret")
tokenString, err := token.SignedString(secretKey)
if err != nil {
    panic(err)
}

fmt.Println("Token:", tokenString)

Creating a Token with RSA Signing

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

// Load RSA private key (example - in practice load from file)
var privateKey *rsa.PrivateKey

// Create registered claims
claims := jwt.RegisteredClaims{
    Issuer:    "my-service",
    Subject:   "user456",
    Audience:  jwt.ClaimStrings{"client-app"},
    ExpiresAt: jwt.NewNumericDate(time.Now().Add(1 * time.Hour)),
    IssuedAt:  jwt.NewNumericDate(time.Now()),
    ID:        "token-id-123",
}

// Create and sign token
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
tokenString, err := token.SignedString(privateKey)
if err != nil {
    panic(err)
}

Creating a Token with Custom Claims

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

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

// Create custom claims
claims := CustomClaims{
    Username: "johndoe",
    Roles:    []string{"admin", "user"},
    RegisteredClaims: jwt.RegisteredClaims{
        ExpiresAt: jwt.NewNumericDate(time.Now().Add(2 * time.Hour)),
        IssuedAt:  jwt.NewNumericDate(time.Now()),
    },
}

// Create and sign token
token := jwt.NewWithClaims(jwt.SigningMethodHS512, claims)
secretKey := []byte("your-secret-key")
tokenString, err := token.SignedString(secretKey)

Creating a Token with ECDSA Signing

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

// Load ECDSA private key
var ecPrivateKey *ecdsa.PrivateKey

// Create claims
claims := jwt.RegisteredClaims{
    Subject:   "user789",
    ExpiresAt: jwt.NewNumericDate(time.Now().Add(30 * time.Minute)),
}

// Create and sign token with ES256
token := jwt.NewWithClaims(jwt.SigningMethodES256, claims)
tokenString, err := token.SignedString(ecPrivateKey)

Modifying Token Headers

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

// Create token
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
    "sub": "user123",
})

// Add custom header fields
token.Header["kid"] = "key-id-1"  // Key ID
token.Header["cty"] = "JWT"       // Content Type

// Sign token
secretKey := []byte("secret")
tokenString, err := token.SignedString(secretKey)

Best Practices

Key Management

  • Never hardcode secret keys in your source code
  • Use appropriate key sizes:
    • HMAC: At least 256 bits (32 bytes)
    • RSA: At least 2048 bits
    • ECDSA: Use P-256, P-384, or P-521 curves
  • Store keys securely using environment variables or key management services
  • Rotate keys periodically

Claims Design

  • Always set expiration time (exp) to limit token validity
  • Use short-lived tokens for sensitive operations
  • Include iat (issued at) claim for token age verification
  • Set appropriate audience (aud) to prevent token reuse across services
  • Use structured claims (RegisteredClaims) for type safety

Algorithm Selection

  • HMAC (HS256/384/512): Simple, fast, symmetric - good for internal services
  • RSA (RS256/384/512): Asymmetric - when you need to distribute public keys
  • ECDSA (ES256/384/512): Asymmetric, smaller signatures than RSA
  • EdDSA: Modern, fast, small signatures - recommended for new projects

Error Handling

Always check for errors when signing tokens:

tokenString, err := token.SignedString(key)
if err != nil {
    // Handle errors appropriately
    // Common errors: invalid key type, signing failure
    return fmt.Errorf("failed to sign token: %w", err)
}

Common Errors

  • Invalid key type: Key type doesn't match the signing method
  • Hash unavailable: Required hash function not available (check crypto imports)
  • Signing failure: Underlying cryptographic operation failed