github.com/greenpau/go-authcrunch@v1.1.4/pkg/kms/keystore.go (about)

     1  // Copyright 2022 Paul Greenberg greenpau@outlook.com
     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  package kms
    16  
    17  import (
    18  	"strings"
    19  
    20  	jwtlib "github.com/golang-jwt/jwt/v4"
    21  	"github.com/greenpau/go-authcrunch/pkg/errors"
    22  	"github.com/greenpau/go-authcrunch/pkg/requests"
    23  	"github.com/greenpau/go-authcrunch/pkg/user"
    24  	"go.uber.org/zap"
    25  )
    26  
    27  var (
    28  	reservedTokenNames = map[string]bool{
    29  		"access_token":     true,
    30  		"jwt_access_token": true,
    31  		"bearer":           true,
    32  	}
    33  )
    34  
    35  // CryptoKeyStore constains keys assembled for a specific purpose, i.e. signing or
    36  // validation.
    37  type CryptoKeyStore struct {
    38  	keys       []*CryptoKey
    39  	signKeys   []*CryptoKey
    40  	verifyKeys []*CryptoKey
    41  	logger     *zap.Logger
    42  	defaults   map[string]interface{}
    43  }
    44  
    45  // NewCryptoKeyStore returns a new instance of CryptoKeyStore
    46  func NewCryptoKeyStore() *CryptoKeyStore {
    47  	ks := &CryptoKeyStore{}
    48  	ks.defaults = make(map[string]interface{})
    49  	return ks
    50  }
    51  
    52  // SetLogger adds a logger to CryptoKeyStore.
    53  func (ks *CryptoKeyStore) SetLogger(logger *zap.Logger) {
    54  	ks.logger = logger
    55  }
    56  
    57  // AddDefaults adds default settings to CryptoKeyStore.
    58  func (ks *CryptoKeyStore) AddDefaults(m map[string]interface{}) error {
    59  	if m == nil {
    60  		return nil
    61  	}
    62  	if ks.defaults == nil {
    63  		ks.defaults = make(map[string]interface{})
    64  	}
    65  	for k, v := range m {
    66  		switch k {
    67  		case "token_name":
    68  			ks.defaults[k] = v.(string)
    69  		case "token_lifetime":
    70  			ks.defaults[k] = int(v.(float64))
    71  		default:
    72  			ks.defaults[k] = v
    73  		}
    74  	}
    75  	return nil
    76  }
    77  
    78  // AutoGenerate auto-generates public-private key pair capable of both
    79  // signing and verifying tokens.
    80  func (ks *CryptoKeyStore) AutoGenerate(tag, algo string) error {
    81  	cfg := &CryptoKeyConfig{
    82  		ID:            "0",
    83  		Usage:         "sign-verify",
    84  		TokenName:     "access_token",
    85  		Source:        "config",
    86  		TokenLifetime: 900,
    87  		parsed:        true,
    88  	}
    89  
    90  	if ks.defaults != nil {
    91  		if _, exists := ks.defaults["token_name"]; exists {
    92  			cfg.TokenName = ks.defaults["token_name"].(string)
    93  		}
    94  		if _, exists := ks.defaults["token_lifetime"]; exists {
    95  			cfg.TokenLifetime = ks.defaults["token_lifetime"].(int)
    96  		}
    97  	}
    98  
    99  	if len(ks.keys) > 0 {
   100  		return errors.ErrCryptoKeyStoreAutoGenerateNotAvailable
   101  	}
   102  
   103  	key, err := generateKey(cfg, tag, algo)
   104  	if err != nil {
   105  		return err
   106  	}
   107  
   108  	key.enableUsage()
   109  	ks.keys = append(ks.keys, key)
   110  	ks.signKeys = append(ks.signKeys, key)
   111  	ks.verifyKeys = append(ks.verifyKeys, key)
   112  	return nil
   113  }
   114  
   115  // GetKeys returns CryptoKey instances from CryptoKeyStore.
   116  func (ks *CryptoKeyStore) GetKeys() []*CryptoKey {
   117  	return ks.keys
   118  }
   119  
   120  // GetSignKeys returns CryptoKey instances with key signing capabilities
   121  // from CryptoKeyStore.
   122  func (ks *CryptoKeyStore) GetSignKeys() []*CryptoKey {
   123  	return ks.signKeys
   124  }
   125  
   126  // GetVerifyKeys returns CryptoKey instances with key verification capabilities
   127  // from CryptoKeyStore.
   128  func (ks *CryptoKeyStore) GetVerifyKeys() []*CryptoKey {
   129  	return ks.verifyKeys
   130  }
   131  
   132  // AddKeysWithConfigs adds CryptoKey instances by providing their
   133  // configurations to CryptoKeyStore.
   134  func (ks *CryptoKeyStore) AddKeysWithConfigs(cfgs []*CryptoKeyConfig) error {
   135  	keys, err := GetKeysFromConfigs(cfgs)
   136  	if err != nil {
   137  		return err
   138  	}
   139  	for _, k := range keys {
   140  		if err := ks.AddKey(k); err != nil {
   141  			return err
   142  		}
   143  	}
   144  	return nil
   145  }
   146  
   147  // HasVerifyKeys returns true if CryptoKeyStore has key verification
   148  // capabilities.
   149  func (ks *CryptoKeyStore) HasVerifyKeys() error {
   150  	if len(ks.verifyKeys) > 0 {
   151  		return nil
   152  	}
   153  	return errors.ErrCryptoKeyStoreNoVerifyKeysFound
   154  }
   155  
   156  // HasSignKeys returns true if CryptoKeyStore has key signing
   157  // capabilities.
   158  func (ks *CryptoKeyStore) HasSignKeys() error {
   159  	if len(ks.signKeys) > 0 {
   160  		return nil
   161  	}
   162  	return errors.ErrCryptoKeyStoreNoSignKeysFound
   163  }
   164  
   165  // AddKeys adds CryptoKey instances to CryptoKeyStore.
   166  func (ks *CryptoKeyStore) AddKeys(keys []*CryptoKey) error {
   167  	for _, k := range keys {
   168  		if err := ks.AddKey(k); err != nil {
   169  			return err
   170  		}
   171  	}
   172  	return nil
   173  }
   174  
   175  // AddKey adds CryptoKey instance to CryptoKeyStore.
   176  func (ks *CryptoKeyStore) AddKey(k *CryptoKey) error {
   177  	if k == nil {
   178  		return errors.ErrCryptoKeyStoreAddKeyNil
   179  	}
   180  	if k.Sign != nil {
   181  		if k.Sign.Capable {
   182  			ks.signKeys = append(ks.signKeys, k)
   183  		}
   184  	}
   185  	if k.Verify != nil {
   186  		if k.Verify.Capable {
   187  			ks.verifyKeys = append(ks.verifyKeys, k)
   188  		}
   189  	}
   190  	if k.Verify == nil && k.Sign == nil {
   191  		return errors.ErrCryptoKeyStoreAddKeyNil
   192  	}
   193  	ks.keys = append(ks.keys, k)
   194  	return nil
   195  }
   196  
   197  // ParseToken parses JWT token and returns User instance.
   198  func (ks *CryptoKeyStore) ParseToken(ar *requests.AuthorizationRequest) (*user.User, error) {
   199  	for _, k := range ks.verifyKeys {
   200  		if _, exists := reservedTokenNames[ar.Token.Name]; !exists {
   201  			if ar.Token.Name != k.Verify.Token.Name {
   202  				continue
   203  			}
   204  		}
   205  		parsedToken, err := jwtlib.Parse(ar.Token.Payload, k.ProvideKey)
   206  		if err != nil && !strings.Contains(err.Error(), "is expired") {
   207  			continue
   208  		}
   209  
   210  		userData := make(map[string]interface{})
   211  		errData := make(map[string]interface{})
   212  		for k, v := range parsedToken.Claims.(jwtlib.MapClaims) {
   213  			switch k {
   214  			case "iss":
   215  				if strings.HasPrefix(v.(string), "http") {
   216  					ar.Redirect.AuthURL = strings.TrimSuffix(v.(string), "authorization-code-callback")
   217  				}
   218  			case "mail", "email":
   219  				errData["email"] = v.(string)
   220  				ar.Redirect.LoginHint = v.(string)
   221  			case "sub", "name", "jti":
   222  				errData[k] = v.(string)
   223  			}
   224  			userData[k] = v
   225  		}
   226  
   227  		if err != nil {
   228  			ar.Response.User = errData
   229  			return nil, errors.ErrCryptoKeyStoreParseTokenExpired
   230  		}
   231  
   232  		usr, err := user.NewUser(userData)
   233  		if err != nil {
   234  			return usr, errors.ErrCryptoKeyStoreTokenData
   235  		}
   236  		return usr, nil
   237  	}
   238  	return nil, errors.ErrCryptoKeyStoreParseTokenFailed
   239  }
   240  
   241  // SignToken signs user claims and add signed token to user identity.
   242  func (ks *CryptoKeyStore) SignToken(tokenName, signMethod interface{}, usr *user.User) error {
   243  	for _, k := range ks.signKeys {
   244  		if tokenName != nil {
   245  			if tokenName.(string) != k.Sign.Token.Name {
   246  				continue
   247  			}
   248  		}
   249  		response, err := k.sign(signMethod, usr.AsMap())
   250  		if err != nil {
   251  			return err
   252  		}
   253  		usr.Token = response.(string)
   254  		usr.TokenName = k.Sign.Token.Name
   255  		return nil
   256  	}
   257  	return errors.ErrCryptoKeyStoreSignTokenFailed
   258  }
   259  
   260  // GetTokenLifetime returns lifetime for a signed token.
   261  func (ks *CryptoKeyStore) GetTokenLifetime(tokenName, signMethod interface{}) int {
   262  	for _, k := range ks.signKeys {
   263  		if tokenName != nil {
   264  			if tokenName.(string) != k.Sign.Token.Name {
   265  				continue
   266  			}
   267  		}
   268  		return k.Sign.Token.MaxLifetime
   269  	}
   270  	return 900
   271  }