...

Source file src/github.com/ory/fosite/token/jwt/claims_jwt.go

Documentation: github.com/ory/fosite/token/jwt

     1  /*
     2   * Copyright © 2015-2018 Aeneas Rekkas <aeneas+oss@aeneas.io>
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   *
    16   * @author		Aeneas Rekkas <aeneas+oss@aeneas.io>
    17   * @copyright 	2015-2018 Aeneas Rekkas <aeneas+oss@aeneas.io>
    18   * @license 	Apache-2.0
    19   *
    20   */
    21  
    22  package jwt
    23  
    24  import (
    25  	"strings"
    26  	"time"
    27  
    28  	"github.com/pborman/uuid"
    29  )
    30  
    31  // Enum for different types of scope encoding.
    32  type JWTScopeFieldEnum int
    33  
    34  const (
    35  	JWTScopeFieldUnset JWTScopeFieldEnum = iota
    36  	JWTScopeFieldList
    37  	JWTScopeFieldString
    38  	JWTScopeFieldBoth
    39  )
    40  
    41  type JWTClaimsDefaults struct {
    42  	ExpiresAt time.Time
    43  	IssuedAt  time.Time
    44  	Issuer    string
    45  	Scope     []string
    46  }
    47  
    48  type JWTClaimsContainer interface {
    49  	// With returns a copy of itself with expiresAt, scope, audience set to the given values.
    50  	With(expiry time.Time, scope, audience []string) JWTClaimsContainer
    51  
    52  	// WithDefaults returns a copy of itself with issuedAt and issuer set to the given default values. If those
    53  	// values are already set in the claims, they will not be updated.
    54  	WithDefaults(iat time.Time, issuer string) JWTClaimsContainer
    55  
    56  	// WithScopeField configures how a scope field should be represented in JWT.
    57  	WithScopeField(scopeField JWTScopeFieldEnum) JWTClaimsContainer
    58  
    59  	// ToMapClaims returns the claims as a github.com/dgrijalva/jwt-go.MapClaims type.
    60  	ToMapClaims() MapClaims
    61  }
    62  
    63  // JWTClaims represent a token's claims.
    64  type JWTClaims struct {
    65  	Subject    string
    66  	Issuer     string
    67  	Audience   []string
    68  	JTI        string
    69  	IssuedAt   time.Time
    70  	NotBefore  time.Time
    71  	ExpiresAt  time.Time
    72  	Scope      []string
    73  	Extra      map[string]interface{}
    74  	ScopeField JWTScopeFieldEnum
    75  }
    76  
    77  func (c *JWTClaims) With(expiry time.Time, scope, audience []string) JWTClaimsContainer {
    78  	c.ExpiresAt = expiry
    79  	c.Scope = scope
    80  	c.Audience = audience
    81  	return c
    82  }
    83  
    84  func (c *JWTClaims) WithDefaults(iat time.Time, issuer string) JWTClaimsContainer {
    85  	if c.IssuedAt.IsZero() {
    86  		c.IssuedAt = iat
    87  	}
    88  
    89  	if c.Issuer == "" {
    90  		c.Issuer = issuer
    91  	}
    92  	return c
    93  }
    94  
    95  func (c *JWTClaims) WithScopeField(scopeField JWTScopeFieldEnum) JWTClaimsContainer {
    96  	c.ScopeField = scopeField
    97  	return c
    98  }
    99  
   100  // ToMap will transform the headers to a map structure
   101  func (c *JWTClaims) ToMap() map[string]interface{} {
   102  	var ret = Copy(c.Extra)
   103  
   104  	if c.Subject != "" {
   105  		ret["sub"] = c.Subject
   106  	} else {
   107  		delete(ret, "sub")
   108  	}
   109  
   110  	if c.Issuer != "" {
   111  		ret["iss"] = c.Issuer
   112  	} else {
   113  		delete(ret, "iss")
   114  	}
   115  
   116  	if c.JTI != "" {
   117  		ret["jti"] = c.JTI
   118  	} else {
   119  		ret["jti"] = uuid.New()
   120  	}
   121  
   122  	if len(c.Audience) > 0 {
   123  		ret["aud"] = c.Audience
   124  	} else {
   125  		ret["aud"] = []string{}
   126  	}
   127  
   128  	if !c.IssuedAt.IsZero() {
   129  		ret["iat"] = c.IssuedAt.Unix()
   130  	} else {
   131  		delete(ret, "iat")
   132  	}
   133  
   134  	if !c.NotBefore.IsZero() {
   135  		ret["nbf"] = c.NotBefore.Unix()
   136  	} else {
   137  		delete(ret, "nbf")
   138  	}
   139  
   140  	if !c.ExpiresAt.IsZero() {
   141  		ret["exp"] = c.ExpiresAt.Unix()
   142  	} else {
   143  		delete(ret, "exp")
   144  	}
   145  
   146  	if c.Scope != nil {
   147  		// ScopeField default (when value is JWTScopeFieldUnset) is the list for backwards compatibility with old versions of fosite.
   148  		if c.ScopeField == JWTScopeFieldUnset || c.ScopeField == JWTScopeFieldList || c.ScopeField == JWTScopeFieldBoth {
   149  			ret["scp"] = c.Scope
   150  		}
   151  		if c.ScopeField == JWTScopeFieldString || c.ScopeField == JWTScopeFieldBoth {
   152  			ret["scope"] = strings.Join(c.Scope, " ")
   153  		}
   154  	} else {
   155  		delete(ret, "scp")
   156  		delete(ret, "scope")
   157  	}
   158  
   159  	return ret
   160  }
   161  
   162  // FromMap will set the claims based on a mapping
   163  func (c *JWTClaims) FromMap(m map[string]interface{}) {
   164  	c.Extra = make(map[string]interface{})
   165  	for k, v := range m {
   166  		switch k {
   167  		case "jti":
   168  			if s, ok := v.(string); ok {
   169  				c.JTI = s
   170  			}
   171  		case "sub":
   172  			if s, ok := v.(string); ok {
   173  				c.Subject = s
   174  			}
   175  		case "iss":
   176  			if s, ok := v.(string); ok {
   177  				c.Issuer = s
   178  			}
   179  		case "aud":
   180  			if s, ok := v.(string); ok {
   181  				c.Audience = []string{s}
   182  			} else if s, ok := v.([]string); ok {
   183  				c.Audience = s
   184  			}
   185  		case "iat":
   186  			c.IssuedAt = toTime(v, c.IssuedAt)
   187  		case "nbf":
   188  			c.NotBefore = toTime(v, c.NotBefore)
   189  		case "exp":
   190  			c.ExpiresAt = toTime(v, c.ExpiresAt)
   191  		case "scp":
   192  			switch s := v.(type) {
   193  			case []string:
   194  				c.Scope = s
   195  				if c.ScopeField == JWTScopeFieldString {
   196  					c.ScopeField = JWTScopeFieldBoth
   197  				} else if c.ScopeField == JWTScopeFieldUnset {
   198  					c.ScopeField = JWTScopeFieldList
   199  				}
   200  			case []interface{}:
   201  				c.Scope = make([]string, len(s))
   202  				for i, vi := range s {
   203  					if s, ok := vi.(string); ok {
   204  						c.Scope[i] = s
   205  					}
   206  				}
   207  				if c.ScopeField == JWTScopeFieldString {
   208  					c.ScopeField = JWTScopeFieldBoth
   209  				} else if c.ScopeField == JWTScopeFieldUnset {
   210  					c.ScopeField = JWTScopeFieldList
   211  				}
   212  			}
   213  		case "scope":
   214  			if s, ok := v.(string); ok {
   215  				c.Scope = strings.Split(s, " ")
   216  				if c.ScopeField == JWTScopeFieldList {
   217  					c.ScopeField = JWTScopeFieldBoth
   218  				} else if c.ScopeField == JWTScopeFieldUnset {
   219  					c.ScopeField = JWTScopeFieldString
   220  				}
   221  			}
   222  		default:
   223  			c.Extra[k] = v
   224  		}
   225  	}
   226  }
   227  
   228  func toTime(v interface{}, def time.Time) (t time.Time) {
   229  	t = def
   230  	switch a := v.(type) {
   231  	case float64:
   232  		t = time.Unix(int64(a), 0).UTC()
   233  	case int64:
   234  		t = time.Unix(a, 0).UTC()
   235  	}
   236  	return
   237  }
   238  
   239  // Add will add a key-value pair to the extra field
   240  func (c *JWTClaims) Add(key string, value interface{}) {
   241  	if c.Extra == nil {
   242  		c.Extra = make(map[string]interface{})
   243  	}
   244  	c.Extra[key] = value
   245  }
   246  
   247  // Get will get a value from the extra field based on a given key
   248  func (c JWTClaims) Get(key string) interface{} {
   249  	return c.ToMap()[key]
   250  }
   251  
   252  // ToMapClaims will return a jwt-go MapClaims representation
   253  func (c JWTClaims) ToMapClaims() MapClaims {
   254  	return c.ToMap()
   255  }
   256  
   257  // FromMapClaims will populate claims from a jwt-go MapClaims representation
   258  func (c *JWTClaims) FromMapClaims(mc MapClaims) {
   259  	c.FromMap(mc)
   260  }
   261  

View as plain text