github.com/lestrrat-go/jwx/v2@v2.0.21/format.go (about) 1 package jwx 2 3 import ( 4 "bytes" 5 "encoding/json" 6 ) 7 8 type FormatKind int 9 10 // These constants describe the result from guessing the format 11 // of the incoming buffer. 12 const ( 13 // InvalidFormat is returned when the format of the incoming buffer 14 // has been deemed conclusively invalid 15 InvalidFormat FormatKind = iota 16 // UnknownFormat is returned when GuessFormat was not able to conclusively 17 // determine the format of the 18 UnknownFormat 19 JWE 20 JWS 21 JWK 22 JWKS 23 JWT 24 ) 25 26 type formatHint struct { 27 Payload json.RawMessage `json:"payload"` // Only in JWS 28 Signatures json.RawMessage `json:"signatures"` // Only in JWS 29 Ciphertext json.RawMessage `json:"ciphertext"` // Only in JWE 30 KeyType json.RawMessage `json:"kty"` // Only in JWK 31 Keys json.RawMessage `json:"keys"` // Only in JWKS 32 Audience json.RawMessage `json:"aud"` // Only in JWT 33 } 34 35 // GuessFormat is used to guess the format the given payload is in 36 // using heuristics. See the type FormatKind for a full list of 37 // possible types. 38 // 39 // This may be useful in determining your next action when you may 40 // encounter a payload that could either be a JWE, JWS, or a plain JWT. 41 // 42 // Because JWTs are almost always JWS signed, you may be thrown off 43 // if you pass what you think is a JWT payload to this function. 44 // If the function is in the "Compact" format, it means it's a JWS 45 // signed message, and its payload is the JWT. Therefore this function 46 // will reuturn JWS, not JWT. 47 // 48 // This function requires an extra parsing of the payload, and therefore 49 // may be inefficient if you call it every time before parsing. 50 func GuessFormat(payload []byte) FormatKind { 51 // The check against kty, keys, and aud are something this library 52 // made up. for the distinctions between JWE and JWS, we used 53 // https://datatracker.ietf.org/doc/html/rfc7516#section-9. 54 // 55 // The above RFC described several ways to distinguish between 56 // a JWE and JWS JSON, but we're only using one of them 57 58 payload = bytes.TrimSpace(payload) 59 if len(payload) <= 0 { 60 return UnknownFormat 61 } 62 63 if payload[0] != '{' { 64 // Compact format. It's probably a JWS or JWE 65 sep := []byte{'.'} // I want to const this :/ 66 67 // Note: this counts the number of occurrences of the 68 // separator, but the RFC talks about the number of segments. 69 // number of '.' == segments - 1, so that's why we have 2 and 4 here 70 switch count := bytes.Count(payload, sep); count { 71 case 2: 72 return JWS 73 case 4: 74 return JWE 75 default: 76 return InvalidFormat 77 } 78 } 79 80 // If we got here, we probably have JSON. 81 var h formatHint 82 if err := json.Unmarshal(payload, &h); err != nil { 83 return UnknownFormat 84 } 85 86 if h.Audience != nil { 87 return JWT 88 } 89 if h.KeyType != nil { 90 return JWK 91 } 92 if h.Keys != nil { 93 return JWKS 94 } 95 if h.Ciphertext != nil { 96 return JWE 97 } 98 if h.Signatures != nil && h.Payload != nil { 99 return JWS 100 } 101 return UnknownFormat 102 }