95 lines
2.5 KiB
Go
95 lines
2.5 KiB
Go
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
|
|
}
|