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 }