# JWT in Golang

December 19, 2025 Golang JWT Back-end

JWTs are a great alternative to sessions and cookies. This article walks through usage of JWTs in Golang.

Creating tokens

package authToken

import (
	"fmt"
	"time"

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

type TokenPayload struct {
	Id    string
	Email string
	Role  string
}

type JwtConfig struct {
	Issuer string
	Secret string
	Expiry time.Duration
}

func CreateToken(payload *TokenPayload, config *JwtConfig) (string, error) {
	claims := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
		"userId": payload.Id,
		"sub":    payload.Email,
		"iss":    config.Issuer,
		"aud":    payload.Role,
		"exp":    time.Now().Add(config.Expiry).Unix(),
		"iat":    time.Now().Unix(), // Issued at
	})

	tokenString, err := claims.SignedString([]byte(config.Secret))
	if err != nil {
		return "", err
	}

	return tokenString, nil
}

Note: Generated tokens can be inspected using this online tool https://www.jwt.io/.

Verifying tokens

func VerifyToken(tokenString string, config *JwtConfig) (*TokenPayload, error) {
	token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
		return []byte(config.Secret), nil
	})

	if err != nil {
		return nil, err
	}

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

	email, err := token.Claims.GetSubject()
	if err != nil {
		return nil, err
	}

	role, err := token.Claims.GetAudience()
	if err != nil || len(role) == 0 {
		return nil, err
	}

	claimsMap, ok := token.Claims.(jwt.MapClaims)
	if !ok {
		return nil, fmt.Errorf("invalid token claims")
	}

	userId, ok := claimsMap["userId"].(string)
	if !ok {
		return nil, fmt.Errorf("invalid token claims")
	}

	expiry, err := token.Claims.GetExpirationTime()
	if err != nil {
		return nil, err
	}

	if time.Now().After(expiry.Time) {
		return nil, fmt.Errorf("token expired")
	}

	tokenPayload := TokenPayload{
		Id:    userId,
		Email: email,
		Role:  role[0],
	}

	return &tokenPayload, nil
}

Testing functionality

package authToken

import (
	"testing"
	"time"

	"github.com/google/uuid"
	"github.com/stretchr/testify/assert"
)

func TestTokenCreateVerify(t *testing.T) {
	testUser := TokenPayload{
		Id:    uuid.NewString(),
		Email: "some-test-user@site.com",
		Role:  "ADMIN",
	}

	config := JwtConfig{
		Issuer: "sample.com",
		Secret: "some-super-secret-token",
		Expiry: time.Hour,
	}

	token, err := CreateToken(&testUser, &config)
	assert.NoError(t, err)
	assert.NotEqual(t, token, "")

	payload, err := VerifyToken(token, &config)
	assert.NoError(t, err)
	assert.Equal(t, payload.Id, testUser.Id)
	assert.Equal(t, payload.Email, testUser.Email)
	assert.Equal(t, payload.Role, testUser.Role)
}