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.