...

Source file src/github.com/ory/fosite/token/jwt/token.go

Documentation: github.com/ory/fosite/token/jwt

     1  package jwt
     2  
     3  import (
     4  	"encoding/base64"
     5  	"encoding/json"
     6  	"fmt"
     7  	"reflect"
     8  
     9  	"github.com/ory/x/errorsx"
    10  	"gopkg.in/square/go-jose.v2"
    11  	"gopkg.in/square/go-jose.v2/jwt"
    12  )
    13  
    14  // Token represets a JWT Token
    15  // This token provide an adaptation to
    16  // transit from [jwt-go](https://github.com/dgrijalva/jwt-go)
    17  // to [go-jose](https://github.com/square/go-jose)
    18  // It provides method signatures compatible with jwt-go but implemented
    19  // using go-json
    20  type Token struct {
    21  	Header map[string]interface{} // The first segment of the token
    22  	Claims MapClaims              // The second segment of the token
    23  	Method jose.SignatureAlgorithm
    24  	valid  bool
    25  }
    26  
    27  const (
    28  	SigningMethodNone = jose.SignatureAlgorithm("none")
    29  	// This key should be use to correctly sign and verify alg:none JWT tokens
    30  	UnsafeAllowNoneSignatureType unsafeNoneMagicConstant = "none signing method allowed"
    31  
    32  	JWTHeaderType      = jose.HeaderKey("typ")
    33  	JWTHeaderTypeValue = "JWT"
    34  )
    35  
    36  type unsafeNoneMagicConstant string
    37  
    38  // Valid informs if the token was verified against a given verification key
    39  // and claims are valid
    40  func (t *Token) Valid() bool {
    41  	return t.valid
    42  }
    43  
    44  // Claims is a port from https://github.com/dgrijalva/jwt-go/blob/master/claims.go
    45  // including its validation methods, which are not available in go-jose library
    46  //
    47  // > For a type to be a Claims object, it must just have a Valid method that determines
    48  // if the token is invalid for any supported reason
    49  type Claims interface {
    50  	Valid() error
    51  }
    52  
    53  // NewWithClaims creates an unverified Token with the given claims and signing method
    54  func NewWithClaims(method jose.SignatureAlgorithm, claims MapClaims) *Token {
    55  	return &Token{
    56  		Claims: claims,
    57  		Method: method,
    58  		Header: map[string]interface{}{},
    59  	}
    60  }
    61  
    62  func (t *Token) toJoseHeader() map[jose.HeaderKey]interface{} {
    63  	h := map[jose.HeaderKey]interface{}{
    64  		JWTHeaderType: JWTHeaderTypeValue,
    65  	}
    66  	for k, v := range t.Header {
    67  		h[jose.HeaderKey(k)] = v
    68  	}
    69  	return h
    70  }
    71  
    72  // SignedString provides a compatible `jwt-go` Token.SignedString method
    73  //
    74  // > Get the complete, signed token
    75  func (t *Token) SignedString(k interface{}) (rawToken string, err error) {
    76  	if _, ok := k.(unsafeNoneMagicConstant); ok {
    77  		rawToken, err = unsignedToken(t)
    78  		return
    79  
    80  	}
    81  	var signer jose.Signer
    82  	key := jose.SigningKey{
    83  		Algorithm: t.Method,
    84  		Key:       k,
    85  	}
    86  	opts := &jose.SignerOptions{ExtraHeaders: t.toJoseHeader()}
    87  	signer, err = jose.NewSigner(key, opts)
    88  	if err != nil {
    89  		err = errorsx.WithStack(err)
    90  		return
    91  	}
    92  
    93  	// A explicit conversion from type alias MapClaims
    94  	// to map[string]interface{} is required because the
    95  	// go-jose CompactSerialize() only support explicit maps
    96  	// as claims or structs but not type aliases from maps.
    97  	claims := map[string]interface{}(t.Claims)
    98  	rawToken, err = jwt.Signed(signer).Claims(claims).CompactSerialize()
    99  	if err != nil {
   100  		err = &ValidationError{Errors: ValidationErrorClaimsInvalid, Inner: err}
   101  		return
   102  	}
   103  	return
   104  }
   105  
   106  func unsignedToken(t *Token) (string, error) {
   107  	t.Header["alg"] = "none"
   108  	t.Header[string(JWTHeaderType)] = JWTHeaderTypeValue
   109  	hbytes, err := json.Marshal(&t.Header)
   110  	if err != nil {
   111  		return "", errorsx.WithStack(err)
   112  	}
   113  	bbytes, err := json.Marshal(&t.Claims)
   114  	if err != nil {
   115  		return "", errorsx.WithStack(err)
   116  	}
   117  	h := base64.RawURLEncoding.EncodeToString(hbytes)
   118  	b := base64.RawURLEncoding.EncodeToString(bbytes)
   119  	return fmt.Sprintf("%v.%v.", h, b), nil
   120  }
   121  
   122  func newToken(parsedToken *jwt.JSONWebToken, claims MapClaims) (*Token, error) {
   123  	token := &Token{Claims: claims}
   124  	if len(parsedToken.Headers) != 1 {
   125  		return nil, &ValidationError{text: fmt.Sprintf("only one header supported, got %v", len(parsedToken.Headers)), Errors: ValidationErrorMalformed}
   126  	}
   127  
   128  	// copy headers
   129  	h := parsedToken.Headers[0]
   130  	token.Header = map[string]interface{}{
   131  		"alg": h.Algorithm,
   132  	}
   133  	if h.KeyID != "" {
   134  		token.Header["kid"] = h.KeyID
   135  	}
   136  	for k, v := range h.ExtraHeaders {
   137  		token.Header[string(k)] = v
   138  	}
   139  
   140  	token.Method = jose.SignatureAlgorithm(h.Algorithm)
   141  
   142  	return token, nil
   143  }
   144  
   145  // Parse methods use this callback function to supply
   146  // the key for verification.  The function receives the parsed,
   147  // but unverified Token.  This allows you to use properties in the
   148  // Header of the token (such as `kid`) to identify which key to use.
   149  type Keyfunc func(*Token) (interface{}, error)
   150  
   151  func Parse(tokenString string, keyFunc Keyfunc) (*Token, error) {
   152  	return ParseWithClaims(tokenString, MapClaims{}, keyFunc)
   153  }
   154  
   155  // Parse, validate, and return a token.
   156  // keyFunc will receive the parsed token and should return the key for validating.
   157  // If everything is kosher, err will be nil
   158  func ParseWithClaims(rawToken string, claims MapClaims, keyFunc Keyfunc) (*Token, error) {
   159  	// Parse the token.
   160  	parsedToken, err := jwt.ParseSigned(rawToken)
   161  	if err != nil {
   162  		return &Token{}, &ValidationError{Errors: ValidationErrorMalformed, text: err.Error()}
   163  	}
   164  
   165  	// fill unverified claims
   166  	// This conversion is required because go-jose supports
   167  	// only marshalling structs or maps but not alias types from maps
   168  	//
   169  	// The KeyFunc(*Token) function requires the claims to be set into the
   170  	// Token, that is an unverified token, therefore an UnsafeClaimsWithoutVerification is done first
   171  	// then with the returned key, the claims gets verified.
   172  	if err := parsedToken.UnsafeClaimsWithoutVerification(&claims); err != nil {
   173  		return nil, &ValidationError{Errors: ValidationErrorClaimsInvalid, text: err.Error()}
   174  	}
   175  
   176  	// creates an usafe token
   177  	token, err := newToken(parsedToken, claims)
   178  	if err != nil {
   179  		return nil, err
   180  	}
   181  
   182  	if keyFunc == nil {
   183  		// keyFunc was not provided.  short circuiting validation
   184  		return token, &ValidationError{Errors: ValidationErrorUnverifiable, text: "no Keyfunc was provided."}
   185  	}
   186  
   187  	// Call keyFunc callback to get verification key
   188  	verificationKey, err := keyFunc(token)
   189  	if err != nil {
   190  		// keyFunc returned an error
   191  		if ve, ok := err.(*ValidationError); ok {
   192  			return token, ve
   193  		}
   194  		return token, &ValidationError{Errors: ValidationErrorUnverifiable, Inner: err}
   195  	}
   196  	if verificationKey == nil {
   197  		return token, &ValidationError{Errors: ValidationErrorSignatureInvalid, text: "keyfunc returned a nil verification key"}
   198  	}
   199  	// To verify signature go-jose requires a pointer to
   200  	// public key instead of the public key value.
   201  	// The pointer values provides that pointer.
   202  	// E.g. transform rsa.PublicKey -> *rsa.PublicKey
   203  	verificationKey = pointer(verificationKey)
   204  
   205  	// verify signature with returned key
   206  	_, validNoneKey := verificationKey.(*unsafeNoneMagicConstant)
   207  	isSignedToken := !(token.Method == SigningMethodNone && validNoneKey)
   208  	if isSignedToken {
   209  		if err := parsedToken.Claims(verificationKey, &claims); err != nil {
   210  			return token, &ValidationError{Errors: ValidationErrorSignatureInvalid, text: err.Error()}
   211  		}
   212  	}
   213  
   214  	// Validate claims
   215  	// This validation is performed to be backwards compatible
   216  	// with jwt-go library behavior
   217  	if err := claims.Valid(); err != nil {
   218  		if e, ok := err.(*ValidationError); !ok {
   219  			err = &ValidationError{Inner: e, Errors: ValidationErrorClaimsInvalid}
   220  		}
   221  		return token, err
   222  	}
   223  
   224  	// set token as verified and validated
   225  	token.valid = true
   226  	return token, nil
   227  }
   228  
   229  // if underline value of v is not a pointer
   230  // it creates a pointer of it and returns it
   231  func pointer(v interface{}) interface{} {
   232  	if reflect.ValueOf(v).Kind() != reflect.Ptr {
   233  		value := reflect.New(reflect.ValueOf(v).Type())
   234  		value.Elem().Set(reflect.ValueOf(v))
   235  		return value.Interface()
   236  	}
   237  	return v
   238  }
   239  

View as plain text