...

Source file src/gopkg.in/square/go-jose.v2/jwt/builder.go

Documentation: gopkg.in/square/go-jose.v2/jwt

     1  /*-
     2   * Copyright 2016 Zbigniew Mandziejewicz
     3   * Copyright 2016 Square, Inc.
     4   *
     5   * Licensed under the Apache License, Version 2.0 (the "License");
     6   * you may not use this file except in compliance with the License.
     7   * You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17  
    18  package jwt
    19  
    20  import (
    21  	"bytes"
    22  	"reflect"
    23  
    24  	"gopkg.in/square/go-jose.v2/json"
    25  
    26  	"gopkg.in/square/go-jose.v2"
    27  )
    28  
    29  // Builder is a utility for making JSON Web Tokens. Calls can be chained, and
    30  // errors are accumulated until the final call to CompactSerialize/FullSerialize.
    31  type Builder interface {
    32  	// Claims encodes claims into JWE/JWS form. Multiple calls will merge claims
    33  	// into single JSON object. If you are passing private claims, make sure to set
    34  	// struct field tags to specify the name for the JSON key to be used when
    35  	// serializing.
    36  	Claims(i interface{}) Builder
    37  	// Token builds a JSONWebToken from provided data.
    38  	Token() (*JSONWebToken, error)
    39  	// FullSerialize serializes a token using the full serialization format.
    40  	FullSerialize() (string, error)
    41  	// CompactSerialize serializes a token using the compact serialization format.
    42  	CompactSerialize() (string, error)
    43  }
    44  
    45  // NestedBuilder is a utility for making Signed-Then-Encrypted JSON Web Tokens.
    46  // Calls can be chained, and errors are accumulated until final call to
    47  // CompactSerialize/FullSerialize.
    48  type NestedBuilder interface {
    49  	// Claims encodes claims into JWE/JWS form. Multiple calls will merge claims
    50  	// into single JSON object. If you are passing private claims, make sure to set
    51  	// struct field tags to specify the name for the JSON key to be used when
    52  	// serializing.
    53  	Claims(i interface{}) NestedBuilder
    54  	// Token builds a NestedJSONWebToken from provided data.
    55  	Token() (*NestedJSONWebToken, error)
    56  	// FullSerialize serializes a token using the full serialization format.
    57  	FullSerialize() (string, error)
    58  	// CompactSerialize serializes a token using the compact serialization format.
    59  	CompactSerialize() (string, error)
    60  }
    61  
    62  type builder struct {
    63  	payload map[string]interface{}
    64  	err     error
    65  }
    66  
    67  type signedBuilder struct {
    68  	builder
    69  	sig jose.Signer
    70  }
    71  
    72  type encryptedBuilder struct {
    73  	builder
    74  	enc jose.Encrypter
    75  }
    76  
    77  type nestedBuilder struct {
    78  	builder
    79  	sig jose.Signer
    80  	enc jose.Encrypter
    81  }
    82  
    83  // Signed creates builder for signed tokens.
    84  func Signed(sig jose.Signer) Builder {
    85  	return &signedBuilder{
    86  		sig: sig,
    87  	}
    88  }
    89  
    90  // Encrypted creates builder for encrypted tokens.
    91  func Encrypted(enc jose.Encrypter) Builder {
    92  	return &encryptedBuilder{
    93  		enc: enc,
    94  	}
    95  }
    96  
    97  // SignedAndEncrypted creates builder for signed-then-encrypted tokens.
    98  // ErrInvalidContentType will be returned if encrypter doesn't have JWT content type.
    99  func SignedAndEncrypted(sig jose.Signer, enc jose.Encrypter) NestedBuilder {
   100  	if contentType, _ := enc.Options().ExtraHeaders[jose.HeaderContentType].(jose.ContentType); contentType != "JWT" {
   101  		return &nestedBuilder{
   102  			builder: builder{
   103  				err: ErrInvalidContentType,
   104  			},
   105  		}
   106  	}
   107  	return &nestedBuilder{
   108  		sig: sig,
   109  		enc: enc,
   110  	}
   111  }
   112  
   113  func (b builder) claims(i interface{}) builder {
   114  	if b.err != nil {
   115  		return b
   116  	}
   117  
   118  	m, ok := i.(map[string]interface{})
   119  	switch {
   120  	case ok:
   121  		return b.merge(m)
   122  	case reflect.Indirect(reflect.ValueOf(i)).Kind() == reflect.Struct:
   123  		m, err := normalize(i)
   124  		if err != nil {
   125  			return builder{
   126  				err: err,
   127  			}
   128  		}
   129  		return b.merge(m)
   130  	default:
   131  		return builder{
   132  			err: ErrInvalidClaims,
   133  		}
   134  	}
   135  }
   136  
   137  func normalize(i interface{}) (map[string]interface{}, error) {
   138  	m := make(map[string]interface{})
   139  
   140  	raw, err := json.Marshal(i)
   141  	if err != nil {
   142  		return nil, err
   143  	}
   144  
   145  	d := json.NewDecoder(bytes.NewReader(raw))
   146  	d.SetNumberType(json.UnmarshalJSONNumber)
   147  
   148  	if err := d.Decode(&m); err != nil {
   149  		return nil, err
   150  	}
   151  
   152  	return m, nil
   153  }
   154  
   155  func (b *builder) merge(m map[string]interface{}) builder {
   156  	p := make(map[string]interface{})
   157  	for k, v := range b.payload {
   158  		p[k] = v
   159  	}
   160  	for k, v := range m {
   161  		p[k] = v
   162  	}
   163  
   164  	return builder{
   165  		payload: p,
   166  	}
   167  }
   168  
   169  func (b *builder) token(p func(interface{}) ([]byte, error), h []jose.Header) (*JSONWebToken, error) {
   170  	return &JSONWebToken{
   171  		payload: p,
   172  		Headers: h,
   173  	}, nil
   174  }
   175  
   176  func (b *signedBuilder) Claims(i interface{}) Builder {
   177  	return &signedBuilder{
   178  		builder: b.builder.claims(i),
   179  		sig:     b.sig,
   180  	}
   181  }
   182  
   183  func (b *signedBuilder) Token() (*JSONWebToken, error) {
   184  	sig, err := b.sign()
   185  	if err != nil {
   186  		return nil, err
   187  	}
   188  
   189  	h := make([]jose.Header, len(sig.Signatures))
   190  	for i, v := range sig.Signatures {
   191  		h[i] = v.Header
   192  	}
   193  
   194  	return b.builder.token(sig.Verify, h)
   195  }
   196  
   197  func (b *signedBuilder) CompactSerialize() (string, error) {
   198  	sig, err := b.sign()
   199  	if err != nil {
   200  		return "", err
   201  	}
   202  
   203  	return sig.CompactSerialize()
   204  }
   205  
   206  func (b *signedBuilder) FullSerialize() (string, error) {
   207  	sig, err := b.sign()
   208  	if err != nil {
   209  		return "", err
   210  	}
   211  
   212  	return sig.FullSerialize(), nil
   213  }
   214  
   215  func (b *signedBuilder) sign() (*jose.JSONWebSignature, error) {
   216  	if b.err != nil {
   217  		return nil, b.err
   218  	}
   219  
   220  	p, err := json.Marshal(b.payload)
   221  	if err != nil {
   222  		return nil, err
   223  	}
   224  
   225  	return b.sig.Sign(p)
   226  }
   227  
   228  func (b *encryptedBuilder) Claims(i interface{}) Builder {
   229  	return &encryptedBuilder{
   230  		builder: b.builder.claims(i),
   231  		enc:     b.enc,
   232  	}
   233  }
   234  
   235  func (b *encryptedBuilder) CompactSerialize() (string, error) {
   236  	enc, err := b.encrypt()
   237  	if err != nil {
   238  		return "", err
   239  	}
   240  
   241  	return enc.CompactSerialize()
   242  }
   243  
   244  func (b *encryptedBuilder) FullSerialize() (string, error) {
   245  	enc, err := b.encrypt()
   246  	if err != nil {
   247  		return "", err
   248  	}
   249  
   250  	return enc.FullSerialize(), nil
   251  }
   252  
   253  func (b *encryptedBuilder) Token() (*JSONWebToken, error) {
   254  	enc, err := b.encrypt()
   255  	if err != nil {
   256  		return nil, err
   257  	}
   258  
   259  	return b.builder.token(enc.Decrypt, []jose.Header{enc.Header})
   260  }
   261  
   262  func (b *encryptedBuilder) encrypt() (*jose.JSONWebEncryption, error) {
   263  	if b.err != nil {
   264  		return nil, b.err
   265  	}
   266  
   267  	p, err := json.Marshal(b.payload)
   268  	if err != nil {
   269  		return nil, err
   270  	}
   271  
   272  	return b.enc.Encrypt(p)
   273  }
   274  
   275  func (b *nestedBuilder) Claims(i interface{}) NestedBuilder {
   276  	return &nestedBuilder{
   277  		builder: b.builder.claims(i),
   278  		sig:     b.sig,
   279  		enc:     b.enc,
   280  	}
   281  }
   282  
   283  func (b *nestedBuilder) Token() (*NestedJSONWebToken, error) {
   284  	enc, err := b.signAndEncrypt()
   285  	if err != nil {
   286  		return nil, err
   287  	}
   288  
   289  	return &NestedJSONWebToken{
   290  		enc:     enc,
   291  		Headers: []jose.Header{enc.Header},
   292  	}, nil
   293  }
   294  
   295  func (b *nestedBuilder) CompactSerialize() (string, error) {
   296  	enc, err := b.signAndEncrypt()
   297  	if err != nil {
   298  		return "", err
   299  	}
   300  
   301  	return enc.CompactSerialize()
   302  }
   303  
   304  func (b *nestedBuilder) FullSerialize() (string, error) {
   305  	enc, err := b.signAndEncrypt()
   306  	if err != nil {
   307  		return "", err
   308  	}
   309  
   310  	return enc.FullSerialize(), nil
   311  }
   312  
   313  func (b *nestedBuilder) signAndEncrypt() (*jose.JSONWebEncryption, error) {
   314  	if b.err != nil {
   315  		return nil, b.err
   316  	}
   317  
   318  	p, err := json.Marshal(b.payload)
   319  	if err != nil {
   320  		return nil, err
   321  	}
   322  
   323  	sig, err := b.sig.Sign(p)
   324  	if err != nil {
   325  		return nil, err
   326  	}
   327  
   328  	p2, err := sig.CompactSerialize()
   329  	if err != nil {
   330  		return nil, err
   331  	}
   332  
   333  	return b.enc.Encrypt([]byte(p2))
   334  }
   335  

View as plain text