cristalhq/jwt

repository·main·Indexed 20 days ago

https://github.com/cristalhq/jwt

A high-performance, dependency-free JSON Web Token (JWT) library for Go implementing RFC 7519. Designed to be memory and CPU conservative with a concurrent-safe API, it supports HMAC (HS), RSA (RS), RSA-PSS (PS), ECDSA (ES), and EdDSA algorithms. The library provides tools for building, parsing, and verifying tokens through Signer and Verifier interfaces, and allows for the implementation of custom algorithms.

Tokens
7.9K
Snippets
36
Records
45
Agent score
71%

What's inside cristalhq/jwt

  1. Use secure key lengths for HMAC (HS) algorithms

    main

    When using HMAC (HS) algorithms, ensure your signing keys are sufficiently long to prevent brute-force attacks. While short keys are often used in tests or examples for clarity, production environments require high-entropy, long keys.

    To generate a secure key, use the jwt.GenerateRandom512Bit function.

    // Use this to generate a secure key for production
    key := jwt.GenerateRandom512Bit()
  2. Create a JWT using the Builder pattern

    main

    To create a new JSON Web Token (JWT), use the NewBuilder function followed by the Build method.

    1. Initialize: Call NewBuilder(signer, ...opts) where signer is an implementation of the Signer interface. You can optionally provide BuilderOption functions to configure the header.
    2. Build: Call Build(claims) on the builder instance. The claims argument can be a struct (which will be JSON marshaled), a string, or a []byte (treated as pre-marshaled JSON).

    The Builder is safe for concurrent use.

    // Assuming 'signer' is already implemented
    builder := jwt.NewBuilder(signer, jwt.WithKeyID("my-key-id"))
    
    type MyClaims struct {
    	Sub string `json:"sub"`
    }
    
    token, err := builder.Build(MyClaims{Sub: "12345"})
    if err != nil {
    	// handle error
    }
  3. Build a new JWT token

    main

    To create a new token, you must first create a Signer using a specific algorithm (e.g., NewSignerHS for HMAC), define your claims (such as RegisteredClaims), and use a Builder to assemble the token.

    // create a Signer (HMAC in this example)
    key := []byte(`secret`)
    signer, err := jwt.NewSignerHS(jwt.HS256, key)
    checkErr(err)
    
    // create claims (you can create your own, see: ExampleBuilder_withUserClaims)
    claims := &jwt.RegisteredClaims{
        Audience: []string{"admin"},
        ID:       "random-unique-string",
    }
    
    // create a Builder
    builder := jwt.NewBuilder(signer)
    
    // and build a Token
    token, err := builder.Build(claims)
    checkErr(err)
    
    // here is token as a string
    var _ string = token.String()
  4. Parse and verify a JWT token

    main

    To validate a token, create a Verifier with the appropriate key and algorithm. You can then parse the token bytes into a new token object, verify its signature, and extract the claims. The library provides methods to parse the full token or just the claims directly.

    // create a Verifier (HMAC in this example)
    key := []byte(`secret`)
    verifier, err := jwt.NewVerifierHS(jwt.HS256, key)
    checkErr(err)
    
    // parse and verify a token
    tokenBytes := token.Bytes()
    newToken, err := jwt.Parse(tokenBytes, verifier)
    checkErr(err)
    
    // or just verify it's signature
    err = verifier.Verify(newToken)
    checkErr(err)
    
    // get Registered claims
    var newClaims jwt.RegisteredClaims
    errClaims := json.Unmarshal(newToken.Claims(), &newClaims)
    checkErr(errClaims)
    
    // or parse only claims
    errParseClaims := jwt.ParseClaims(tokenBytes, verifier, &newClaims)
    checkErr(errParseClaims)
    
    // verify claims as you wish
    var _ bool = newClaims.IsForAudience("admin")
    var _ bool = newClaims.IsValidAt(time.Now())
  5. Handle JWT parsing errors

    main

    When using Parse, ParseClaims, or ParseNoVerify, the library may return ErrInvalidFormat. This typically occurs if:

    • The input does not start with the expected JWT prefix (eyJ).
    • The token does not contain the required two dots (.) separating header, claims, and signature.
    • The base64 encoding or JSON structure is malformed.
  6. Sign a payload using RSAlg.Sign

    main

    The Sign method on an RSAlg instance generates a digital signature for the provided byte slice payload using the configured RSA private key and PKCS#1 v1.5 padding.

    signature, err := signer.Sign(payload)
    if err != nil {
    	// handle error
    }
  7. Create an RSA signer with NewSignerRS

    main

    Use NewSignerRS to create a signer for RSA-based algorithms (RS256, RS384, or RS512). This requires an Algorithm type and a pointer to an rsa.PrivateKey. The signer uses PKCS#1 v1.5 padding.

    Supported algorithms:

    • RS256 (SHA-256)
    • RS384 (SHA-384)
    • RS512 (SHA-512)
    import (
    	"crypto/rsa"
    	"github.com/cristalhq/cristalhq/jwt"
    )
    
    // Assuming 'key' is an existing *rsa.PrivateKey
    signer, err := jwt.NewSignerRS(jwt.RS256, key)
    if err != nil {
    	// handle error
    }
  8. Decode a JWT without verification using ParseNoVerify()

    main

    Use ParseNoVerify to decode a raw JWT byte slice into a *Token without checking the signature.

    Warning: This method does not guarantee the token is authentic or has not been tampered with. Only use this if you specifically need to inspect the header or claims before deciding whether to verify the signature.

    token, err := jwt.ParseNoVerify(rawTokenBytes)
    if err != nil {
    	// handle error
    }
    // token is decoded but NOT verified
  9. Create an EdDSA verifier with NewVerifierEdDSA

    main

    Use NewVerifierEdDSA to create a verifier instance for EdDSA (Ed25519) signatures. You must provide a valid ed25519.PublicKey.

    Errors:

    • ErrNilKey: if the provided key is empty.
    • ErrInvalidKey: if the key length does not match ed25519.PublicKeySize.
    import (
    	"crypto/ed25519"
    	"github.com/cristalhq/cristalhq/jwt"
    )
    
    // Assuming you have a valid ed25519 public key
    verifier, err := jwt.NewVerifierEdDSA(publicKey)
    if err != nil {
    	// handle error
    }
  10. Validate RegisteredClaims temporal validity

    main

    Use the following methods to check if a token is valid relative to a specific point in time (now).

    • IsValidAt(now time.Time) bool: A composite check that returns true only if the token satisfies ExpiresAt, NotBefore, and IssuedAt constraints.
    • IsValidExpiresAt(now time.Time) bool: Returns true if ExpiresAt is nil or if the expiration time is strictly after now.
    • IsValidNotBefore(now time.Time) bool: Returns true if NotBefore is nil or if the not-before time is strictly before now.
    • IsValidIssuedAt(now time.Time) bool: Returns true if IssuedAt is nil or if the issued-at time is strictly before now.