This library has two goals.
- Make generating and validating JWTs as intuitive and easy as possible, with the ability to add complexity if the developer chooses
- Follow RFC7519 and implement the recommended cryptographic algorithms for both JWS and JWE
This library is extremely new. Integrate it with your applications at your own risk. Notably, the library could rapidly change in design - causing breaking changes.
If all you want to do is generate and validate a JWT, use these examples.
package main
import (
"fmt"
. "github.com/bmwadforth/jwt"
"log"
"time"
)
func main(){
key := []byte("Key")
claims := NewClaimSet()
claims.Add(string(Audience), "your_audience")
claims.Add(string(Subject), "your_subject")
claims.Add(string(IssuedAt), time.Now())
claims.Add("my_claim", "some_value")
//Create new HS256 token, set claims and key
token, err := New(HS256, claims, key)
if err != nil {
log.Fatal(err)
}
//Encode token
tokenBytes, err := token.Encode()
fmt.Println(string(tokenBytes))
//eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ5b3VyX2F1ZGllbmNlIiwiaWF0IjoiMjAyMC0wMS0wMlQyMTo1NTo1OS40MzE1ODErMTE6MDAiLCJteV9jbGFpbSI6InNvbWVfdmFsdWUiLCJzdWIiOiJ5b3VyX3N1YmplY3QifQ.PAR_a60R6VZakCmBZg8aMgt3eXDi-CMC4P4p08yJy-I
}
package main
import (
"fmt"
. "github.com/bmwadforth/jwt"
"log"
)
func main(){
key := []byte("Key")
tokenString := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ5b3VyX2F1ZGllbmNlIiwiaWF0IjoiMjAyMC0wMS0wMlQyMTo1NTo1OS40MzE1ODErMTE6MDAiLCJteV9jbGFpbSI6InNvbWVfdmFsdWUiLCJzdWIiOiJ5b3VyX3N1YmplY3QifQ.PAR_a60R6VZakCmBZg8aMgt3eXDi-CMC4P4p08yJy-I"
//Parse token string
token, err := Parse(tokenString, key)
if err != nil {
log.Fatal(err)
}
//Validate token
_, err = Validate(token)
if err != nil {
log.Fatal(err)
}
//Token is valid
}
If you would prefer to define your own JWS signing method, you can define your own signing function. Notably:
- The signing function will always receive a base64 encoded header and payload as the bytes to sign, per the JWS specification
- What you return from the signing function is base 64 encoded and attached to the signature component of the JWS
A good example of when you would want to implement your own signing function is when you want more control over how to sign your token. For example, RS256:
package main
import (
"fmt"
. "github.com/bmwadforth/jwt"
"io/ioutil"
"log"
"crypto/x509"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto"
"encoding/pem"
)
func main(){
b, _ := ioutil.ReadFile("./rsa_private.pem")
block, _ := pem.Decode(b)
key, _ := x509.ParsePKCS1PrivateKey(block.Bytes)
token, err := New(RS256, NewClaimSet(), block.Bytes)
if err != nil {
log.Fatal(err)
}
//Before calling sign, set SignFunc
token.SignFunc = func(t *Token, signingInput []byte) (bytes []byte, e error) {
// crypto/rand.Reader is a good source of entropy for blinding the RSA
// operation.
rng := rand.Reader
hashed := sha256.Sum256(signingInput)
signature, err := rsa.SignPKCS1v15(rng, key, crypto.SHA256, hashed[:])
if err != nil {
return nil, err
}
return signature, nil
}
signedBytes, err := token.Sign()
if err != nil {
log.Fatal(err)
}
fmt.Println(string(signedBytes))
//eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.e30.uZTBWMOdIYMlSxyJgGOgjPXwISnMDzLyiOE5k9GK2ruWc2IvWkOLtmZ9ECOwDqwLM93WH7CMIP7IEOMVZJzkHkFj16GgQnz-KSgY9MK8fBROij4R09XyXVRMvmBjVAyPxBS8dK9j-FuZIceu5TEN3-FmjcTq87OQfc3-mO6_3mruQfg59m9dSbcVL2SEQrRyrG-Jitkma7f_up8BSJHt0Q08ASVBivHjws2Z_QGYb3NkrI0oEcH_yoXlvJohsEQtNaycFLGNDtzujABHp9ZT5a2L-U8WCf8K9JwttGnuVTMhDviEjWC2M2weXAB8WimiwqQB2zER-4ILpbUhhL_MjA
}
Just as you can create a custom signing method, you can also create a custom validation method.
package main
import (
"fmt"
. "github.com/bmwadforth/jwt"
"io/ioutil"
"log"
"crypto/x509"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto"
"encoding/pem"
"encoding/base64"
)
func main(){
b, _ := ioutil.ReadFile("./rsa_private.pem")
block, _ := pem.Decode(b)
key, _ := x509.ParsePKCS1PrivateKey(block.Bytes)
tokenString := "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.e30.uZTBWMOdIYMlSxyJgGOgjPXwISnMDzLyiOE5k9GK2ruWc2IvWkOLtmZ9ECOwDqwLM93WH7CMIP7IEOMVZJzkHkFj16GgQnz-KSgY9MK8fBROij4R09XyXVRMvmBjVAyPxBS8dK9j-FuZIceu5TEN3-FmjcTq87OQfc3-mO6_3mruQfg59m9dSbcVL2SEQrRyrG-Jitkma7f_up8BSJHt0Q08ASVBivHjws2Z_QGYb3NkrI0oEcH_yoXlvJohsEQtNaycFLGNDtzujABHp9ZT5a2L-U8WCf8K9JwttGnuVTMhDviEjWC2M2weXAB8WimiwqQB2zER-4ILpbUhhL_MjA"
token, err := Parse(tokenString, b)
if err != nil {
log.Fatal(err)
}
//Before calling validate, set ValidateFunc
token.ValidateFunc = func(t *Token) (b bool, e error) {
headerB64, _ := t.Header.ToBase64()
payloadB64, _ := t.Payload.ToBase64()
hashed := sha256.Sum256([]byte(fmt.Sprintf("%s.%s", headerB64, payloadB64)))
decodedSignature, err := base64.RawURLEncoding.DecodeString(string(t.Signature.Raw))
if err != nil {
return false, err
}
err = rsa.VerifyPKCS1v15(&key.PublicKey, crypto.SHA256, hashed[:], decodedSignature)
if err != nil {
return false, err
}
return true, nil
}
_, err = token.Validate()
if err != nil {
log.Fatal(err)
}
//Token is valid
}
This library currently supports JWS only.
"alg" Param | Digital Signature/MAC Algorithm | Implementation Requirement | Implemented |
---|---|---|---|
HS256 | HMAC using SHA-256 | Required | ✅ |
HS384 | HMAC using SHA-384 | Optional | ❌ |
HS512 | HMAC using SHA-512 | Optional | ❌ |
RS256 | RSASSA-PKCS1-v1_5 using SHA-256 | Recommended | ✅ |
RS384 | RSASSA-PKCS1-v1_5 using SHA-384 | Optional | ❌ |
RS512 | RSASSA-PKCS1-v1_5 using SHA-512 | Optional | ❌ |
ES256 | ECDSA using P-256 and SHA-256 | Recommended+ | ❌ |
ES384 | ECDSA using P-384 and SHA-384 | Optional | ❌ |
ES512 | ECDSA using P-521 and SHA-512 | Optional | ❌ |
PS256 | RSASSA-PSS using SHA-256 and MGF1 with SHA-256 | Optional | ❌ |
PS384 | RSASSA-PSS using SHA-384 and MGF1 with SHA-384 | Optional | ❌ |
PS512 | RSASSA-PSS using SHA-512 and MGF1 with SHA-512 | Optional | ❌ |
none | No digital signature or MAC performed | Optional | ✅ |
JWE has not been implemented in this library yet