github.com/cs3org/reva/v2@v2.27.7/pkg/token/manager/jwt/jwt.go (about)

     1  // Copyright 2018-2021 CERN
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  // In applying this license, CERN does not waive the privileges and immunities
    16  // granted to it by virtue of its status as an Intergovernmental Organization
    17  // or submit itself to any jurisdiction.
    18  
    19  package jwt
    20  
    21  import (
    22  	"context"
    23  	"time"
    24  
    25  	auth "github.com/cs3org/go-cs3apis/cs3/auth/provider/v1beta1"
    26  	user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
    27  	"github.com/cs3org/reva/v2/pkg/errtypes"
    28  	"github.com/cs3org/reva/v2/pkg/sharedconf"
    29  	"github.com/cs3org/reva/v2/pkg/token"
    30  	"github.com/cs3org/reva/v2/pkg/token/manager/registry"
    31  	"github.com/golang-jwt/jwt/v5"
    32  	"github.com/mitchellh/mapstructure"
    33  	"github.com/pkg/errors"
    34  )
    35  
    36  const defaultExpiration int64 = 86400 // 1 day
    37  const defaultLeeway int64 = 5         // 5 seconds
    38  
    39  func init() {
    40  	registry.Register("jwt", New)
    41  }
    42  
    43  type config struct {
    44  	Secret          string `mapstructure:"secret"`
    45  	Expires         int64  `mapstructure:"expires"`
    46  	tokenTimeLeeway int64  `mapstructure:"token_leeway"`
    47  }
    48  
    49  type manager struct {
    50  	conf *config
    51  }
    52  
    53  // claims are custom claims for the JWT token.
    54  type claims struct {
    55  	jwt.RegisteredClaims
    56  	User  *user.User             `json:"user"`
    57  	Scope map[string]*auth.Scope `json:"scope"`
    58  }
    59  
    60  func parseConfig(m map[string]interface{}) (*config, error) {
    61  	c := &config{}
    62  	if err := mapstructure.Decode(m, c); err != nil {
    63  		err = errors.Wrap(err, "error decoding conf")
    64  		return nil, err
    65  	}
    66  	return c, nil
    67  }
    68  
    69  // New returns an implementation of the token manager that uses JWT as tokens.
    70  func New(value map[string]interface{}) (token.Manager, error) {
    71  	c, err := parseConfig(value)
    72  	if err != nil {
    73  		return nil, errors.Wrap(err, "error parsing config")
    74  	}
    75  
    76  	if c.Expires == 0 {
    77  		c.Expires = defaultExpiration
    78  	}
    79  
    80  	if c.tokenTimeLeeway == 0 {
    81  		c.tokenTimeLeeway = defaultLeeway
    82  	}
    83  
    84  	c.Secret = sharedconf.GetJWTSecret(c.Secret)
    85  
    86  	if c.Secret == "" {
    87  		return nil, errors.New("jwt: secret for signing payloads is not defined in config")
    88  	}
    89  
    90  	m := &manager{conf: c}
    91  	return m, nil
    92  }
    93  
    94  func (m *manager) MintToken(ctx context.Context, u *user.User, scope map[string]*auth.Scope) (string, error) {
    95  	ttl := time.Duration(m.conf.Expires) * time.Second
    96  	newClaims := claims{
    97  		RegisteredClaims: jwt.RegisteredClaims{
    98  			ExpiresAt: jwt.NewNumericDate(time.Now().Add(ttl)),
    99  			Issuer:    u.Id.Idp,
   100  			Audience:  jwt.ClaimStrings{"reva"},
   101  			IssuedAt:  jwt.NewNumericDate(time.Now()),
   102  		},
   103  		User:  u,
   104  		Scope: scope,
   105  	}
   106  
   107  	t := jwt.NewWithClaims(jwt.GetSigningMethod("HS256"), newClaims)
   108  
   109  	tkn, err := t.SignedString([]byte(m.conf.Secret))
   110  	if err != nil {
   111  		return "", errors.Wrapf(err, "error signing token with claims %+v", newClaims)
   112  	}
   113  
   114  	return tkn, nil
   115  }
   116  
   117  func (m *manager) DismantleToken(ctx context.Context, tkn string) (*user.User, map[string]*auth.Scope, error) {
   118  	keyfunc := func(token *jwt.Token) (interface{}, error) {
   119  		return []byte(m.conf.Secret), nil
   120  	}
   121  	token, err := jwt.ParseWithClaims(tkn, &claims{}, keyfunc, jwt.WithLeeway(time.Duration(m.conf.tokenTimeLeeway)*time.Second))
   122  
   123  	if err != nil {
   124  		return nil, nil, errors.Wrap(err, "error parsing token")
   125  	}
   126  
   127  	if claims, ok := token.Claims.(*claims); ok && token.Valid {
   128  		return claims.User, claims.Scope, nil
   129  	}
   130  
   131  	return nil, nil, errtypes.InvalidCredentials("invalid token")
   132  }