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

     1  /*
     2   * Copyright 2018-2019 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  	"strings"
    26  	"time"
    27  
    28  	"github.com/nats-io/nkeys"
    29  )
    30  
    31  // ClaimType is used to indicate the type of JWT being stored in a Claim
    32  type ClaimType string
    33  
    34  const (
    35  	// AccountClaim is the type of an Account JWT
    36  	AccountClaim = "account"
    37  	//ActivationClaim is the type of an activation JWT
    38  	ActivationClaim = "activation"
    39  	//UserClaim is the type of an user JWT
    40  	UserClaim = "user"
    41  	//OperatorClaim is the type of an operator JWT
    42  	OperatorClaim = "operator"
    43  
    44  	//ServerClaim is the type of an server JWT
    45  	// Deprecated: ServerClaim is not supported
    46  	ServerClaim = "server"
    47  	// ClusterClaim is the type of an cluster JWT
    48  	// Deprecated: ClusterClaim is not supported
    49  	ClusterClaim = "cluster"
    50  )
    51  
    52  // Claims is a JWT claims
    53  type Claims interface {
    54  	Claims() *ClaimsData
    55  	Encode(kp nkeys.KeyPair) (string, error)
    56  	ExpectedPrefixes() []nkeys.PrefixByte
    57  	Payload() interface{}
    58  	String() string
    59  	Validate(vr *ValidationResults)
    60  	Verify(payload string, sig []byte) bool
    61  }
    62  
    63  // ClaimsData is the base struct for all claims
    64  type ClaimsData struct {
    65  	Audience  string    `json:"aud,omitempty"`
    66  	Expires   int64     `json:"exp,omitempty"`
    67  	ID        string    `json:"jti,omitempty"`
    68  	IssuedAt  int64     `json:"iat,omitempty"`
    69  	Issuer    string    `json:"iss,omitempty"`
    70  	Name      string    `json:"name,omitempty"`
    71  	NotBefore int64     `json:"nbf,omitempty"`
    72  	Subject   string    `json:"sub,omitempty"`
    73  	Tags      TagList   `json:"tags,omitempty"`
    74  	Type      ClaimType `json:"type,omitempty"`
    75  }
    76  
    77  // Prefix holds the prefix byte for an NKey
    78  type Prefix struct {
    79  	nkeys.PrefixByte
    80  }
    81  
    82  func encodeToString(d []byte) string {
    83  	return base64.RawURLEncoding.EncodeToString(d)
    84  }
    85  
    86  func decodeString(s string) ([]byte, error) {
    87  	return base64.RawURLEncoding.DecodeString(s)
    88  }
    89  
    90  func serialize(v interface{}) (string, error) {
    91  	j, err := json.Marshal(v)
    92  	if err != nil {
    93  		return "", err
    94  	}
    95  	return encodeToString(j), nil
    96  }
    97  
    98  func (c *ClaimsData) doEncode(header *Header, kp nkeys.KeyPair, claim Claims) (string, error) {
    99  	if header == nil {
   100  		return "", errors.New("header is required")
   101  	}
   102  
   103  	if kp == nil {
   104  		return "", errors.New("keypair is required")
   105  	}
   106  
   107  	if c.Subject == "" {
   108  		return "", errors.New("subject is not set")
   109  	}
   110  
   111  	h, err := serialize(header)
   112  	if err != nil {
   113  		return "", err
   114  	}
   115  
   116  	issuerBytes, err := kp.PublicKey()
   117  	if err != nil {
   118  		return "", err
   119  	}
   120  
   121  	prefixes := claim.ExpectedPrefixes()
   122  	if prefixes != nil {
   123  		ok := false
   124  		for _, p := range prefixes {
   125  			switch p {
   126  			case nkeys.PrefixByteAccount:
   127  				if nkeys.IsValidPublicAccountKey(issuerBytes) {
   128  					ok = true
   129  				}
   130  			case nkeys.PrefixByteOperator:
   131  				if nkeys.IsValidPublicOperatorKey(issuerBytes) {
   132  					ok = true
   133  				}
   134  			case nkeys.PrefixByteServer:
   135  				if nkeys.IsValidPublicServerKey(issuerBytes) {
   136  					ok = true
   137  				}
   138  			case nkeys.PrefixByteCluster:
   139  				if nkeys.IsValidPublicClusterKey(issuerBytes) {
   140  					ok = true
   141  				}
   142  			case nkeys.PrefixByteUser:
   143  				if nkeys.IsValidPublicUserKey(issuerBytes) {
   144  					ok = true
   145  				}
   146  			}
   147  		}
   148  		if !ok {
   149  			return "", fmt.Errorf("unable to validate expected prefixes - %v", prefixes)
   150  		}
   151  	}
   152  
   153  	c.Issuer = string(issuerBytes)
   154  	c.IssuedAt = time.Now().UTC().Unix()
   155  
   156  	c.ID, err = c.hash()
   157  	if err != nil {
   158  		return "", err
   159  	}
   160  
   161  	payload, err := serialize(claim)
   162  	if err != nil {
   163  		return "", err
   164  	}
   165  
   166  	sig, err := kp.Sign([]byte(payload))
   167  	if err != nil {
   168  		return "", err
   169  	}
   170  	eSig := encodeToString(sig)
   171  	return fmt.Sprintf("%s.%s.%s", h, payload, eSig), nil
   172  }
   173  
   174  func (c *ClaimsData) hash() (string, error) {
   175  	j, err := json.Marshal(c)
   176  	if err != nil {
   177  		return "", err
   178  	}
   179  	h := sha512.New512_256()
   180  	h.Write(j)
   181  	return base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(h.Sum(nil)), nil
   182  }
   183  
   184  // Encode encodes a claim into a JWT token. The claim is signed with the
   185  // provided nkey's private key
   186  func (c *ClaimsData) Encode(kp nkeys.KeyPair, payload Claims) (string, error) {
   187  	return c.doEncode(&Header{TokenTypeJwt, AlgorithmNkey}, kp, payload)
   188  }
   189  
   190  // Returns a JSON representation of the claim
   191  func (c *ClaimsData) String(claim interface{}) string {
   192  	j, err := json.MarshalIndent(claim, "", "  ")
   193  	if err != nil {
   194  		return ""
   195  	}
   196  	return string(j)
   197  }
   198  
   199  func parseClaims(s string, target Claims) error {
   200  	h, err := decodeString(s)
   201  	if err != nil {
   202  		return err
   203  	}
   204  	return json.Unmarshal(h, &target)
   205  }
   206  
   207  // Verify verifies that the encoded payload was signed by the
   208  // provided public key. Verify is called automatically with
   209  // the claims portion of the token and the public key in the claim.
   210  // Client code need to insure that the public key in the
   211  // claim is trusted.
   212  func (c *ClaimsData) Verify(payload string, sig []byte) bool {
   213  	// decode the public key
   214  	kp, err := nkeys.FromPublicKey(c.Issuer)
   215  	if err != nil {
   216  		return false
   217  	}
   218  	if err := kp.Verify([]byte(payload), sig); err != nil {
   219  		return false
   220  	}
   221  	return true
   222  }
   223  
   224  // Validate checks a claim to make sure it is valid. Validity checks
   225  // include expiration and not before constraints.
   226  func (c *ClaimsData) Validate(vr *ValidationResults) {
   227  	now := time.Now().UTC().Unix()
   228  	if c.Expires > 0 && now > c.Expires {
   229  		vr.AddTimeCheck("claim is expired")
   230  	}
   231  
   232  	if c.NotBefore > 0 && c.NotBefore > now {
   233  		vr.AddTimeCheck("claim is not yet valid")
   234  	}
   235  }
   236  
   237  // IsSelfSigned returns true if the claims issuer is the subject
   238  func (c *ClaimsData) IsSelfSigned() bool {
   239  	return c.Issuer == c.Subject
   240  }
   241  
   242  // Decode takes a JWT string decodes it and validates it
   243  // and return the embedded Claims. If the token header
   244  // doesn't match the expected algorithm, or the claim is
   245  // not valid or verification fails an error is returned.
   246  func Decode(token string, target Claims) error {
   247  	// must have 3 chunks
   248  	chunks := strings.Split(token, ".")
   249  	if len(chunks) != 3 {
   250  		return errors.New("expected 3 chunks")
   251  	}
   252  
   253  	_, err := parseHeaders(chunks[0])
   254  	if err != nil {
   255  		return err
   256  	}
   257  
   258  	if err := parseClaims(chunks[1], target); err != nil {
   259  		return err
   260  	}
   261  
   262  	sig, err := decodeString(chunks[2])
   263  	if err != nil {
   264  		return err
   265  	}
   266  
   267  	if !target.Verify(chunks[1], sig) {
   268  		return errors.New("claim failed signature verification")
   269  	}
   270  
   271  	prefixes := target.ExpectedPrefixes()
   272  	if prefixes != nil {
   273  		ok := false
   274  		issuer := target.Claims().Issuer
   275  		for _, p := range prefixes {
   276  			switch p {
   277  			case nkeys.PrefixByteAccount:
   278  				if nkeys.IsValidPublicAccountKey(issuer) {
   279  					ok = true
   280  				}
   281  			case nkeys.PrefixByteOperator:
   282  				if nkeys.IsValidPublicOperatorKey(issuer) {
   283  					ok = true
   284  				}
   285  			case nkeys.PrefixByteServer:
   286  				if nkeys.IsValidPublicServerKey(issuer) {
   287  					ok = true
   288  				}
   289  			case nkeys.PrefixByteCluster:
   290  				if nkeys.IsValidPublicClusterKey(issuer) {
   291  					ok = true
   292  				}
   293  			case nkeys.PrefixByteUser:
   294  				if nkeys.IsValidPublicUserKey(issuer) {
   295  					ok = true
   296  				}
   297  			}
   298  		}
   299  		if !ok {
   300  			return fmt.Errorf("unable to validate expected prefixes - %v", prefixes)
   301  		}
   302  	}
   303  
   304  	return nil
   305  }