go-sample-webpage/vendor/github.com/lestrrat-go/jwx/jwt/README.md

223 lines
7.4 KiB
Markdown
Raw Normal View History

2021-11-04 01:14:51 +00:00
# JWT [![Go Reference](https://pkg.go.dev/badge/github.com/lestrrat-go/jwx/jwt.svg)](https://pkg.go.dev/github.com/lestrrat-go/jwx/jwt)
Package jwt implements JSON Web Tokens as described in [RFC7519](https://tools.ietf.org/html/rfc7519).
* Convenience methods for oft-used keys ("aud", "sub", "iss", etc)
* Convenience functions to extract/parse from http.Request, http.Header, url.Values
* Ability to Get/Set arbitrary keys
* Conversion to and from JSON
* Generate signed tokens
* Verify signed tokens
* Extra support for OpenID tokens via [github.com/lestrrat-go/jwx/jwt/openid](./jwt/openid)
More examples are located in the examples directory ([jwt_example_test.go](../examples/jwt_example_test.go))
# SYNOPSIS
## Verify a signed JWT
```go
token, err := jwt.Parse(bytes.NewReader(payload), jwt.WithKeySet(keyset))
if err != nil {
fmt.Printf("failed to parse payload: %s\n", err)
}
```
## Token Usage
```go
func ExampleJWT() {
const aLongLongTimeAgo = 233431200
t := jwt.New()
t.Set(jwt.SubjectKey, `https://github.com/lestrrat-go/jwx/jwt`)
t.Set(jwt.AudienceKey, `Golang Users`)
t.Set(jwt.IssuedAtKey, time.Unix(aLongLongTimeAgo, 0))
t.Set(`privateClaimKey`, `Hello, World!`)
buf, err := json.MarshalIndent(t, "", " ")
if err != nil {
fmt.Printf("failed to generate JSON: %s\n", err)
return
}
fmt.Printf("%s\n", buf)
fmt.Printf("aud -> '%s'\n", t.Audience())
fmt.Printf("iat -> '%s'\n", t.IssuedAt().Format(time.RFC3339))
if v, ok := t.Get(`privateClaimKey`); ok {
fmt.Printf("privateClaimKey -> '%s'\n", v)
}
fmt.Printf("sub -> '%s'\n", t.Subject())
key, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
log.Printf("failed to generate private key: %s", err)
return
}
{
// Signing a token (using raw rsa.PrivateKey)
signed, err := jwt.Sign(t, jwa.RS256, key)
if err != nil {
log.Printf("failed to sign token: %s", err)
return
}
_ = signed
}
{
// Signing a token (using JWK)
jwkKey, err := jwk.New(key)
if err != nil {
log.Printf("failed to create JWK key: %s", err)
return
}
signed, err := jwt.Sign(t, jwa.RS256, jwkKey)
if err != nil {
log.Printf("failed to sign token: %s", err)
return
}
_ = signed
}
}
```
## OpenID Claims
`jwt` package can work with token types other than the default one.
For OpenID claims, use the token created by `openid.New()`, or
use the `jwt.WithToken(openid.New())`. If you need to use other specialized
claims, use `jwt.WithToken()` to specify the exact token type
```go
func Example_openid() {
const aLongLongTimeAgo = 233431200
t := openid.New()
t.Set(jwt.SubjectKey, `https://github.com/lestrrat-go/jwx/jwt`)
t.Set(jwt.AudienceKey, `Golang Users`)
t.Set(jwt.IssuedAtKey, time.Unix(aLongLongTimeAgo, 0))
t.Set(`privateClaimKey`, `Hello, World!`)
addr := openid.NewAddress()
addr.Set(openid.AddressPostalCodeKey, `105-0011`)
addr.Set(openid.AddressCountryKey, `日本`)
addr.Set(openid.AddressRegionKey, `東京都`)
addr.Set(openid.AddressLocalityKey, `港区`)
addr.Set(openid.AddressStreetAddressKey, `芝公園 4-2-8`)
t.Set(openid.AddressKey, addr)
buf, err := json.MarshalIndent(t, "", " ")
if err != nil {
fmt.Printf("failed to generate JSON: %s\n", err)
return
}
fmt.Printf("%s\n", buf)
t2, err := jwt.ParseBytes(buf, jwt.WithToken(openid.New())
if err != nil {
fmt.Printf("failed to parse JSON: %s\n", err)
return
}
if _, ok := t2.(openid.Token); !ok {
fmt.Printf("using jwt.WithToken(openid.New()) creates an openid.Token instance")
return
}
}
```
# FAQ
## Why is `jwt.Token` an interface?
In this package, `jwt.Token` is an interface. This is not an arbitrary choice: there are actual reason for the type being an interface.
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.
### No uninitialized tokens
First and foremost, by making it an interface, you cannot use an uninitialized token:
```go
var token1 jwt.Token // this is nil, you can't just start using this
if err := json.Unmarshal(data, &token1); err != nil { // so you can't do this
...
}
// But you _can_ do this, and we _want_ you to do this so the object is properly initialized
token2 = jwt.New()
if err := json.Unmarshal(data, &token2); err != nil { // actually, in practice you should use jwt.Parse()
....
}
```
### But why does it need to be initialized?
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.
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:
```go
// assume jwt.Token was s struct, not an interface
token1 := jwt.Token{ privateClaims: make(map[string]interface{}) }
token2 := jwt.Token{ privateClaims: nil }
```
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.
### Standard way to store values
Unlike some other libraries, this library allows you to store standard claims and non-standard claims in the same token.
You _want_ to store standard claims in a properly typed field, which we do for fields like "iss", "nbf", etc.
But for non-standard claims, there is just no way of doing this, so we _have_ to use a container like `map[string]interface{}`
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:
```go
tok.Issuer = ...
tok.PrivateClaims["foo"] = ...
```
So we want to hide where this data is stored, and use a standard method like `Set()` and `Get()` to store all the values.
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{}:
```go
type Token struct {
// unexported fields
}
func (tok *Token) Set(...) { ... }
```
### Use of pointers to store values
We wanted to differentiate the state between a claim being uninitialized, and a claim being initialized to empty.
So we use pointers to store values:
```go
type stdToken struct {
....
issuer *string // if nil, uninitialized. if &(""), initialized to empty
}
```
This is fine for us, but we doubt that this would be something users would want to do.
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.
```go
token.Issuer = &issuer // want to avoid this
token.Set(jwt.IssuerKey, "foobar") // so this is what we picked
```
This way users no longer need to care how the data is internally stored.
### Allow more than one type of token through the same interface
`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.
See the `openid` token for an example.