github.com/nats-io/jwt/v2@v2.5.6/genericlaims.go (about) 1 /* 2 * Copyright 2018-2020 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 "encoding/json" 20 "errors" 21 "strings" 22 23 "github.com/nats-io/nkeys" 24 ) 25 26 // GenericClaims can be used to read a JWT as a map for any non-generic fields 27 type GenericClaims struct { 28 ClaimsData 29 Data map[string]interface{} `json:"nats,omitempty"` 30 } 31 32 // NewGenericClaims creates a map-based Claims 33 func NewGenericClaims(subject string) *GenericClaims { 34 if subject == "" { 35 return nil 36 } 37 c := GenericClaims{} 38 c.Subject = subject 39 c.Data = make(map[string]interface{}) 40 return &c 41 } 42 43 // DecodeGeneric takes a JWT string and decodes it into a ClaimsData and map 44 func DecodeGeneric(token string) (*GenericClaims, error) { 45 // must have 3 chunks 46 chunks := strings.Split(token, ".") 47 if len(chunks) != 3 { 48 return nil, errors.New("expected 3 chunks") 49 } 50 51 // header 52 header, err := parseHeaders(chunks[0]) 53 if err != nil { 54 return nil, err 55 } 56 // claim 57 data, err := decodeString(chunks[1]) 58 if err != nil { 59 return nil, err 60 } 61 62 gc := struct { 63 GenericClaims 64 GenericFields 65 }{} 66 if err := json.Unmarshal(data, &gc); err != nil { 67 return nil, err 68 } 69 70 // sig 71 sig, err := decodeString(chunks[2]) 72 if err != nil { 73 return nil, err 74 } 75 76 if header.Algorithm == AlgorithmNkeyOld { 77 if !gc.verify(chunks[1], sig) { 78 return nil, errors.New("claim failed V1 signature verification") 79 } 80 if tp := gc.GenericFields.Type; tp != "" { 81 // the conversion needs to be from a string because 82 // on custom types the type is not going to be one of 83 // the constants 84 gc.GenericClaims.Data["type"] = string(tp) 85 } 86 if tp := gc.GenericFields.Tags; len(tp) != 0 { 87 gc.GenericClaims.Data["tags"] = tp 88 } 89 90 } else { 91 if !gc.verify(token[:len(chunks[0])+len(chunks[1])+1], sig) { 92 return nil, errors.New("claim failed V2 signature verification") 93 } 94 } 95 return &gc.GenericClaims, nil 96 } 97 98 // Claims returns the standard part of the generic claim 99 func (gc *GenericClaims) Claims() *ClaimsData { 100 return &gc.ClaimsData 101 } 102 103 // Payload returns the custom part of the claims data 104 func (gc *GenericClaims) Payload() interface{} { 105 return &gc.Data 106 } 107 108 // Encode takes a generic claims and creates a JWT string 109 func (gc *GenericClaims) Encode(pair nkeys.KeyPair) (string, error) { 110 return gc.ClaimsData.encode(pair, gc) 111 } 112 113 // Validate checks the generic part of the claims data 114 func (gc *GenericClaims) Validate(vr *ValidationResults) { 115 gc.ClaimsData.Validate(vr) 116 } 117 118 func (gc *GenericClaims) String() string { 119 return gc.ClaimsData.String(gc) 120 } 121 122 // ExpectedPrefixes returns the types allowed to encode a generic JWT, which is nil for all 123 func (gc *GenericClaims) ExpectedPrefixes() []nkeys.PrefixByte { 124 return nil 125 } 126 127 func (gc *GenericClaims) ClaimType() ClaimType { 128 v, ok := gc.Data["type"] 129 if !ok { 130 v, ok = gc.Data["nats"] 131 if ok { 132 m, ok := v.(map[string]interface{}) 133 if ok { 134 v = m["type"] 135 } 136 } 137 } 138 139 switch ct := v.(type) { 140 case string: 141 if IsGenericClaimType(ct) { 142 return GenericClaim 143 } 144 return ClaimType(ct) 145 case ClaimType: 146 return ct 147 default: 148 return "" 149 } 150 } 151 152 func (gc *GenericClaims) updateVersion() { 153 if gc.Data != nil { 154 // store as float as that is what decoding with json does too 155 gc.Data["version"] = float64(libVersion) 156 } 157 }