jwtx

package module
v1.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 29, 2026 License: Apache-2.0 Imports: 16 Imported by: 0

README

jwtx-go

A lightweight, secure JWT library for Go — EdDSA & ES256 only.

Go Reference License Go Version Coverage Zero External Dependencies


English · 中文


jwtx-go

jwtx-go is a lightweight JWT library for Go that prioritizes security, simplicity, and zero external runtime dependencies.

Why jwtx-go?

Most JWT libraries support a dozen algorithms — many of which introduce unnecessary attack surface. jwtx-go takes a different philosophy:

Only Two Asymmetric Algorithms
Algorithm Curve Go Standard Library Security Rationale
EdDSA Ed25519 crypto/ed25519 Modern, constant-time, fast, side-channel resistant
ES256 P-256 crypto/ecdsa Widely standardized, FIPS-compliant curve
  • No symmetric (HS) algorithms* — eliminates algorithm confusion attacks (CVE-2015-9235, CVE-2016-5431)
  • No RSA — avoids large key sizes, slow signing, and padding oracle risks
  • No exotic curves — only battle-tested, standard library backed curves
  • 100% Go standard library — zero external runtime dependencies
Key ID (Kid) — Built-in Algorithm Routing

jwtx-go uses a numeric Kid in the range 1100–2999 to encode both algorithm selection and capabilities:

Kid Range    Algorithm
1100–1999 →  EdDSA (Ed25519)
2100–2999 →  ES256 (ECDSA P-256)
  • The last digit controls additional behavior:
    • Kid % 10 == 1 — allows CanRefresh in SubConfig, enables ChangeIssuer()
    • Kid % 10 == 0 — rejected as invalid
  • This built-in routing prevents algorithm mix-ups and key confusion at the application level

Custom validation — You can implement your own Kid validation by replacing the Kid field before calling GenJwt() or ParseJwt(), or by extending the library to add your own range checks and business logic on top of the built-in enforcement.

Zero External Runtime Dependencies
go.mod: require github.com/stretchr/testify v1.11.1  // test only

Only testify is needed for testing — your production builds carry zero third-party dependencies.

Features

  • ✅ JWT generation and parsing with EdDSA (Ed25519) and ES256 (ECDSA P-256)
  • ✅ PEM key parsing for both Ed25519 and ECDSA keys
  • ✅ Built-in Key ID (Kid) routing and validation
  • ChangeIssuer() with safe copy semantics
  • ✅ Fast path JWT string pre-check (CheckJwtStr)
  • ✅ Auto-expiry with TTL support
  • ✅ Configurable SubConfig payload (AttendeeId, CanRefresh, OpenId)
  • ✅ No external dependencies in production
  • ✅ High test coverage (98.9%)

Installation

go get github.com/fabletang/jwtx-go

Requires Go 1.21 or later.

Quick Start

package main

import (
    "crypto/ed25519"
    "time"
    "github.com/fabletang/jwtx-go"
)

func main() {
    // Generate Ed25519 key
    pub, pri, _ := ed25519.GenerateKey(nil)

    // Create a token
    m := &jwtx.JwtX{
        Kid:    1101,         // EdDSA
        Id:     12345,
        PriKey: pri,
        PubKey: pub,
        Exp:    time.Now().Add(5 * time.Minute),
        Sub:    jwtx.SubConfig{OpenId: "user_abc", CanRefresh: true},
        Issuer: 1,
    }

    token, err := m.GenJwt()
    if err != nil { panic(err) }

    // Parse & verify
    result, err := m.ParseJwt(token)
    if err != nil { panic(err) }
    println(result.Sub.OpenId) // "user_abc"
}

API Overview

Core Types
Type Description
JwtX Main JWT struct with all parameters
SubConfig Custom payload (AttendeeId, OpenId, CanRefresh)
JwtHeader JWT header (Alg, Kid, Typ)
EcdsaKey Parsed ECDSA key pair
Ed25519Key Parsed Ed25519 key pair
Key Functions
Function Description
GenJwt() Generate a signed JWT string
ParseJwt() Parse and verify a JWT string
ChangeIssuer() Create a new token with a different issuer
CheckJwtStr() Fast pre-check on JWT format (no crypto)
GetEd25519Key() Parse Ed25519 PEM keys
GetEcdsaKey() Parse ECDSA PEM keys (PKCS1 or PKCS8)

Benchmark Results

goos: darwin
goarch: arm64
cpu: Apple M1 Pro
BenchmarkGenJwt_Ed25519-10             23.5 µs/op    2738 B/op    37 allocs/op
BenchmarkGenJwt_ECDSA-10               26.3 µs/op    8961 B/op    98 allocs/op
BenchmarkParseJwt_Ed25519-10           48.8 µs/op    3232 B/op    61 allocs/op
BenchmarkParseJwt_ECDSA-10             62.2 µs/op    4448 B/op    82 allocs/op
BenchmarkCheckJwtStr-10                 0.65 µs/op    368 B/op     9 allocs/op
BenchmarkParseWithClaims_Ed25519-10    47.0 µs/op    2248 B/op    41 allocs/op
BenchmarkSign_Ed25519-10               19.9 µs/op     168 B/op     5 allocs/op
BenchmarkVerify_Ed25519-10             43.4 µs/op       0 B/op     0 allocs/op

Run benchmarks on your own hardware:

go test -bench=. -benchmem -count=5 ./...

License

Apache 2.0. See LICENSE.


jwtx-go

jwtx-go 是一个轻量级 Go JWT 库,专注于安全性、简洁性,且生产环境零外部依赖。

为什么选择 jwtx-go?

大多数 JWT 库支持十几种算法,其中许多引入了不必要的攻击面。jwtx-go 采用不同的理念:

仅两种非对称算法
算法 曲线 Go 标准库 安全理由
EdDSA Ed25519 crypto/ed25519 现代、常量时间、快速、抗侧信道攻击
ES256 P-256 crypto/ecdsa 广泛标准化、FIPS 兼容曲线
  • 不实现对称 (HS*) 算法 — 彻底消除算法混淆攻击(CVE-2015-9235、CVE-2016-5431)
  • 不实现 RSA — 避免大密钥、慢签名和填充预言攻击风险
  • 不实现另类曲线 — 仅采用经过实战检验的标准库曲线
  • 100% Go 标准库 — 生产环境零外部依赖
Key ID (Kid) — 内置算法路由

jwtx-go 使用 1100–2999 范围的数值型 Kid,同时编码算法选择和功能特性:

Kid 范围      算法
1100–1999 →  EdDSA (Ed25519)
2100–2999 →  ES256 (ECDSA P-256)
  • 个位数控制额外行为:
    • Kid % 10 == 1 — 允许 SubConfig 中的 CanRefresh,启用 ChangeIssuer()
    • Kid % 10 == 0 — 拒绝,视为无效
  • 内置路由避免了应用层的算法误配和密钥混淆

自定义验证 — 您可以通过在调用 GenJwt()ParseJwt() 之前自行设置 Kid 字段来实现自定义校验逻辑,或在内置规则之上添加自己的范围检查与业务逻辑。

零外部运行时依赖
go.mod: require github.com/stretchr/testify v1.11.1  // 仅测试使用

仅 testify 用于测试,生产构建第三方依赖。

功能特性

  • ✅ JWT 签发与解析,支持 EdDSA (Ed25519) 和 ES256 (ECDSA P-256)
  • ✅ PEM 密钥解析,支持 Ed25519 和 ECDSA
  • ✅ 内置 Key ID (Kid) 路由与校验
  • ChangeIssuer() 安全复制语义
  • ✅ JWT 字符串快速预检(CheckJwtStr,不涉及密码运算)
  • ✅ 自动过期与 TTL 支持
  • ✅ 可配置 SubConfig 载荷(AttendeeId、CanRefresh、OpenId)
  • ✅ 生产环境无外部依赖
  • ✅ 高测试覆盖率(98.9%)

安装

go get github.com/fabletang/jwtx-go

需要 Go 1.21 或更高版本。

快速开始

package main

import (
    "crypto/ed25519"
    "time"
    "github.com/fabletang/jwtx-go"
)

func main() {
    // 生成 Ed25519 密钥
    pub, pri, _ := ed25519.GenerateKey(nil)

    // 创建令牌
    m := &jwtx.JwtX{
        Kid:    1101,         // EdDSA
        Id:     12345,
        PriKey: pri,
        PubKey: pub,
        Exp:    time.Now().Add(5 * time.Minute),
        Sub:    jwtx.SubConfig{OpenId: "user_abc", CanRefresh: true},
        Issuer: 1,
    }

    token, err := m.GenJwt()
    if err != nil { panic(err) }

    // 解析与验证
    result, err := m.ParseJwt(token)
    if err != nil { panic(err) }
    println(result.Sub.OpenId) // "user_abc"
}

API 概览

核心类型
类型 说明
JwtX 主 JWT 结构体,包含所有参数
SubConfig 自定义载荷(AttendeeId, OpenId, CanRefresh)
JwtHeader JWT 头部(Alg, Kid, Typ)
EcdsaKey 已解析的 ECDSA 密钥对
Ed25519Key 已解析的 Ed25519 密钥对
关键函数
函数 说明
GenJwt() 生成签名后的 JWT 字符串
ParseJwt() 解析并验证 JWT 字符串
ChangeIssuer() 创建具有不同签发者的新令牌
CheckJwtStr() JWT 格式快速预检(不涉及密码运算)
GetEd25519Key() 解析 Ed25519 PEM 密钥
GetEcdsaKey() 解析 ECDSA PEM 密钥(支持 PKCS1 或 PKCS8)

性能测试结果

goos: darwin
goarch: arm64
cpu: Apple M1 Pro
BenchmarkGenJwt_Ed25519-10             23.5 µs/op    2738 B/op    37 allocs/op
BenchmarkGenJwt_ECDSA-10               26.3 µs/op    8961 B/op    98 allocs/op
BenchmarkParseJwt_Ed25519-10           48.8 µs/op    3232 B/op    61 allocs/op
BenchmarkParseJwt_ECDSA-10             62.2 µs/op    4448 B/op    82 allocs/op
BenchmarkCheckJwtStr-10                 0.65 µs/op    368 B/op     9 allocs/op
BenchmarkParseWithClaims_Ed25519-10    47.0 µs/op    2248 B/op    41 allocs/op
BenchmarkSign_Ed25519-10               19.9 µs/op     168 B/op     5 allocs/op
BenchmarkVerify_Ed25519-10             43.4 µs/op       0 B/op     0 allocs/op

在自己的机器上运行性能测试:

go test -bench=. -benchmem -count=5 ./...

开源协议

Apache 2.0。详见 LICENSE

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrPriKeyInavailable = errors.New("private key inavailable")
	ErrPubKeyInavailable = errors.New("public key inavailable")
	ErrExpLost           = errors.New("expiry date lost")
	ErrIatLost           = errors.New("date of issue lost")
	ErrKidIllegal        = errors.New("Kid should between 1100 to 2999")
	ErrJwtInavailable    = errors.New("JWT inavailable")
	ErrJwtIllegal        = errors.New("JWT illegal")
	ErrJwtChangeIssuer   = errors.New("JWT is not allow to change issuer")
)

Functions

This section is empty.

Types

type ClaimStrings

type ClaimStrings []string

type Claims

type Claims interface {
	GetExpirationTime() (*NumericDate, error)
	GetIssuedAt() (*NumericDate, error)
	GetNotBefore() (*NumericDate, error)
	GetIssuer() (string, error)
	GetSubject() (string, error)
	GetAudience() (ClaimStrings, error)
}

type EcdsaKey

type EcdsaKey struct {
	PubKey *ecdsa.PublicKey
	PriKey *ecdsa.PrivateKey
}

func GetEcdsaKey

func GetEcdsaKey(priStr string, pubStr string) (key *EcdsaKey, err error)

type Ed25519Key

type Ed25519Key struct {
	PubKey ed25519.PublicKey
	PriKey ed25519.PrivateKey
}

func GetEd25519Key

func GetEd25519Key(priStr string, pubStr string) (key *Ed25519Key, err error)

type JwtHeader

type JwtHeader struct {
	Alg string `json:"alg"`
	Kid int16  `json:"kid"`
	Typ string `json:"typ"`
}

func CheckJwtStr

func CheckJwtStr(jwtStr string) (header JwtHeader, err error)

type JwtX

type JwtX struct {
	Kid    int16
	Id     int64
	Exp    time.Time
	Iat    time.Time
	Nob    time.Time
	Sub    SubConfig
	PriKey interface{} `json:"-"`
	PubKey interface{} `json:"-"`
	TTL    int
	Issuer int64
	// contains filtered or unexported fields
}

func (*JwtX) ChangeIssuer

func (m *JwtX) ChangeIssuer(issuer int64) (rs string, err error)

func (*JwtX) GenJwt

func (m *JwtX) GenJwt() (jwtStr string, err error)

func (*JwtX) ParseJwt

func (m *JwtX) ParseJwt(jwtStr string) (result *JwtX, err error)

type Keyfunc

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

type NumericDate

type NumericDate struct {
	time.Time
}

func NewNumericDate

func NewNumericDate(t time.Time) *NumericDate

func (NumericDate) MarshalJSON

func (n NumericDate) MarshalJSON() ([]byte, error)

func (*NumericDate) UnmarshalJSON

func (n *NumericDate) UnmarshalJSON(b []byte) error

type RegisteredClaims

type RegisteredClaims struct {
	Issuer    string       `json:"iss,omitempty"`
	Subject   string       `json:"sub,omitempty"`
	Audience  ClaimStrings `json:"aud,omitempty"`
	ExpiresAt *NumericDate `json:"exp,omitempty"`
	NotBefore *NumericDate `json:"nbf,omitempty"`
	IssuedAt  *NumericDate `json:"iat,omitempty"`
	ID        string       `json:"jti,omitempty"`
}

func (RegisteredClaims) GetAudience

func (c RegisteredClaims) GetAudience() (ClaimStrings, error)

func (RegisteredClaims) GetExpirationTime

func (c RegisteredClaims) GetExpirationTime() (*NumericDate, error)

func (RegisteredClaims) GetIssuedAt

func (c RegisteredClaims) GetIssuedAt() (*NumericDate, error)

func (RegisteredClaims) GetIssuer

func (c RegisteredClaims) GetIssuer() (string, error)

func (RegisteredClaims) GetNotBefore

func (c RegisteredClaims) GetNotBefore() (*NumericDate, error)

func (RegisteredClaims) GetSubject

func (c RegisteredClaims) GetSubject() (string, error)

type SigningMethod

type SigningMethod interface {
	Verify(signingString string, sig []byte, key any) error
	Sign(signingString string, key any) ([]byte, error)
	Alg() string
}

type SigningMethodECDSA

type SigningMethodECDSA struct {
	Name      string
	KeySize   int
	CurveBits int
}

func (*SigningMethodECDSA) Alg

func (m *SigningMethodECDSA) Alg() string

func (*SigningMethodECDSA) Sign

func (m *SigningMethodECDSA) Sign(signingString string, key any) ([]byte, error)

func (*SigningMethodECDSA) Verify

func (m *SigningMethodECDSA) Verify(signingString string, sig []byte, key any) error

type SigningMethodEd25519

type SigningMethodEd25519 struct{}

func (*SigningMethodEd25519) Alg

func (m *SigningMethodEd25519) Alg() string

func (*SigningMethodEd25519) Sign

func (m *SigningMethodEd25519) Sign(signingString string, key any) ([]byte, error)

func (*SigningMethodEd25519) Verify

func (m *SigningMethodEd25519) Verify(signingString string, sig []byte, key any) error

type SubConfig

type SubConfig struct {
	AttendeeId int64  `json:"attendeeId,string"`
	CanRefresh bool   `json:"canRefresh"`
	OpenId     string `json:"openId"`
}

type Token

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

func NewWithClaims

func NewWithClaims(method SigningMethod, claims Claims) *Token

func ParseWithClaims

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

func (*Token) SignedString

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

func (*Token) SigningString

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

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL