148 lines
3.7 KiB
Go
148 lines
3.7 KiB
Go
package jwt
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"reflect"
|
|
"strings"
|
|
)
|
|
|
|
// JOSE represent a JSON Object Signing and Encryption header as
|
|
// defined in RFC 7515.
|
|
type JOSE struct {
|
|
Algorithm string `json:"alg"`
|
|
Type string `json:"typ,omitempty"`
|
|
Content string `json:"cty,omitempty"`
|
|
Critical []string `json:"crit,omitempty"`
|
|
JWKSetURL string `json:"jku,omitempty"`
|
|
JSONWebKey string `json:"jwk,omitempty"`
|
|
KeyID string `json:"kid,omitempty"`
|
|
X509URL string `json:"x5u,omitempty"`
|
|
X509CertificateChain string `json:"x5c,omitempty"`
|
|
X509ThumbprintSha1 string `json:"x5t,omitempty"`
|
|
X509ThumbprintSha256 string `json:"x5t#S256,omitempty"`
|
|
|
|
//Sets of adfditional headers, private or public
|
|
AdditionalHeaders map[string]interface{} `json:"-"`
|
|
}
|
|
|
|
// EncodeJSON encodes a JOSE header in JSON format. Not using
|
|
// MarshallJSON tyo avoid loops
|
|
func (j *JOSE) EncodeJSON() ([]byte, error) {
|
|
data, err := json.Marshal(j)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(data) == 0 || data[0] != '{' || data[len(data)-1] != '}' {
|
|
return nil, fmt.Errorf("jws: Invalid JSON encoding: %s", data)
|
|
}
|
|
|
|
if len(j.AdditionalHeaders) == 0 {
|
|
return data, nil
|
|
}
|
|
|
|
moreHeader, err := json.Marshal(j.AdditionalHeaders)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(moreHeader) == 0 || moreHeader[0] != '{' || moreHeader[len(moreHeader)-1] != '}' {
|
|
return nil, fmt.Errorf("jws: Invalid JSON encoding: %s", data)
|
|
}
|
|
data[len(data)-1] = ','
|
|
return append(data, moreHeader[1:]...), nil
|
|
}
|
|
|
|
// EncodeBase64 encodes a JOSE into JSON base64 string. will allocate
|
|
// buffer.
|
|
func (j *JOSE) EncodeBase64() ([]byte, error) {
|
|
dec, err := j.EncodeJSON()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
enc := make([]byte, Base64EncodedBufferLen(len(dec)))
|
|
base64.URLEncoding.Encode(enc, dec)
|
|
return enc[:Base64EncodedStrippedLen(len(dec))], nil
|
|
}
|
|
|
|
// DecodeJOSE a JOSE header from a base64 text as defined in the RFC 7515
|
|
func DecodeJOSE(data []byte) (*JOSE, error) {
|
|
bData := make([]byte, Base64DecodedLenFromStripped(len(data)))
|
|
|
|
if err := Base64Decode(bData, data); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
j := &JOSE{}
|
|
if err := json.NewDecoder(bytes.NewBuffer(bData)).Decode(j); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return j, nil
|
|
}
|
|
|
|
// Validate validates a JOSE header data. Maybe it should disappear
|
|
func (j *JOSE) Validate() error {
|
|
if len(j.Algorithm) == 0 {
|
|
return fmt.Errorf("jwt: missing 'alg' header")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type fieldSetter func(*JOSE, string)
|
|
|
|
var fieldSetters = make(map[string]fieldSetter)
|
|
|
|
// UnmarshalJSON is here to satisfy interface json.Unmarshaller. We
|
|
// need to provide ou own unmarshaller for the additional header.
|
|
func (j *JOSE) UnmarshalJSON(b []byte) error {
|
|
raw := make(map[string]interface{})
|
|
|
|
if err := json.Unmarshal(b, &raw); err != nil {
|
|
return err
|
|
}
|
|
|
|
if j.AdditionalHeaders == nil {
|
|
j.AdditionalHeaders = make(map[string]interface{}, len(raw))
|
|
}
|
|
|
|
for key, data := range raw {
|
|
if fSetter, ok := fieldSetters[key]; ok == true {
|
|
strData, ok := data.(string)
|
|
if ok == false {
|
|
return &json.UnmarshalTypeError{
|
|
Value: reflect.TypeOf(data).Kind().String(),
|
|
Type: reflect.TypeOf(string("")),
|
|
}
|
|
}
|
|
|
|
fSetter(j, strData)
|
|
continue
|
|
}
|
|
|
|
j.AdditionalHeaders[key] = data
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func init() {
|
|
tJOSE := reflect.TypeOf(JOSE{})
|
|
for i := 0; i < tJOSE.NumField(); i++ {
|
|
jField := tJOSE.Field(i)
|
|
jTag := jField.Tag.Get("json")
|
|
if len(jTag) == 0 || jTag == "-" {
|
|
continue
|
|
}
|
|
|
|
jName := strings.Split(jTag, ",")
|
|
fieldSetters[jName[0]] = func(j *JOSE, data string) {
|
|
reflect.ValueOf(j).Elem().FieldByName(jField.Name).SetString(data)
|
|
}
|
|
}
|
|
}
|