123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335 |
- /*-
- * Copyright 2016 Zbigniew Mandziejewicz
- * Copyright 2016 Square, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package jwt
- import (
- "bytes"
- "reflect"
- "gopkg.in/square/go-jose.v2/json"
- "gopkg.in/square/go-jose.v2"
- )
- // Builder is a utility for making JSON Web Tokens. Calls can be chained, and
- // errors are accumulated until the final call to CompactSerialize/FullSerialize.
- type Builder interface {
- // Claims encodes claims into JWE/JWS form. Multiple calls will merge claims
- // into single JSON object. If you are passing private claims, make sure to set
- // struct field tags to specify the name for the JSON key to be used when
- // serializing.
- Claims(i interface{}) Builder
- // Token builds a JSONWebToken from provided data.
- Token() (*JSONWebToken, error)
- // FullSerialize serializes a token using the full serialization format.
- FullSerialize() (string, error)
- // CompactSerialize serializes a token using the compact serialization format.
- CompactSerialize() (string, error)
- }
- // NestedBuilder is a utility for making Signed-Then-Encrypted JSON Web Tokens.
- // Calls can be chained, and errors are accumulated until final call to
- // CompactSerialize/FullSerialize.
- type NestedBuilder interface {
- // Claims encodes claims into JWE/JWS form. Multiple calls will merge claims
- // into single JSON object. If you are passing private claims, make sure to set
- // struct field tags to specify the name for the JSON key to be used when
- // serializing.
- Claims(i interface{}) NestedBuilder
- // Token builds a NestedJSONWebToken from provided data.
- Token() (*NestedJSONWebToken, error)
- // FullSerialize serializes a token using the full serialization format.
- FullSerialize() (string, error)
- // CompactSerialize serializes a token using the compact serialization format.
- CompactSerialize() (string, error)
- }
- type builder struct {
- payload map[string]interface{}
- err error
- }
- type signedBuilder struct {
- builder
- sig jose.Signer
- }
- type encryptedBuilder struct {
- builder
- enc jose.Encrypter
- }
- type nestedBuilder struct {
- builder
- sig jose.Signer
- enc jose.Encrypter
- }
- // Signed creates builder for signed tokens.
- func Signed(sig jose.Signer) Builder {
- return &signedBuilder{
- sig: sig,
- }
- }
- // Encrypted creates builder for encrypted tokens.
- func Encrypted(enc jose.Encrypter) Builder {
- return &encryptedBuilder{
- enc: enc,
- }
- }
- // SignedAndEncrypted creates builder for signed-then-encrypted tokens.
- // ErrInvalidContentType will be returned if encrypter doesn't have JWT content type.
- func SignedAndEncrypted(sig jose.Signer, enc jose.Encrypter) NestedBuilder {
- if contentType, _ := enc.Options().ExtraHeaders[jose.HeaderContentType].(jose.ContentType); contentType != "JWT" {
- return &nestedBuilder{
- builder: builder{
- err: ErrInvalidContentType,
- },
- }
- }
- return &nestedBuilder{
- sig: sig,
- enc: enc,
- }
- }
- func (b builder) claims(i interface{}) builder {
- if b.err != nil {
- return b
- }
- m, ok := i.(map[string]interface{})
- switch {
- case ok:
- return b.merge(m)
- case reflect.Indirect(reflect.ValueOf(i)).Kind() == reflect.Struct:
- m, err := normalize(i)
- if err != nil {
- return builder{
- err: err,
- }
- }
- return b.merge(m)
- default:
- return builder{
- err: ErrInvalidClaims,
- }
- }
- }
- func normalize(i interface{}) (map[string]interface{}, error) {
- m := make(map[string]interface{})
- raw, err := json.Marshal(i)
- if err != nil {
- return nil, err
- }
- d := json.NewDecoder(bytes.NewReader(raw))
- d.UseNumber()
- if err := d.Decode(&m); err != nil {
- return nil, err
- }
- return m, nil
- }
- func (b *builder) merge(m map[string]interface{}) builder {
- p := make(map[string]interface{})
- for k, v := range b.payload {
- p[k] = v
- }
- for k, v := range m {
- p[k] = v
- }
- return builder{
- payload: p,
- }
- }
- func (b *builder) token(p func(interface{}) ([]byte, error), h []jose.Header) (*JSONWebToken, error) {
- return &JSONWebToken{
- payload: p,
- Headers: h,
- }, nil
- }
- func (b *signedBuilder) Claims(i interface{}) Builder {
- return &signedBuilder{
- builder: b.builder.claims(i),
- sig: b.sig,
- }
- }
- func (b *signedBuilder) Token() (*JSONWebToken, error) {
- sig, err := b.sign()
- if err != nil {
- return nil, err
- }
- h := make([]jose.Header, len(sig.Signatures))
- for i, v := range sig.Signatures {
- h[i] = v.Header
- }
- return b.builder.token(sig.Verify, h)
- }
- func (b *signedBuilder) CompactSerialize() (string, error) {
- sig, err := b.sign()
- if err != nil {
- return "", err
- }
- return sig.CompactSerialize()
- }
- func (b *signedBuilder) FullSerialize() (string, error) {
- sig, err := b.sign()
- if err != nil {
- return "", err
- }
- return sig.FullSerialize(), nil
- }
- func (b *signedBuilder) sign() (*jose.JSONWebSignature, error) {
- if b.err != nil {
- return nil, b.err
- }
- p, err := json.Marshal(b.payload)
- if err != nil {
- return nil, err
- }
- return b.sig.Sign(p)
- }
- func (b *encryptedBuilder) Claims(i interface{}) Builder {
- return &encryptedBuilder{
- builder: b.builder.claims(i),
- enc: b.enc,
- }
- }
- func (b *encryptedBuilder) CompactSerialize() (string, error) {
- enc, err := b.encrypt()
- if err != nil {
- return "", err
- }
- return enc.CompactSerialize()
- }
- func (b *encryptedBuilder) FullSerialize() (string, error) {
- enc, err := b.encrypt()
- if err != nil {
- return "", err
- }
- return enc.FullSerialize(), nil
- }
- func (b *encryptedBuilder) Token() (*JSONWebToken, error) {
- enc, err := b.encrypt()
- if err != nil {
- return nil, err
- }
- return b.builder.token(enc.Decrypt, []jose.Header{enc.Header})
- }
- func (b *encryptedBuilder) encrypt() (*jose.JSONWebEncryption, error) {
- if b.err != nil {
- return nil, b.err
- }
- p, err := json.Marshal(b.payload)
- if err != nil {
- return nil, err
- }
- return b.enc.Encrypt(p)
- }
- func (b *nestedBuilder) Claims(i interface{}) NestedBuilder {
- return &nestedBuilder{
- builder: b.builder.claims(i),
- sig: b.sig,
- enc: b.enc,
- }
- }
- func (b *nestedBuilder) Token() (*NestedJSONWebToken, error) {
- enc, err := b.signAndEncrypt()
- if err != nil {
- return nil, err
- }
- return &NestedJSONWebToken{
- enc: enc,
- Headers: []jose.Header{enc.Header},
- }, nil
- }
- func (b *nestedBuilder) CompactSerialize() (string, error) {
- enc, err := b.signAndEncrypt()
- if err != nil {
- return "", err
- }
- return enc.CompactSerialize()
- }
- func (b *nestedBuilder) FullSerialize() (string, error) {
- enc, err := b.signAndEncrypt()
- if err != nil {
- return "", err
- }
- return enc.FullSerialize(), nil
- }
- func (b *nestedBuilder) signAndEncrypt() (*jose.JSONWebEncryption, error) {
- if b.err != nil {
- return nil, b.err
- }
- p, err := json.Marshal(b.payload)
- if err != nil {
- return nil, err
- }
- sig, err := b.sig.Sign(p)
- if err != nil {
- return nil, err
- }
- p2, err := sig.CompactSerialize()
- if err != nil {
- return nil, err
- }
- return b.enc.Encrypt([]byte(p2))
- }
|