From 6b4b8f20a52ec7b66998da189388a2c6d070ffc5 Mon Sep 17 00:00:00 2001 From: Livio Amstutz Date: Tue, 19 Nov 2019 16:28:51 +0100 Subject: [PATCH] new packaging --- pkg/rp/{defaults => }/default_rp.go | 20 +++-- .../verifier.go => default_verifier.go} | 76 ++++++++++++------- pkg/rp/{defaults => }/delegation.go | 2 +- pkg/rp/{defaults => }/error.go | 2 +- .../relaying_party.go => tockenexchange.go} | 5 +- 5 files changed, 60 insertions(+), 45 deletions(-) rename pkg/rp/{defaults => }/default_rp.go (91%) rename pkg/rp/{defaults/verifier.go => default_verifier.go} (78%) rename pkg/rp/{defaults => }/delegation.go (97%) rename pkg/rp/{defaults => }/error.go (99%) rename pkg/rp/{tokenexchange/relaying_party.go => tockenexchange.go} (92%) diff --git a/pkg/rp/defaults/default_rp.go b/pkg/rp/default_rp.go similarity index 91% rename from pkg/rp/defaults/default_rp.go rename to pkg/rp/default_rp.go index d9204ae..6b4d2d7 100644 --- a/pkg/rp/defaults/default_rp.go +++ b/pkg/rp/default_rp.go @@ -1,4 +1,4 @@ -package defaults +package rp import ( "context" @@ -12,8 +12,6 @@ import ( "github.com/caos/oidc/pkg/oidc" grants_tx "github.com/caos/oidc/pkg/oidc/grants/tokenexchange" - "github.com/caos/oidc/pkg/rp" - "github.com/caos/oidc/pkg/rp/tokenexchange" "github.com/caos/oidc/pkg/utils" ) @@ -23,18 +21,18 @@ const ( ) type DefaultRP struct { - endpoints rp.Endpoints + endpoints Endpoints oauthConfig oauth2.Config - config *rp.Config + config *Config httpClient *http.Client cookieHandler *utils.CookieHandler - verifier rp.Verifier + verifier Verifier } -func NewDefaultRelayingParty(rpConfig *rp.Config, rpOpts ...DefaultReplayingPartyOpts) (tokenexchange.DelegationTokenExchangeRP, error) { +func NewDefaultRelayingParty(rpConfig *Config, rpOpts ...DefaultRPOpts) (DelegationTokenExchangeRP, error) { p := &DefaultRP{ config: rpConfig, httpClient: utils.DefaultHTTPClient, @@ -55,15 +53,15 @@ func NewDefaultRelayingParty(rpConfig *rp.Config, rpOpts ...DefaultReplayingPart return p, nil } -type DefaultReplayingPartyOpts func(p *DefaultRP) +type DefaultRPOpts func(p *DefaultRP) -func WithCookieHandler(cookieHandler *utils.CookieHandler) DefaultReplayingPartyOpts { +func WithCookieHandler(cookieHandler *utils.CookieHandler) DefaultRPOpts { return func(p *DefaultRP) { p.cookieHandler = cookieHandler } } -func WithHTTPClient(client *http.Client) DefaultReplayingPartyOpts { +func WithHTTPClient(client *http.Client) DefaultRPOpts { return func(p *DefaultRP) { p.httpClient = client } @@ -169,7 +167,7 @@ func (p *DefaultRP) discover() error { return err } - p.endpoints = rp.GetEndpoints(discoveryConfig) + p.endpoints = GetEndpoints(discoveryConfig) p.oauthConfig = oauth2.Config{ ClientID: p.config.ClientID, ClientSecret: p.config.ClientSecret, diff --git a/pkg/rp/defaults/verifier.go b/pkg/rp/default_verifier.go similarity index 78% rename from pkg/rp/defaults/verifier.go rename to pkg/rp/default_verifier.go index 45df7a9..5123c1c 100644 --- a/pkg/rp/defaults/verifier.go +++ b/pkg/rp/default_verifier.go @@ -1,4 +1,4 @@ -package defaults +package rp import ( "bytes" @@ -15,11 +15,24 @@ import ( "gopkg.in/square/go-jose.v2" "github.com/caos/oidc/pkg/oidc" - "github.com/caos/oidc/pkg/rp" str_utils "github.com/caos/utils/strings" ) -func NewVerifier(issuer, clientID string, keySet oidc.KeySet, confOpts ...ConfFunc) rp.Verifier { +//DefaultVerifier implements the `Verifier` interface +type DefaultVerifier struct { + config *verifierConfig + keySet oidc.KeySet +} + +//ConfFunc is the type for providing dynamic verifierConfig +type ConfFunc func(*verifierConfig) + +//ACRVerifier specifies the function to be used by the `DefaultVerifier` for validating the acr claim +type ACRVerifier func(string) error + +//NewDefaultVerifier creates `DefaultVerifier` with the given +//issuer, clientID, keyset and possible configOptions +func NewDefaultVerifier(issuer, clientID string, keySet oidc.KeySet, confOpts ...ConfFunc) Verifier { conf := &verifierConfig{ issuer: issuer, clientID: clientID, @@ -33,52 +46,53 @@ func NewVerifier(issuer, clientID string, keySet oidc.KeySet, confOpts ...ConfFu opt(conf) } } - return &Verifier{config: conf, keySet: keySet} + return &DefaultVerifier{config: conf, keySet: keySet} } -type Verifier struct { - config *verifierConfig - keySet oidc.KeySet -} - -type ConfFunc func(*verifierConfig) - +//WithIgnoreIssuedAt will turn off iat claim verification func WithIgnoreIssuedAt() func(*verifierConfig) { return func(conf *verifierConfig) { conf.iat.ignore = true } } +//WithIssuedAtOffset mitigates the risk of iat to be in the future +//because of clock skews with the ability to add an offset to the current time func WithIssuedAtOffset(offset time.Duration) func(*verifierConfig) { return func(conf *verifierConfig) { conf.iat.offset = offset } } +//WithIssuedAtMaxAge provides the ability to define the maximum duration between iat and now func WithIssuedAtMaxAge(maxAge time.Duration) func(*verifierConfig) { return func(conf *verifierConfig) { conf.iat.maxAge = maxAge } } +//WithNonce TODO: ? func WithNonce(nonce string) func(*verifierConfig) { return func(conf *verifierConfig) { conf.nonce = nonce } } +//WithACRVerifier sets the verifier for the acr claim func WithACRVerifier(verifier ACRVerifier) func(*verifierConfig) { return func(conf *verifierConfig) { conf.acr = verifier } } +//WithAuthTimeMaxAge provides the ability to define the maximum duration between auth_time and now func WithAuthTimeMaxAge(maxAge time.Duration) func(*verifierConfig) { return func(conf *verifierConfig) { conf.maxAge = maxAge } } +//WithSupportedSigningAlgorithms overwrites the default RS256 signing algorithm func WithSupportedSigningAlgorithms(algs ...string) func(*verifierConfig) { return func(conf *verifierConfig) { conf.supportedSignAlgs = algs @@ -111,9 +125,9 @@ type iatConfig struct { maxAge time.Duration } -type ACRVerifier func(string) error - -func DefaultACRVerifier(possibleValues []string) func(string) error { +//DefaultACRVerifier implements `ACRVerifier` returning an error +//if non of the provided values matches the acr claim +func DefaultACRVerifier(possibleValues []string) ACRVerifier { return func(acr string) error { if !str_utils.Contains(possibleValues, acr) { return ErrAcrInvalid(possibleValues, acr) @@ -122,7 +136,10 @@ func DefaultACRVerifier(possibleValues []string) func(string) error { } } -func (v *Verifier) Verify(ctx context.Context, accessToken, idTokenString string) (*oidc.IDTokenClaims, error) { +//Verify implements the `Verify` method of the `Verifier` interface +//according to https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation +//and https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowTokenValidation +func (v *DefaultVerifier) Verify(ctx context.Context, accessToken, idTokenString string) (*oidc.IDTokenClaims, error) { v.config.now = time.Now().UTC() idToken, err := v.VerifyIDToken(ctx, idTokenString) if err != nil { @@ -134,14 +151,15 @@ func (v *Verifier) Verify(ctx context.Context, accessToken, idTokenString string return idToken, nil } -func (v *Verifier) now() time.Time { +func (v *DefaultVerifier) now() time.Time { if v.config.now.IsZero() { v.config.now = time.Now().UTC().Round(time.Second) } return v.config.now } -func (v *Verifier) VerifyIDToken(ctx context.Context, idTokenString string) (*oidc.IDTokenClaims, error) { +//VerifyIDToken: https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation +func (v *DefaultVerifier) VerifyIDToken(ctx context.Context, idTokenString string) (*oidc.IDTokenClaims, error) { //1. if encrypted --> decrypt decrypted, err := v.decryptToken(idTokenString) if err != nil { @@ -206,7 +224,7 @@ func (v *Verifier) VerifyIDToken(ctx context.Context, idTokenString string) (*oi // return err } -func (v *Verifier) parseToken(tokenString string) (*oidc.IDTokenClaims, []byte, error) { +func (v *DefaultVerifier) parseToken(tokenString string) (*oidc.IDTokenClaims, []byte, error) { parts := strings.Split(tokenString, ".") if len(parts) != 3 { return nil, nil, ValidationError("token contains an invalid number of segments") //TODO: err NewValidationError("token contains an invalid number of segments", ValidationErrorMalformed) @@ -220,14 +238,14 @@ func (v *Verifier) parseToken(tokenString string) (*oidc.IDTokenClaims, []byte, return idToken, payload, err } -func (v *Verifier) checkIssuer(issuer string) error { +func (v *DefaultVerifier) checkIssuer(issuer string) error { if v.config.issuer != issuer { return ErrIssuerInvalid(v.config.issuer, issuer) } return nil } -func (v *Verifier) checkAudience(audiences []string) error { +func (v *DefaultVerifier) checkAudience(audiences []string) error { if !str_utils.Contains(audiences, v.config.clientID) { return ErrAudienceMissingClientID(v.config.clientID) } @@ -238,7 +256,7 @@ func (v *Verifier) checkAudience(audiences []string) error { //4. if multiple aud strings --> check if azp //5. if azp --> check azp == client_id -func (v *Verifier) checkAuthorizedParty(audiences []string, authorizedParty string) error { +func (v *DefaultVerifier) checkAuthorizedParty(audiences []string, authorizedParty string) error { if len(audiences) > 1 { if authorizedParty == "" { return ErrAzpMissing() @@ -250,7 +268,7 @@ func (v *Verifier) checkAuthorizedParty(audiences []string, authorizedParty stri return nil } -func (v *Verifier) checkSignature(ctx context.Context, idTokenString string, payload []byte) (jose.SignatureAlgorithm, error) { +func (v *DefaultVerifier) checkSignature(ctx context.Context, idTokenString string, payload []byte) (jose.SignatureAlgorithm, error) { jws, err := jose.ParseSigned(idTokenString) if err != nil { return "", err @@ -344,7 +362,7 @@ func (v *Verifier) checkSignature(ctx context.Context, idTokenString string, pay // return nil // } -func (v *Verifier) checkExpiration(expiration time.Time) error { +func (v *DefaultVerifier) checkExpiration(expiration time.Time) error { expiration = expiration.Round(time.Second) if !v.now().Before(expiration) { return ErrExpInvalid(expiration) @@ -352,7 +370,7 @@ func (v *Verifier) checkExpiration(expiration time.Time) error { return nil } -func (v *Verifier) checkIssuedAt(issuedAt time.Time) error { +func (v *DefaultVerifier) checkIssuedAt(issuedAt time.Time) error { if v.config.iat.ignore { return nil } @@ -370,7 +388,7 @@ func (v *Verifier) checkIssuedAt(issuedAt time.Time) error { } return nil } -func (v *Verifier) checkNonce(nonce string) error { +func (v *DefaultVerifier) checkNonce(nonce string) error { if v.config.nonce == "" { return nil } @@ -379,13 +397,13 @@ func (v *Verifier) checkNonce(nonce string) error { } return nil } -func (v *Verifier) checkAuthorizationContextClassReference(acr string) error { +func (v *DefaultVerifier) checkAuthorizationContextClassReference(acr string) error { if v.config.acr != nil { return v.config.acr(acr) } return nil } -func (v *Verifier) checkAuthTime(authTime time.Time) error { +func (v *DefaultVerifier) checkAuthTime(authTime time.Time) error { if v.config.maxAge == 0 { return nil } @@ -400,7 +418,7 @@ func (v *Verifier) checkAuthTime(authTime time.Time) error { return nil } -func (v *Verifier) decryptToken(tokenString string) (string, error) { +func (v *DefaultVerifier) decryptToken(tokenString string) (string, error) { return tokenString, nil //TODO: impl } @@ -423,7 +441,7 @@ func (v *Verifier) decryptToken(tokenString string) (string, error) { // return token, nil //TODO: impl // } -func (v *Verifier) verifyAccessToken(accessToken, atHash string, sigAlgorithm jose.SignatureAlgorithm) error { +func (v *DefaultVerifier) verifyAccessToken(accessToken, atHash string, sigAlgorithm jose.SignatureAlgorithm) error { if atHash == "" { return nil //TODO: return error } diff --git a/pkg/rp/defaults/delegation.go b/pkg/rp/delegation.go similarity index 97% rename from pkg/rp/defaults/delegation.go rename to pkg/rp/delegation.go index 9ae0e93..3ae6bb6 100644 --- a/pkg/rp/defaults/delegation.go +++ b/pkg/rp/delegation.go @@ -1,4 +1,4 @@ -package defaults +package rp import ( "github.com/caos/oidc/pkg/oidc/grants/tokenexchange" diff --git a/pkg/rp/defaults/error.go b/pkg/rp/error.go similarity index 99% rename from pkg/rp/defaults/error.go rename to pkg/rp/error.go index f4da915..038aa4a 100644 --- a/pkg/rp/defaults/error.go +++ b/pkg/rp/error.go @@ -1,4 +1,4 @@ -package defaults +package rp import ( "fmt" diff --git a/pkg/rp/tokenexchange/relaying_party.go b/pkg/rp/tockenexchange.go similarity index 92% rename from pkg/rp/tokenexchange/relaying_party.go rename to pkg/rp/tockenexchange.go index 8b1eb22..d84b38e 100644 --- a/pkg/rp/tokenexchange/relaying_party.go +++ b/pkg/rp/tockenexchange.go @@ -1,4 +1,4 @@ -package tokenexchange +package rp import ( "context" @@ -6,12 +6,11 @@ import ( "golang.org/x/oauth2" "github.com/caos/oidc/pkg/oidc/grants/tokenexchange" - "github.com/caos/oidc/pkg/rp" ) //TokenExchangeRP extends the `RelayingParty` interface for the *draft* oauth2 `Token Exchange` type TokenExchangeRP interface { - rp.RelayingParty + RelayingParty //TokenExchange implement the `Token Echange Grant` exchanging some token for an other TokenExchange(context.Context, *tokenexchange.TokenExchangeRequest) (*oauth2.Token, error)