github.com/nats-io/jwt/v2@v2.5.6/claims.go (about)

     1  /*
     2   * Copyright 2018-2022 The NATS Authors
     3   * Licensed under the Apache License, Version 2.0 (the "License");
     4   * you may not use this file except in compliance with the License.
     5   * You may obtain a copy of the License at
     6   *
     7   * http://www.apache.org/licenses/LICENSE-2.0
     8   *
     9   * Unless required by applicable law or agreed to in writing, software
    10   * distributed under the License is distributed on an "AS IS" BASIS,
    11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12   * See the License for the specific language governing permissions and
    13   * limitations under the License.
    14   */
    15  
    16  package jwt
    17  
    18  import (
    19  	"crypto/sha512"
    20  	"encoding/base32"
    21  	"encoding/base64"
    22  	"encoding/json"
    23  	"errors"
    24  	"fmt"
    25  	"time"
    26  
    27  	"github.com/nats-io/nkeys"
    28  )
    29  
    30  // ClaimType is used to indicate the type of JWT being stored in a Claim
    31  type ClaimType string
    32  
    33  const (
    34  	// OperatorClaim is the type of an operator JWT
    35  	OperatorClaim = "operator"
    36  	// AccountClaim is the type of an Account JWT
    37  	AccountClaim = "account"
    38  	// UserClaim is the type of an user JWT
    39  	UserClaim = "user"
    40  	// ActivationClaim is the type of an activation JWT
    41  	ActivationClaim = "activation"
    42  	// AuthorizationRequestClaim is the type of an auth request claim JWT
    43  	AuthorizationRequestClaim = "authorization_request"
    44  	// AuthorizationResponseClaim is the response for an auth request
    45  	AuthorizationResponseClaim = "authorization_response"
    46  	// GenericClaim is a type that doesn't match Operator/Account/User/ActionClaim
    47  	GenericClaim = "generic"
    48  )
    49  
    50  func IsGenericClaimType(s string) bool {
    51  	switch s {
    52  	case OperatorClaim:
    53  		fallthrough
    54  	case AccountClaim:
    55  		fallthrough
    56  	case UserClaim:
    57  		fallthrough
    58  	case AuthorizationRequestClaim:
    59  		fallthrough
    60  	case AuthorizationResponseClaim:
    61  		fallthrough
    62  	case ActivationClaim:
    63  		return false
    64  	case GenericClaim:
    65  		return true
    66  	default:
    67  		return true
    68  	}
    69  }
    70  
    71  // Claims is a JWT claims
    72  type Claims interface {
    73  	Claims() *ClaimsData
    74  	Encode(kp nkeys.KeyPair) (string, error)
    75  	ExpectedPrefixes() []nkeys.PrefixByte
    76  	Payload() interface{}
    77  	String() string
    78  	Validate(vr *ValidationResults)
    79  	ClaimType() ClaimType
    80  
    81  	verify(payload string, sig []byte) bool
    82  	updateVersion()
    83  }
    84  
    85  type GenericFields struct {
    86  	Tags    TagList   `json:"tags,omitempty"`
    87  	Type    ClaimType `json:"type,omitempty"`
    88  	Version int       `json:"version,omitempty"`
    89  }
    90  
    91  // ClaimsData is the base struct for all claims
    92  type ClaimsData struct {
    93  	Audience  string `json:"aud,omitempty"`
    94  	Expires   int64  `json:"exp,omitempty"`
    95  	ID        string `json:"jti,omitempty"`
    96  	IssuedAt  int64  `json:"iat,omitempty"`
    97  	Issuer    string `json:"iss,omitempty"`
    98  	Name      string `json:"name,omitempty"`
    99  	NotBefore int64  `json:"nbf,omitempty"`
   100  	Subject   string `json:"sub,omitempty"`
   101  }
   102  
   103  // Prefix holds the prefix byte for an NKey
   104  type Prefix struct {
   105  	nkeys.PrefixByte
   106  }
   107  
   108  func encodeToString(d []byte) string {
   109  	return base64.RawURLEncoding.EncodeToString(d)
   110  }
   111  
   112  func decodeString(s string) ([]byte, error) {
   113  	return base64.RawURLEncoding.DecodeString(s)
   114  }
   115  
   116  func serialize(v interface{}) (string, error) {
   117  	j, err := json.Marshal(v)
   118  	if err != nil {
   119  		return "", err
   120  	}
   121  	return encodeToString(j), nil
   122  }
   123  
   124  func (c *ClaimsData) doEncode(header *Header, kp nkeys.KeyPair, claim Claims) (string, error) {
   125  	if header == nil {
   126  		return "", errors.New("header is required")
   127  	}
   128  
   129  	if kp == nil {
   130  		return "", errors.New("keypair is required")
   131  	}
   132  
   133  	if c != claim.Claims() {
   134  		return "", errors.New("claim and claim data do not match")
   135  	}
   136  
   137  	if c.Subject == "" {
   138  		return "", errors.New("subject is not set")
   139  	}
   140  
   141  	h, err := serialize(header)
   142  	if err != nil {
   143  		return "", err
   144  	}
   145  
   146  	issuerBytes, err := kp.PublicKey()
   147  	if err != nil {
   148  		return "", err
   149  	}
   150  
   151  	prefixes := claim.ExpectedPrefixes()
   152  	if prefixes != nil {
   153  		ok := false
   154  		for _, p := range prefixes {
   155  			switch p {
   156  			case nkeys.PrefixByteAccount:
   157  				if nkeys.IsValidPublicAccountKey(issuerBytes) {
   158  					ok = true
   159  				}
   160  			case nkeys.PrefixByteOperator:
   161  				if nkeys.IsValidPublicOperatorKey(issuerBytes) {
   162  					ok = true
   163  				}
   164  			case nkeys.PrefixByteServer:
   165  				if nkeys.IsValidPublicServerKey(issuerBytes) {
   166  					ok = true
   167  				}
   168  			case nkeys.PrefixByteCluster:
   169  				if nkeys.IsValidPublicClusterKey(issuerBytes) {
   170  					ok = true
   171  				}
   172  			case nkeys.PrefixByteUser:
   173  				if nkeys.IsValidPublicUserKey(issuerBytes) {
   174  					ok = true
   175  				}
   176  			}
   177  		}
   178  		if !ok {
   179  			return "", fmt.Errorf("unable to validate expected prefixes - %v", prefixes)
   180  		}
   181  	}
   182  
   183  	c.Issuer = issuerBytes
   184  	c.IssuedAt = time.Now().UTC().Unix()
   185  	c.ID = "" // to create a repeatable hash
   186  	c.ID, err = c.hash()
   187  	if err != nil {
   188  		return "", err
   189  	}
   190  
   191  	claim.updateVersion()
   192  
   193  	payload, err := serialize(claim)
   194  	if err != nil {
   195  		return "", err
   196  	}
   197  
   198  	toSign := fmt.Sprintf("%s.%s", h, payload)
   199  	eSig := ""
   200  	if header.Algorithm == AlgorithmNkeyOld {
   201  		return "", errors.New(AlgorithmNkeyOld + " not supported to write jwtV2")
   202  	} else if header.Algorithm == AlgorithmNkey {
   203  		sig, err := kp.Sign([]byte(toSign))
   204  		if err != nil {
   205  			return "", err
   206  		}
   207  		eSig = encodeToString(sig)
   208  	} else {
   209  		return "", errors.New(header.Algorithm + " not supported to write jwtV2")
   210  	}
   211  	// hash need no padding
   212  	return fmt.Sprintf("%s.%s", toSign, eSig), nil
   213  }
   214  
   215  func (c *ClaimsData) hash() (string, error) {
   216  	j, err := json.Marshal(c)
   217  	if err != nil {
   218  		return "", err
   219  	}
   220  	h := sha512.New512_256()
   221  	h.Write(j)
   222  	return base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(h.Sum(nil)), nil
   223  }
   224  
   225  // Encode encodes a claim into a JWT token. The claim is signed with the
   226  // provided nkey's private key
   227  func (c *ClaimsData) encode(kp nkeys.KeyPair, payload Claims) (string, error) {
   228  	return c.doEncode(&Header{TokenTypeJwt, AlgorithmNkey}, kp, payload)
   229  }
   230  
   231  // Returns a JSON representation of the claim
   232  func (c *ClaimsData) String(claim interface{}) string {
   233  	j, err := json.MarshalIndent(claim, "", "  ")
   234  	if err != nil {
   235  		return ""
   236  	}
   237  	return string(j)
   238  }
   239  
   240  func parseClaims(s string, target Claims) error {
   241  	h, err := decodeString(s)
   242  	if err != nil {
   243  		return err
   244  	}
   245  	return json.Unmarshal(h, &target)
   246  }
   247  
   248  // Verify verifies that the encoded payload was signed by the
   249  // provided public key. Verify is called automatically with
   250  // the claims portion of the token and the public key in the claim.
   251  // Client code need to insure that the public key in the
   252  // claim is trusted.
   253  func (c *ClaimsData) verify(payload string, sig []byte) bool {
   254  	// decode the public key
   255  	kp, err := nkeys.FromPublicKey(c.Issuer)
   256  	if err != nil {
   257  		return false
   258  	}
   259  	if err := kp.Verify([]byte(payload), sig); err != nil {
   260  		return false
   261  	}
   262  	return true
   263  }
   264  
   265  // Validate checks a claim to make sure it is valid. Validity checks
   266  // include expiration and not before constraints.
   267  func (c *ClaimsData) Validate(vr *ValidationResults) {
   268  	now := time.Now().UTC().Unix()
   269  	if c.Expires > 0 && now > c.Expires {
   270  		vr.AddTimeCheck("claim is expired")
   271  	}
   272  
   273  	if c.NotBefore > 0 && c.NotBefore > now {
   274  		vr.AddTimeCheck("claim is not yet valid")
   275  	}
   276  }
   277  
   278  // IsSelfSigned returns true if the claims issuer is the subject
   279  func (c *ClaimsData) IsSelfSigned() bool {
   280  	return c.Issuer == c.Subject
   281  }