435c0d9fc8
This PR switches the Nomad repository from using govendor to Go modules for managing dependencies. Aspects of the Nomad workflow remain pretty much the same. The usual Makefile targets should continue to work as they always did. The API submodule simply defers to the parent Nomad version on the repository, keeping the semantics of API versioning that currently exists.
109 lines
3.3 KiB
Go
109 lines
3.3 KiB
Go
package jwt
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// TimeFunc provides the current time when parsing token to validate "exp" claim (expiration time).
|
|
// You can override it to use another time value. This is useful for testing or if your
|
|
// server uses a different time zone than your tokens.
|
|
var TimeFunc = time.Now
|
|
|
|
// Parse methods use this callback function to supply
|
|
// the key for verification. The function receives the parsed,
|
|
// but unverified Token. This allows you to use properties in the
|
|
// Header of the token (such as `kid`) to identify which key to use.
|
|
type Keyfunc func(*Token) (interface{}, error)
|
|
|
|
// A JWT Token. Different fields will be used depending on whether you're
|
|
// creating or parsing/verifying a token.
|
|
type Token struct {
|
|
Raw string // The raw token. Populated when you Parse a token
|
|
Method SigningMethod // The signing method used or to be used
|
|
Header map[string]interface{} // The first segment of the token
|
|
Claims Claims // The second segment of the token
|
|
Signature string // The third segment of the token. Populated when you Parse a token
|
|
Valid bool // Is the token valid? Populated when you Parse/Verify a token
|
|
}
|
|
|
|
// Create a new Token. Takes a signing method
|
|
func New(method SigningMethod) *Token {
|
|
return NewWithClaims(method, MapClaims{})
|
|
}
|
|
|
|
func NewWithClaims(method SigningMethod, claims Claims) *Token {
|
|
return &Token{
|
|
Header: map[string]interface{}{
|
|
"typ": "JWT",
|
|
"alg": method.Alg(),
|
|
},
|
|
Claims: claims,
|
|
Method: method,
|
|
}
|
|
}
|
|
|
|
// Get the complete, signed token
|
|
func (t *Token) SignedString(key interface{}) (string, error) {
|
|
var sig, sstr string
|
|
var err error
|
|
if sstr, err = t.SigningString(); err != nil {
|
|
return "", err
|
|
}
|
|
if sig, err = t.Method.Sign(sstr, key); err != nil {
|
|
return "", err
|
|
}
|
|
return strings.Join([]string{sstr, sig}, "."), nil
|
|
}
|
|
|
|
// Generate the signing string. This is the
|
|
// most expensive part of the whole deal. Unless you
|
|
// need this for something special, just go straight for
|
|
// the SignedString.
|
|
func (t *Token) SigningString() (string, error) {
|
|
var err error
|
|
parts := make([]string, 2)
|
|
for i, _ := range parts {
|
|
var jsonValue []byte
|
|
if i == 0 {
|
|
if jsonValue, err = json.Marshal(t.Header); err != nil {
|
|
return "", err
|
|
}
|
|
} else {
|
|
if jsonValue, err = json.Marshal(t.Claims); err != nil {
|
|
return "", err
|
|
}
|
|
}
|
|
|
|
parts[i] = EncodeSegment(jsonValue)
|
|
}
|
|
return strings.Join(parts, "."), nil
|
|
}
|
|
|
|
// Parse, validate, and return a token.
|
|
// keyFunc will receive the parsed token and should return the key for validating.
|
|
// If everything is kosher, err will be nil
|
|
func Parse(tokenString string, keyFunc Keyfunc) (*Token, error) {
|
|
return new(Parser).Parse(tokenString, keyFunc)
|
|
}
|
|
|
|
func ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc) (*Token, error) {
|
|
return new(Parser).ParseWithClaims(tokenString, claims, keyFunc)
|
|
}
|
|
|
|
// Encode JWT specific base64url encoding with padding stripped
|
|
func EncodeSegment(seg []byte) string {
|
|
return strings.TrimRight(base64.URLEncoding.EncodeToString(seg), "=")
|
|
}
|
|
|
|
// Decode JWT specific base64url encoding with padding stripped
|
|
func DecodeSegment(seg string) ([]byte, error) {
|
|
if l := len(seg) % 4; l > 0 {
|
|
seg += strings.Repeat("=", 4-l)
|
|
}
|
|
|
|
return base64.URLEncoding.DecodeString(seg)
|
|
}
|