github.com/lestrrat-go/jwx/v2@v2.0.21/jwt/README.md (about) 1 # JWT [![Go Reference](https://pkg.go.dev/badge/github.com/lestrrat-go/jwx/v2/jwt.svg)](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwt) 2 3 Package jwt implements JSON Web Tokens as described in [RFC7519](https://tools.ietf.org/html/rfc7519). 4 5 * Convenience methods for oft-used keys ("aud", "sub", "iss", etc) 6 * Convenience functions to extract/parse from http.Request, http.Header, url.Values 7 * Ability to Get/Set arbitrary keys 8 * Conversion to and from JSON 9 * Generate signed tokens 10 * Verify signed tokens 11 * Extra support for OpenID tokens via [github.com/lestrrat-go/jwx/v2/jwt/openid](./jwt/openid) 12 13 How-to style documentation can be found in the [docs directory](../docs). 14 15 More examples are located in the examples directory ([jwt_example_test.go](../examples/jwt_example_test.go)) 16 17 # SYNOPSIS 18 19 ## Verify a signed JWT 20 21 ```go 22 token, err := jwt.Parse(payload, jwt.WithKey(alg, key)) 23 if err != nil { 24 fmt.Printf("failed to parse payload: %s\n", err) 25 } 26 ``` 27 28 ## Token Usage 29 30 ```go 31 func ExampleJWT() { 32 const aLongLongTimeAgo = 233431200 33 34 t := jwt.New() 35 t.Set(jwt.SubjectKey, `https://github.com/lestrrat-go/jwx/v2/jwt`) 36 t.Set(jwt.AudienceKey, `Golang Users`) 37 t.Set(jwt.IssuedAtKey, time.Unix(aLongLongTimeAgo, 0)) 38 t.Set(`privateClaimKey`, `Hello, World!`) 39 40 buf, err := json.MarshalIndent(t, "", " ") 41 if err != nil { 42 fmt.Printf("failed to generate JSON: %s\n", err) 43 return 44 } 45 46 fmt.Printf("%s\n", buf) 47 fmt.Printf("aud -> '%s'\n", t.Audience()) 48 fmt.Printf("iat -> '%s'\n", t.IssuedAt().Format(time.RFC3339)) 49 if v, ok := t.Get(`privateClaimKey`); ok { 50 fmt.Printf("privateClaimKey -> '%s'\n", v) 51 } 52 fmt.Printf("sub -> '%s'\n", t.Subject()) 53 54 key, err := rsa.GenerateKey(rand.Reader, 2048) 55 if err != nil { 56 log.Printf("failed to generate private key: %s", err) 57 return 58 } 59 60 { 61 // Signing a token (using raw rsa.PrivateKey) 62 signed, err := jwt.Sign(t, jwt.WithKey(jwa.RS256, key)) 63 if err != nil { 64 log.Printf("failed to sign token: %s", err) 65 return 66 } 67 _ = signed 68 } 69 70 { 71 // Signing a token (using JWK) 72 jwkKey, err := jwk.New(key) 73 if err != nil { 74 log.Printf("failed to create JWK key: %s", err) 75 return 76 } 77 78 signed, err := jwt.Sign(t, jwt.WithKey(jwa.RS256, jwkKey)) 79 if err != nil { 80 log.Printf("failed to sign token: %s", err) 81 return 82 } 83 _ = signed 84 } 85 } 86 ``` 87 88 ## OpenID Claims 89 90 `jwt` package can work with token types other than the default one. 91 For OpenID claims, use the token created by `openid.New()`, or 92 use the `jwt.WithToken(openid.New())`. If you need to use other specialized 93 claims, use `jwt.WithToken()` to specify the exact token type 94 95 ```go 96 func Example_openid() { 97 const aLongLongTimeAgo = 233431200 98 99 t := openid.New() 100 t.Set(jwt.SubjectKey, `https://github.com/lestrrat-go/jwx/v2/jwt`) 101 t.Set(jwt.AudienceKey, `Golang Users`) 102 t.Set(jwt.IssuedAtKey, time.Unix(aLongLongTimeAgo, 0)) 103 t.Set(`privateClaimKey`, `Hello, World!`) 104 105 addr := openid.NewAddress() 106 addr.Set(openid.AddressPostalCodeKey, `105-0011`) 107 addr.Set(openid.AddressCountryKey, `日本`) 108 addr.Set(openid.AddressRegionKey, `東京都`) 109 addr.Set(openid.AddressLocalityKey, `港区`) 110 addr.Set(openid.AddressStreetAddressKey, `芝公園 4-2-8`) 111 t.Set(openid.AddressKey, addr) 112 113 buf, err := json.MarshalIndent(t, "", " ") 114 if err != nil { 115 fmt.Printf("failed to generate JSON: %s\n", err) 116 return 117 } 118 fmt.Printf("%s\n", buf) 119 120 t2, err := jwt.Parse(buf, jwt.WithToken(openid.New())) 121 if err != nil { 122 fmt.Printf("failed to parse JSON: %s\n", err) 123 return 124 } 125 if _, ok := t2.(openid.Token); !ok { 126 fmt.Printf("using jwt.WithToken(openid.New()) creates an openid.Token instance") 127 return 128 } 129 } 130 ``` 131 132 # FAQ 133 134 ## Why is `jwt.Token` an interface? 135 136 In this package, `jwt.Token` is an interface. This is not an arbitrary choice: there are actual reason for the type being an interface. 137 138 We understand that if you are migrating from another library this may be a deal breaker, but we hope you can at least appreciate the fact that this was not done arbitrarily, and that there were real technical trade offs that were evaluated. 139 140 ### No uninitialized tokens 141 142 First and foremost, by making it an interface, you cannot use an uninitialized token: 143 144 ```go 145 var token1 jwt.Token // this is nil, you can't just start using this 146 if err := json.Unmarshal(data, &token1); err != nil { // so you can't do this 147 ... 148 } 149 150 // But you _can_ do this, and we _want_ you to do this so the object is properly initialized 151 token2 = jwt.New() 152 if err := json.Unmarshal(data, &token2); err != nil { // actually, in practice you should use jwt.Parse() 153 .... 154 } 155 ``` 156 157 ### But why does it need to be initialized? 158 159 There are several reasons, but one of the reasons is that I'm using a sync.Mutex to avoid races. We want this to be properly initialized. 160 161 The other reason is that we support custom claims out of the box. The `map[string]interface{}` container is initialized during new. This is important when checking for equality using reflect-y methods (akin to `reflect.DeepEqual`), because if you allowed zero values, you could end up with "empty" tokens, that actually differ. Consider the following: 162 163 ```go 164 // assume jwt.Token was s struct, not an interface 165 token1 := jwt.Token{ privateClaims: make(map[string]interface{}) } 166 token2 := jwt.Token{ privateClaims: nil } 167 ``` 168 169 These are semantically equivalent, but users would need to be aware of this difference when comparing values. By forcing the user to use a constructor, we can force a uniform empty state. 170 171 ### Standard way to store values 172 173 Unlike some other libraries, this library allows you to store standard claims and non-standard claims in the same token. 174 175 You _want_ to store standard claims in a properly typed field, which we do for fields like "iss", "nbf", etc. 176 But for non-standard claims, there is just no way of doing this, so we _have_ to use a container like `map[string]interface{}` 177 178 This means that if you allow direct access to these fields via a struct, you will have two different ways to access the claims, which is confusing: 179 180 ```go 181 tok.Issuer = ... 182 tok.PrivateClaims["foo"] = ... 183 ``` 184 185 So we want to hide where this data is stored, and use a standard method like `Set()` and `Get()` to store all the values. 186 At this point you are effectively going to hide the implementation detail from the user, so you end up with a struct like below, which is fundamentally not so different from providing just an interface{}: 187 188 ```go 189 type Token struct { 190 // unexported fields 191 } 192 193 func (tok *Token) Set(...) { ... } 194 ``` 195 196 ### Use of pointers to store values 197 198 We wanted to differentiate the state between a claim being uninitialized, and a claim being initialized to empty. 199 200 So we use pointers to store values: 201 202 ```go 203 type stdToken struct { 204 .... 205 issuer *string // if nil, uninitialized. if &(""), initialized to empty 206 } 207 ``` 208 209 This is fine for us, but we doubt that this would be something users would want to do. 210 This is a subtle difference, but cluttering up the API with slight variations of the same type (i.e. pointers vs non-pointers) seemed like a bad idea to us. 211 212 ```go 213 token.Issuer = &issuer // want to avoid this 214 215 token.Set(jwt.IssuerKey, "foobar") // so this is what we picked 216 ``` 217 218 This way users no longer need to care how the data is internally stored. 219 220 ### Allow more than one type of token through the same interface 221 222 `dgrijalva/jwt-go` does this in a different way, but we felt that it would be more intuitive for all tokens to follow a single interface so there is fewer type conversions required. 223 224 See the `openid` token for an example.