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  }