blob: de0f355a3a8367f47e48dc4b6de0c8412742407c [file] [log] [blame]
package jws
import (
"encoding/json"
"fmt"
"github.com/SermoDigital/jose"
)
// JWS represents a specific JWS.
type JWS struct {
payload *payload
plcache rawBase64
clean bool
sb []sigHead
methods []SigningMethod
}
// sigHead represents the 'signatures' member of the JWS' "general"
// serialization form per
// https://tools.ietf.org/html/rfc7515#section-7.2.1
//
// It's embedded inside the "flat" structure in order to properly
// create the "flat" JWS.
type sigHead struct {
Protected rawBase64 `json:"protected,omitempty"`
Unprotected rawBase64 `json:"header,omitempty"`
Signature Signature `json:"signature"`
protected jose.Protected `json:"-"`
unprotected jose.Header `json:"-"`
clean bool `json:"-"`
}
// New creates a new JWS with the provided SigningMethods.
func New(content interface{}, methods ...SigningMethod) *JWS {
sb := make([]sigHead, len(methods))
for i := range methods {
sb[i] = sigHead{
protected: jose.Protected{
"alg": methods[i].Alg(),
},
unprotected: make(jose.Header),
}
}
return &JWS{
payload: &payload{v: content},
sb: sb,
methods: methods,
}
}
type generic struct {
Payload rawBase64 `json:"payload"`
sigHead
Signatures []sigHead `json:"signatures,omitempty"`
}
// Parse parses any of the three serialized JWS forms into a physical
// JWS per https://tools.ietf.org/html/rfc7515#section-5.2
//
// It accepts a json.Unmarshaler in order to properly parse
// the payload. The reason for this is sometimes the payload
// might implement the json.Marshaler interface, and since
// the JWS' payload member is an interface{}, a simple
// json.Unmarshal call cannot magically identify the original
// type. So, in order to keep the caller from having to do extra
// parsing of the payload, the a json.Unmarshaler can be passed
// which will be called to unmarshal the payload however the caller
// wishes. Do note that if json.Unmarshal returns an error the
// original payload will be used as if no json.Unmarshaler was
// passed.
//
// Internally, Parse applies some heuristics and then calls either
// ParseGeneral, ParseFlat, or ParseCompact.
// It should only be called if, for whatever reason, you do not
// know which form the serialized JWT is in.
func Parse(encoded []byte, u ...json.Unmarshaler) (*JWS, error) {
// Try and unmarshal into a generic struct that'll
// hopefully hold either of the two JSON serialization
// formats.s
var g generic
// Not valid JSON. Let's try compact.
if err := json.Unmarshal(encoded, &g); err != nil {
return ParseCompact(encoded, u...)
}
if g.Signatures == nil {
return g.parseFlat(u...)
}
return g.parseGeneral(u...)
}
// ParseGeneral parses a JWS serialized into its "general" form per
// https://tools.ietf.org/html/rfc7515#section-7.2.1
// into a physical JWS per
// https://tools.ietf.org/html/rfc7515#section-5.2
//
// For information on the json.Unmarshaler parameter, see Parse.
func ParseGeneral(encoded []byte, u ...json.Unmarshaler) (*JWS, error) {
var g generic
if err := json.Unmarshal(encoded, &g); err != nil {
return nil, err
}
return g.parseGeneral(u...)
}
func (g *generic) parseGeneral(u ...json.Unmarshaler) (*JWS, error) {
var (
p payload
err error
)
if len(u) > 0 {
if k := u[0]; k.UnmarshalJSON(g.Payload) != nil {
p.v = u
err = ErrCouldNotUnmarshal
}
}
if err != nil {
fmt.Println(string(g.Payload))
if err := json.Unmarshal(g.Payload, &p); err != nil {
return nil, err
}
}
return &JWS{
payload: &p,
sb: g.Signatures,
}, err
}
// ParseFlat parses a JWS serialized into its "flat" form per
// https://tools.ietf.org/html/rfc7515#section-7.2.2
// into a physical JWS per
// https://tools.ietf.org/html/rfc7515#section-5.2
//
// For information on the json.Unmarshaler parameter, see Parse.
func ParseFlat(encoded []byte, u ...json.Unmarshaler) (*JWS, error) {
var g generic
if err := json.Unmarshal(encoded, &g); err != nil {
return nil, err
}
return g.parseFlat(u...)
}
func (g *generic) parseFlat(u ...json.Unmarshaler) (*JWS, error) {
var p payload
if len(u) > 0 {
p.u = u[0]
}
if err := p.UnmarshalJSON(g.Payload); err != nil {
return nil, err
}
return &JWS{
payload: &p,
sb: []sigHead{g.sigHead},
}, nil
}
// ParseCompact parses a JWS serialized into its "compact" form per
// https://tools.ietf.org/html/rfc7515#section-7.1
// into a physical JWS per
// https://tools.ietf.org/html/rfc7515#section-5.2//
// For information on the json.Unmarshaler parameter, see Parse.
func ParseCompact(encoded []byte, u ...json.Unmarshaler) (*JWS, error) {
return nil, nil
}