fix: rp verification process
This commit is contained in:
parent
a2601f1584
commit
65a58039dc
9 changed files with 148 additions and 47 deletions
|
@ -36,6 +36,9 @@ func Discover(issuer string, httpClient *http.Client) (*oidc.DiscoveryConfigurat
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if discoveryConfig.Issuer != issuer {
|
||||||
|
return nil, oidc.ErrIssuerInvalid
|
||||||
|
}
|
||||||
return discoveryConfig, nil
|
return discoveryConfig, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ package rp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -21,6 +22,7 @@ func NewRemoteKeySet(client *http.Client, jwksURL string) oidc.KeySet {
|
||||||
type remoteKeySet struct {
|
type remoteKeySet struct {
|
||||||
jwksURL string
|
jwksURL string
|
||||||
httpClient *http.Client
|
httpClient *http.Client
|
||||||
|
defaultAlg string
|
||||||
|
|
||||||
// guard all other fields
|
// guard all other fields
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
|
@ -66,30 +68,28 @@ func (i *inflight) result() ([]jose.JSONWebKey, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *remoteKeySet) VerifySignature(ctx context.Context, jws *jose.JSONWebSignature) ([]byte, error) {
|
func (r *remoteKeySet) VerifySignature(ctx context.Context, jws *jose.JSONWebSignature) ([]byte, error) {
|
||||||
// We don't support JWTs signed with multiple signatures.
|
keyID, alg := oidc.GetKeyIDAndAlg(jws)
|
||||||
keyID := ""
|
if alg == "" {
|
||||||
for _, sig := range jws.Signatures {
|
alg = r.defaultAlg
|
||||||
keyID = sig.Header.KeyID
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
keys := r.keysFromCache()
|
keys := r.keysFromCache()
|
||||||
payload, err, ok := oidc.CheckKey(keyID, jws, keys...)
|
key, ok := oidc.FindKey(keyID, oidc.KeyUseSignature, alg, keys...)
|
||||||
if ok {
|
if ok && keyID != "" {
|
||||||
|
payload, err := jws.Verify(&key)
|
||||||
return payload, err
|
return payload, err
|
||||||
}
|
}
|
||||||
|
|
||||||
keys, err = r.keysFromRemote(ctx)
|
keys, err := r.keysFromRemote(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("fetching keys %v", err)
|
return nil, fmt.Errorf("fetching keys %v", err)
|
||||||
}
|
}
|
||||||
|
key, ok = oidc.FindKey(keyID, oidc.KeyUseSignature, alg, keys...)
|
||||||
payload, err, ok = oidc.CheckKey(keyID, jws, keys...)
|
if ok {
|
||||||
if !ok {
|
payload, err := jws.Verify(&key)
|
||||||
return nil, errors.New("invalid kid")
|
|
||||||
}
|
|
||||||
return payload, err
|
return payload, err
|
||||||
}
|
}
|
||||||
|
return nil, errors.New("invalid key")
|
||||||
|
}
|
||||||
|
|
||||||
func (r *remoteKeySet) keysFromCache() (keys []jose.JSONWebKey) {
|
func (r *remoteKeySet) keysFromCache() (keys []jose.JSONWebKey) {
|
||||||
r.mu.Lock()
|
r.mu.Lock()
|
||||||
|
@ -147,10 +147,34 @@ func (r *remoteKeySet) fetchRemoteKeys(ctx context.Context) ([]jose.JSONWebKey,
|
||||||
return nil, fmt.Errorf("oidc: can't create request: %v", err)
|
return nil, fmt.Errorf("oidc: can't create request: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
keySet := new(jose.JSONWebKeySet)
|
keySet := new(jsonWebKeySet)
|
||||||
if err = utils.HttpRequest(r.httpClient, req, keySet); err != nil {
|
if err = utils.HttpRequest(r.httpClient, req, keySet); err != nil {
|
||||||
return nil, fmt.Errorf("oidc: failed to get keys: %v", err)
|
return nil, fmt.Errorf("oidc: failed to get keys: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return keySet.Keys, nil
|
return keySet.Keys, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//jsonWebKeySet is an alias for jose.JSONWebKeySet which ignores unknown key types (kty)
|
||||||
|
type jsonWebKeySet jose.JSONWebKeySet
|
||||||
|
|
||||||
|
//UnmarshalJSON overrides the default jose.JSONWebKeySet method to ignore any error
|
||||||
|
//which might occur because of unknown key types (kty)
|
||||||
|
func (k *jsonWebKeySet) UnmarshalJSON(data []byte) (err error) {
|
||||||
|
var raw rawJSONWebKeySet
|
||||||
|
err = json.Unmarshal(data, &raw)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, key := range raw.Keys {
|
||||||
|
webKey := new(jose.JSONWebKey)
|
||||||
|
err = webKey.UnmarshalJSON(key)
|
||||||
|
if err == nil {
|
||||||
|
k.Keys = append(k.Keys, *webKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type rawJSONWebKeySet struct {
|
||||||
|
Keys []json.RawMessage `json:"keys"`
|
||||||
|
}
|
||||||
|
|
|
@ -22,6 +22,10 @@ const (
|
||||||
pkceCode = "pkce"
|
pkceCode = "pkce"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrUserInfoSubNotMatching = errors.New("sub from userinfo does not match the sub from the id_token")
|
||||||
|
)
|
||||||
|
|
||||||
//RelyingParty declares the minimal interface for oidc clients
|
//RelyingParty declares the minimal interface for oidc clients
|
||||||
type RelyingParty interface {
|
type RelyingParty interface {
|
||||||
//OAuthConfig returns the oauth2 Config
|
//OAuthConfig returns the oauth2 Config
|
||||||
|
@ -245,6 +249,9 @@ func Discover(issuer string, httpClient *http.Client) (Endpoints, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Endpoints{}, err
|
return Endpoints{}, err
|
||||||
}
|
}
|
||||||
|
if discoveryConfig.Issuer != issuer {
|
||||||
|
return Endpoints{}, oidc.ErrIssuerInvalid
|
||||||
|
}
|
||||||
return GetEndpoints(discoveryConfig), nil
|
return GetEndpoints(discoveryConfig), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -323,7 +330,7 @@ func CodeExchange(ctx context.Context, code string, rp RelyingParty, opts ...Cod
|
||||||
//CodeExchangeHandler extends the `CodeExchange` method with a http handler
|
//CodeExchangeHandler extends the `CodeExchange` method with a http handler
|
||||||
//including cookie handling for secure `state` transfer
|
//including cookie handling for secure `state` transfer
|
||||||
//and optional PKCE code verifier checking
|
//and optional PKCE code verifier checking
|
||||||
func CodeExchangeHandler(callback func(http.ResponseWriter, *http.Request, *oidc.Tokens, string), rp RelyingParty) http.HandlerFunc {
|
func CodeExchangeHandler(callback func(w http.ResponseWriter, r *http.Request, tokens *oidc.Tokens, state string), rp RelyingParty) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
state, err := tryReadStateCookie(w, r, rp)
|
state, err := tryReadStateCookie(w, r, rp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -361,17 +368,34 @@ func CodeExchangeHandler(callback func(http.ResponseWriter, *http.Request, *oidc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//UserinfoCallback wraps the callback function of the CodeExchangeHandler
|
||||||
|
//and calls the userinfo endpoint with the access token
|
||||||
|
//on success it will pass the userinfo into its callback function as well
|
||||||
|
func UserinfoCallback(provider RelyingParty, f func(w http.ResponseWriter, r *http.Request, tokens *oidc.Tokens, info oidc.UserInfo, state string)) func(http.ResponseWriter, *http.Request, *oidc.Tokens, string) {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request, tokens *oidc.Tokens, state string) {
|
||||||
|
info, err := Userinfo(tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.GetSubject(), provider)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "userinfo failed: "+err.Error(), http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
f(w, r, tokens, info, state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//Userinfo will call the OIDC Userinfo Endpoint with the provided token
|
//Userinfo will call the OIDC Userinfo Endpoint with the provided token
|
||||||
func Userinfo(token string, rp RelyingParty) (oidc.UserInfo, error) {
|
func Userinfo(token, tokenType, subject string, rp RelyingParty) (oidc.UserInfo, error) {
|
||||||
req, err := http.NewRequest("GET", rp.UserinfoEndpoint(), nil)
|
req, err := http.NewRequest("GET", rp.UserinfoEndpoint(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
req.Header.Set("authorization", token)
|
req.Header.Set("authorization", tokenType+" "+token)
|
||||||
userinfo := oidc.NewUserInfo()
|
userinfo := oidc.NewUserInfo()
|
||||||
if err := utils.HttpRequest(rp.HttpClient(), req, &userinfo); err != nil {
|
if err := utils.HttpRequest(rp.HttpClient(), req, &userinfo); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if userinfo.GetSubject() != subject {
|
||||||
|
return nil, ErrUserInfoSubNotMatching
|
||||||
|
}
|
||||||
return userinfo, nil
|
return userinfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,11 @@ func VerifyIDToken(ctx context.Context, token string, v IDTokenVerifier) (oidc.I
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := oidc.CheckIssuer(claims, v.Issuer()); err != nil {
|
if err := oidc.CheckSubject(claims); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = oidc.CheckIssuer(claims, v.Issuer()); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,17 @@ package oidc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/ed25519"
|
||||||
|
"crypto/rsa"
|
||||||
|
|
||||||
"gopkg.in/square/go-jose.v2"
|
"gopkg.in/square/go-jose.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
KeyUseSignature = "sig"
|
||||||
|
)
|
||||||
|
|
||||||
//KeySet represents a set of JSON Web Keys
|
//KeySet represents a set of JSON Web Keys
|
||||||
// - remotely fetch via discovery and jwks_uri -> `remoteKeySet`
|
// - remotely fetch via discovery and jwks_uri -> `remoteKeySet`
|
||||||
// - held by the OP itself in storage -> `openIDKeySet`
|
// - held by the OP itself in storage -> `openIDKeySet`
|
||||||
|
@ -15,16 +22,51 @@ type KeySet interface {
|
||||||
VerifySignature(ctx context.Context, jws *jose.JSONWebSignature) (payload []byte, err error)
|
VerifySignature(ctx context.Context, jws *jose.JSONWebSignature) (payload []byte, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
//CheckKey searches the given JSON Web Keys for the requested key ID
|
//GetKeyIDAndAlg returns the `kid` and `alg` claim from the JWS header
|
||||||
//and verifies the JSON Web Signature with the found key
|
func GetKeyIDAndAlg(jws *jose.JSONWebSignature) (string, string) {
|
||||||
|
keyID := ""
|
||||||
|
alg := ""
|
||||||
|
for _, sig := range jws.Signatures {
|
||||||
|
keyID = sig.Header.KeyID
|
||||||
|
alg = sig.Header.Algorithm
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return keyID, alg
|
||||||
|
}
|
||||||
|
|
||||||
|
//FindKey searches the given JSON Web Keys for the requested key ID, usage and key type
|
||||||
//
|
//
|
||||||
//will return false but no error if key ID is not found
|
//will return the key immediately if matches exact (id, usage, type)
|
||||||
func CheckKey(keyID string, jws *jose.JSONWebSignature, keys ...jose.JSONWebKey) ([]byte, error, bool) {
|
//
|
||||||
|
//will return false none or multiple match
|
||||||
|
func FindKey(keyID, use, expectedAlg string, keys ...jose.JSONWebKey) (jose.JSONWebKey, bool) {
|
||||||
|
var validKeys []jose.JSONWebKey
|
||||||
for _, key := range keys {
|
for _, key := range keys {
|
||||||
if keyID == "" || key.KeyID == keyID {
|
if key.KeyID == keyID && key.Use == use && algToKeyType(key.Key, expectedAlg) {
|
||||||
payload, err := jws.Verify(&key)
|
if keyID != "" {
|
||||||
return payload, err, true
|
return key, true
|
||||||
|
}
|
||||||
|
validKeys = append(validKeys, key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, nil, false
|
if len(validKeys) == 1 {
|
||||||
|
return validKeys[0], true
|
||||||
|
}
|
||||||
|
return jose.JSONWebKey{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func algToKeyType(key interface{}, alg string) bool {
|
||||||
|
switch alg[0] {
|
||||||
|
case 'R', 'P':
|
||||||
|
_, ok := key.(*rsa.PublicKey)
|
||||||
|
return ok
|
||||||
|
case 'E':
|
||||||
|
_, ok := key.(*ecdsa.PublicKey)
|
||||||
|
return ok
|
||||||
|
case 'O':
|
||||||
|
_, ok := key.(*ed25519.PublicKey)
|
||||||
|
return ok
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ import (
|
||||||
|
|
||||||
type Claims interface {
|
type Claims interface {
|
||||||
GetIssuer() string
|
GetIssuer() string
|
||||||
|
GetSubject() string
|
||||||
GetAudience() []string
|
GetAudience() []string
|
||||||
GetExpiration() time.Time
|
GetExpiration() time.Time
|
||||||
GetIssuedAt() time.Time
|
GetIssuedAt() time.Time
|
||||||
|
@ -30,6 +31,7 @@ type Claims interface {
|
||||||
var (
|
var (
|
||||||
ErrParse = errors.New("parsing of request failed")
|
ErrParse = errors.New("parsing of request failed")
|
||||||
ErrIssuerInvalid = errors.New("issuer does not match")
|
ErrIssuerInvalid = errors.New("issuer does not match")
|
||||||
|
ErrSubjectMissing = errors.New("subject missing")
|
||||||
ErrAudience = errors.New("audience is not valid")
|
ErrAudience = errors.New("audience is not valid")
|
||||||
ErrAzpMissing = errors.New("authorized party is not set. If Token is valid for multiple audiences, azp must not be empty")
|
ErrAzpMissing = errors.New("authorized party is not set. If Token is valid for multiple audiences, azp must not be empty")
|
||||||
ErrAzpInvalid = errors.New("authorized party is not valid")
|
ErrAzpInvalid = errors.New("authorized party is not valid")
|
||||||
|
@ -38,6 +40,7 @@ var (
|
||||||
ErrSignatureUnsupportedAlg = errors.New("signature algorithm not supported")
|
ErrSignatureUnsupportedAlg = errors.New("signature algorithm not supported")
|
||||||
ErrSignatureInvalidPayload = errors.New("signature does not match Payload")
|
ErrSignatureInvalidPayload = errors.New("signature does not match Payload")
|
||||||
ErrExpired = errors.New("token has expired")
|
ErrExpired = errors.New("token has expired")
|
||||||
|
ErrIatMissing = errors.New("issuedAt of token is missing")
|
||||||
ErrIatInFuture = errors.New("issuedAt of token is in the future")
|
ErrIatInFuture = errors.New("issuedAt of token is in the future")
|
||||||
ErrIatToOld = errors.New("issuedAt of token is to old")
|
ErrIatToOld = errors.New("issuedAt of token is to old")
|
||||||
ErrNonceInvalid = errors.New("nonce does not match")
|
ErrNonceInvalid = errors.New("nonce does not match")
|
||||||
|
@ -84,6 +87,13 @@ func ParseToken(tokenString string, claims interface{}) ([]byte, error) {
|
||||||
return payload, err
|
return payload, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CheckSubject(claims Claims) error {
|
||||||
|
if claims.GetSubject() == "" {
|
||||||
|
return ErrSubjectMissing
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func CheckIssuer(claims Claims, issuer string) error {
|
func CheckIssuer(claims Claims, issuer string) error {
|
||||||
if claims.GetIssuer() != issuer {
|
if claims.GetIssuer() != issuer {
|
||||||
return fmt.Errorf("%w: Expected: %s, got: %s", ErrIssuerInvalid, issuer, claims.GetIssuer())
|
return fmt.Errorf("%w: Expected: %s, got: %s", ErrIssuerInvalid, issuer, claims.GetIssuer())
|
||||||
|
@ -155,6 +165,9 @@ func CheckExpiration(claims Claims, offset time.Duration) error {
|
||||||
|
|
||||||
func CheckIssuedAt(claims Claims, maxAgeIAT, offset time.Duration) error {
|
func CheckIssuedAt(claims Claims, maxAgeIAT, offset time.Duration) error {
|
||||||
issuedAt := claims.GetIssuedAt().Round(time.Second)
|
issuedAt := claims.GetIssuedAt().Round(time.Second)
|
||||||
|
if issuedAt.IsZero() {
|
||||||
|
return ErrIatMissing
|
||||||
|
}
|
||||||
nowWithOffset := time.Now().UTC().Add(offset).Round(time.Second)
|
nowWithOffset := time.Now().UTC().Add(offset).Round(time.Second)
|
||||||
if issuedAt.After(nowWithOffset) {
|
if issuedAt.After(nowWithOffset) {
|
||||||
return fmt.Errorf("%w: (iat: %v, now with offset: %v)", ErrIatInFuture, issuedAt, nowWithOffset)
|
return fmt.Errorf("%w: (iat: %v, now with offset: %v)", ErrIatInFuture, issuedAt, nowWithOffset)
|
||||||
|
@ -170,9 +183,6 @@ func CheckIssuedAt(claims Claims, maxAgeIAT, offset time.Duration) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func CheckNonce(claims Claims, nonce string) error {
|
func CheckNonce(claims Claims, nonce string) error {
|
||||||
if nonce == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if claims.GetNonce() != nonce {
|
if claims.GetNonce() != nonce {
|
||||||
return fmt.Errorf("%w: expected %q but was %q", ErrNonceInvalid, nonce, claims.GetNonce())
|
return fmt.Errorf("%w: expected %q but was %q", ErrNonceInvalid, nonce, claims.GetNonce())
|
||||||
}
|
}
|
||||||
|
|
10
pkg/op/op.go
10
pkg/op/op.go
|
@ -267,20 +267,16 @@ type openIDKeySet struct {
|
||||||
//VerifySignature implements the oidc.KeySet interface
|
//VerifySignature implements the oidc.KeySet interface
|
||||||
//providing an implementation for the keys stored in the OP Storage interface
|
//providing an implementation for the keys stored in the OP Storage interface
|
||||||
func (o *openIDKeySet) VerifySignature(ctx context.Context, jws *jose.JSONWebSignature) ([]byte, error) {
|
func (o *openIDKeySet) VerifySignature(ctx context.Context, jws *jose.JSONWebSignature) ([]byte, error) {
|
||||||
keyID := ""
|
|
||||||
for _, sig := range jws.Signatures {
|
|
||||||
keyID = sig.Header.KeyID
|
|
||||||
break
|
|
||||||
}
|
|
||||||
keySet, err := o.Storage.GetKeySet(ctx)
|
keySet, err := o.Storage.GetKeySet(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("error fetching keys")
|
return nil, errors.New("error fetching keys")
|
||||||
}
|
}
|
||||||
payload, err, ok := oidc.CheckKey(keyID, jws, keySet.Keys...)
|
keyID, alg := oidc.GetKeyIDAndAlg(jws)
|
||||||
|
key, ok := oidc.FindKey(keyID, oidc.KeyUseSignature, alg, keySet.Keys...)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("invalid kid")
|
return nil, errors.New("invalid kid")
|
||||||
}
|
}
|
||||||
return payload, err
|
return jws.Verify(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Option func(o *openidProvider) error
|
type Option func(o *openidProvider) error
|
||||||
|
|
|
@ -86,18 +86,13 @@ type jwtProfileKeySet struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *jwtProfileKeySet) VerifySignature(ctx context.Context, jws *jose.JSONWebSignature) (payload []byte, err error) {
|
func (k *jwtProfileKeySet) VerifySignature(ctx context.Context, jws *jose.JSONWebSignature) (payload []byte, err error) {
|
||||||
keyID := ""
|
keyID, alg := oidc.GetKeyIDAndAlg(jws)
|
||||||
for _, sig := range jws.Signatures {
|
|
||||||
keyID = sig.Header.KeyID
|
|
||||||
break
|
|
||||||
}
|
|
||||||
key, err := k.Storage.GetKeyByIDAndUserID(ctx, keyID, k.userID)
|
key, err := k.Storage.GetKeyByIDAndUserID(ctx, keyID, k.userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error fetching keys: %w", err)
|
return nil, fmt.Errorf("error fetching keys: %w", err)
|
||||||
}
|
}
|
||||||
payload, err, ok := oidc.CheckKey(keyID, jws, *key)
|
if key.Algorithm != alg {
|
||||||
if !ok {
|
|
||||||
return nil, errors.New("invalid kid")
|
|
||||||
}
|
}
|
||||||
return payload, err
|
return jws.Verify(&key)
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,9 @@ func GetHashAlgorithm(sigAlgorithm jose.SignatureAlgorithm) (hash.Hash, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func HashString(hash hash.Hash, s string, firstHalf bool) string {
|
func HashString(hash hash.Hash, s string, firstHalf bool) string {
|
||||||
|
if hash == nil {
|
||||||
|
return s
|
||||||
|
}
|
||||||
//nolint:errcheck
|
//nolint:errcheck
|
||||||
hash.Write([]byte(s))
|
hash.Write([]byte(s))
|
||||||
size := hash.Size()
|
size := hash.Size()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue