package jwx import ( "bytes" "encoding/json" ) type FormatKind int const ( UnknownFormat FormatKind = iota JWE JWS JWK JWKS JWT ) type formatHint struct { Payload json.RawMessage `json:"payload"` // Only in JWS Signatures json.RawMessage `json:"signatures"` // Only in JWS Ciphertext json.RawMessage `json:"ciphertext"` // Only in JWE KeyType json.RawMessage `json:"kty"` // Only in JWK Keys json.RawMessage `json:"keys"` // Only in JWKS Audience json.RawMessage `json:"aud"` // Only in JWT } // GuessFormat is used to guess the format the given payload is in // using heuristics. See the type FormatKind for a full list of // possible types. // // This may be useful in determining your next action when you may // encounter a payload that could either be a JWE, JWS, or a plain JWT. // // Because JWTs are almost always JWS signed, you may be thrown off // if you pass what you think is a JWT payload to this function. // If the function is in the "Compact" format, it means it's a JWS // signed message, and its payload is the JWT. Therefore this function // will reuturn JWS, not JWT. // // This function requires an extra parsing of the payload, and therefore // may be inefficient if you call it every time before parsing. func GuessFormat(payload []byte) FormatKind { // The check against kty, keys, and aud are something this library // made up. for the distinctions between JWE and JWS, we used // https://datatracker.ietf.org/doc/html/rfc7516#section-9. // // The above RFC described several ways to distinguish between // a JWE and JWS JSON, but we're only using one of them payload = bytes.TrimSpace(payload) if len(payload) <= 0 { return UnknownFormat } if payload[0] != '{' { // Compact format. It's probably a JWS or JWE sep := []byte{'.'} // I want to const this :/ // Note: this counts the number of occurrences of the // separator, but the RFC talks about the number of segments. // number of '.' == segments - 1, so that's why we have 2 and 4 here switch count := bytes.Count(payload, sep); count { case 2: return JWS case 4: return JWE default: return UnknownFormat } } // If we got here, we probably have JSON. var h formatHint if err := json.Unmarshal(payload, &h); err != nil { return UnknownFormat } if h.Audience != nil { return JWT } if h.KeyType != nil { return JWK } if h.Keys != nil { return JWKS } if h.Ciphertext != nil { return JWE } if h.Signatures != nil && h.Payload != nil { return JWS } return UnknownFormat }