From 64797c1df67510828c89ad532b1312d99ea05cc6 Mon Sep 17 00:00:00 2001 From: Livio Amstutz Date: Wed, 16 Sep 2020 15:22:15 +0200 Subject: [PATCH] cleanup --- example/internal/mock/storage.go | 2 +- .../grants/tokenexchange/tokenexchange.go | 2 +- pkg/op/op.go | 2 +- pkg/op/signer.go | 5 +-- pkg/op/token.go | 1 - pkg/op/tokenrequest.go | 7 ++-- pkg/op/verifier_jwt_profile.go | 20 +++++----- pkg/rp/relaying_party.go | 37 +++++++++---------- pkg/rp/tockenexchange.go | 7 ++-- 9 files changed, 39 insertions(+), 44 deletions(-) diff --git a/example/internal/mock/storage.go b/example/internal/mock/storage.go index d5c0682..f20fb9b 100644 --- a/example/internal/mock/storage.go +++ b/example/internal/mock/storage.go @@ -152,7 +152,7 @@ func (s *AuthStorage) AuthRequestByID(_ context.Context, id string) (op.AuthRequ return a, nil } func (s *AuthStorage) CreateToken(_ context.Context, authReq op.TokenRequest) (string, time.Time, error) { - return "authReq.GetID()", time.Now().UTC().Add(5 * time.Minute), nil + return "id", time.Now().UTC().Add(5 * time.Minute), nil } func (s *AuthStorage) TerminateSession(_ context.Context, userID, clientID string) error { return nil diff --git a/pkg/oidc/grants/tokenexchange/tokenexchange.go b/pkg/oidc/grants/tokenexchange/tokenexchange.go index 4df0e80..9464605 100644 --- a/pkg/oidc/grants/tokenexchange/tokenexchange.go +++ b/pkg/oidc/grants/tokenexchange/tokenexchange.go @@ -23,7 +23,7 @@ type TokenExchangeRequest struct { } type JWTProfileRequest struct { - assertion string `schema:"assertion"` + Assertion string `schema:"assertion"` } func NewTokenExchangeRequest(subjectToken, subjectTokenType string, opts ...TokenExchangeOption) *TokenExchangeRequest { diff --git a/pkg/op/op.go b/pkg/op/op.go index 1d9a750..fa78a6f 100644 --- a/pkg/op/op.go +++ b/pkg/op/op.go @@ -212,7 +212,7 @@ func (o *openidProvider) IDTokenHintVerifier() IDTokenHintVerifier { func (o *openidProvider) JWTProfileVerifier() JWTProfileVerifier { if o.jwtProfileVerifier == nil { - o.jwtProfileVerifier = NewJWTProfileVerifier(o.Storage(), o.Issuer()) + o.jwtProfileVerifier = NewJWTProfileVerifier(o.Storage(), o.Issuer(), 1*time.Hour, time.Second) } return o.jwtProfileVerifier } diff --git a/pkg/op/signer.go b/pkg/op/signer.go index a313934..e9926cd 100644 --- a/pkg/op/signer.go +++ b/pkg/op/signer.go @@ -1,13 +1,12 @@ package op import ( + "context" "encoding/json" "errors" - "golang.org/x/net/context" - "gopkg.in/square/go-jose.v2" - "github.com/caos/logging" + "gopkg.in/square/go-jose.v2" "github.com/caos/oidc/pkg/oidc" ) diff --git a/pkg/op/token.go b/pkg/op/token.go index 7fc95f5..87494b9 100644 --- a/pkg/op/token.go +++ b/pkg/op/token.go @@ -15,7 +15,6 @@ type TokenCreator interface { } type TokenRequest interface { - GetClientID() string GetSubject() string GetAudience() []string GetScopes() []string diff --git a/pkg/op/tokenrequest.go b/pkg/op/tokenrequest.go index 18e7a52..b3613ce 100644 --- a/pkg/op/tokenrequest.go +++ b/pkg/op/tokenrequest.go @@ -6,6 +6,7 @@ import ( "net/http" "github.com/caos/oidc/pkg/oidc" + "github.com/caos/oidc/pkg/oidc/grants/tokenexchange" "github.com/caos/oidc/pkg/utils" ) @@ -161,14 +162,12 @@ func ParseJWTProfileRequest(r *http.Request, decoder utils.Decoder) (string, err if err != nil { return "", ErrInvalidRequest("error parsing form") } - tokenReq := new(struct { - Token string `schema:"assertion"` - }) + tokenReq := new(tokenexchange.JWTProfileRequest) err = decoder.Decode(tokenReq, r.Form) if err != nil { return "", ErrInvalidRequest("error decoding form") } - return tokenReq.Token, nil + return tokenReq.Assertion, nil } func TokenExchange(w http.ResponseWriter, r *http.Request, exchanger Exchanger) { diff --git a/pkg/op/verifier_jwt_profile.go b/pkg/op/verifier_jwt_profile.go index 807700f..b30bdc5 100644 --- a/pkg/op/verifier_jwt_profile.go +++ b/pkg/op/verifier_jwt_profile.go @@ -16,14 +16,18 @@ type JWTProfileVerifier interface { } type jwtProfileVerifier struct { - storage Storage - issuer string + storage Storage + issuer string + maxAgeIAT time.Duration + offset time.Duration } -func NewJWTProfileVerifier(storage Storage, issuer string) JWTProfileVerifier { +func NewJWTProfileVerifier(storage Storage, issuer string, maxAgeIAT, offset time.Duration) JWTProfileVerifier { return &jwtProfileVerifier{ - storage: storage, - issuer: issuer, + storage: storage, + issuer: issuer, + maxAgeIAT: maxAgeIAT, + offset: offset, } } @@ -36,13 +40,11 @@ func (v *jwtProfileVerifier) Storage() Storage { } func (v *jwtProfileVerifier) MaxAgeIAT() time.Duration { - //TODO: define in conf/opts - return 1 * time.Hour + return v.maxAgeIAT } func (v *jwtProfileVerifier) Offset() time.Duration { - //TODO: define in conf/opts - return time.Second + return v.offset } func VerifyJWTAssertion(ctx context.Context, assertion string, v JWTProfileVerifier) (*oidc.JWTTokenRequest, error) { diff --git a/pkg/rp/relaying_party.go b/pkg/rp/relaying_party.go index 5d6bb7c..109a3ef 100644 --- a/pkg/rp/relaying_party.go +++ b/pkg/rp/relaying_party.go @@ -6,6 +6,8 @@ import ( "net/http" "strings" + "github.com/google/uuid" + "github.com/caos/oidc/pkg/oidc" "github.com/caos/oidc/pkg/oidc/grants" "github.com/caos/oidc/pkg/utils" @@ -31,12 +33,16 @@ type RelayingParty interface { //CookieHandler returns a http cookie handler used for various state transfer cookies CookieHandler() *utils.CookieHandler - //Client return a standard http client where the token can be used - Client(ctx context.Context, token *oauth2.Token) *http.Client - + //HttpClient returns a http client used for calls to the openid provider, e.g. calling token endpoint HttpClient() *http.Client + + //IsOAuth2Only specifies whether relaying party handles only oauth2 or oidc calls IsOAuth2Only() bool + + //IDTokenVerifier returns the verifier interface used for oidc id_token verification IDTokenVerifier() IDTokenVerifier + + //ErrorHandler returns the handler used for callback errors ErrorHandler() func(http.ResponseWriter, *http.Request, string, string, string) } @@ -90,10 +96,6 @@ func (rp *relayingParty) IDTokenVerifier() IDTokenVerifier { return rp.idTokenVerifier } -func (rp *relayingParty) Client(ctx context.Context, token *oauth2.Token) *http.Client { - return rp.oauthConfig.Client(ctx, token) -} - func (rp *relayingParty) ErrorHandler() func(http.ResponseWriter, *http.Request, string, string, string) { if rp.errorHandler == nil { rp.errorHandler = DefaultErrorHandler @@ -234,18 +236,17 @@ func AuthURLHandler(stateFn func() string, rp RelayingParty) http.HandlerFunc { } } +//GenerateAndStoreCodeChallenge generates a PKCE code challenge and stores its verifier into a secure cookie func GenerateAndStoreCodeChallenge(w http.ResponseWriter, rp RelayingParty) (string, error) { - var codeVerifier string - codeVerifier = "s" + codeVerifier := uuid.New().String() if err := rp.CookieHandler().SetCookie(w, pkceCode, codeVerifier); err != nil { return "", err } return oidc.NewSHACodeChallenge(codeVerifier), nil } -//AuthURL is the `RelayingParty` interface implementation -//handling the oauth2 code exchange, extracting and validating the id_token -//returning it paresed together with the oauth2 tokens (access, refresh) +//CodeExchange handles the oauth2 code exchange, extracting and validating the id_token +//returning it parsed together with the oauth2 tokens (access, refresh) func CodeExchange(ctx context.Context, code string, rp RelayingParty, opts ...CodeExchangeOpt) (tokens *oidc.Tokens, err error) { ctx = context.WithValue(ctx, oauth2.HTTPClient, rp.HttpClient()) codeOpts := make([]oauth2.AuthCodeOption, 0) @@ -255,7 +256,7 @@ func CodeExchange(ctx context.Context, code string, rp RelayingParty, opts ...Co token, err := rp.OAuthConfig().Exchange(ctx, code, codeOpts...) if err != nil { - return nil, err //TODO: our error + return nil, err } if rp.IsOAuth2Only() { @@ -275,8 +276,9 @@ func CodeExchange(ctx context.Context, code string, rp RelayingParty, opts ...Co return &oidc.Tokens{Token: token, IDTokenClaims: idToken, IDToken: idTokenString}, nil } -//AuthURL is the `RelayingParty` interface implementation -//extending the `CodeExchange` method with callback function +//CodeExchangeHandler extends the `CodeExchange` method with a http handler +//including cookie handling for secure `state` transfer +//and optional PKCE code verifier checking func CodeExchangeHandler(callback func(http.ResponseWriter, *http.Request, *oidc.Tokens, string), rp RelayingParty) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { state, err := tryReadStateCookie(w, r, rp) @@ -365,11 +367,6 @@ func tryReadStateCookie(w http.ResponseWriter, r *http.Request, rp RelayingParty return state, nil } -type Configuration struct { - Issuer string - *oauth2.Config -} - type OptionFunc func(RelayingParty) type Endpoints struct { diff --git a/pkg/rp/tockenexchange.go b/pkg/rp/tockenexchange.go index 62913e0..dfcd2cd 100644 --- a/pkg/rp/tockenexchange.go +++ b/pkg/rp/tockenexchange.go @@ -32,18 +32,17 @@ type DelegationTokenExchangeRP interface { DelegationTokenExchange(context.Context, string, ...tokenexchange.TokenExchangeOption) (*oauth2.Token, error) } -//TokenExchange is the `TokenExchangeRP` interface implementation -//handling the oauth2 token exchange (draft) +//TokenExchange handles the oauth2 token exchange func TokenExchange(ctx context.Context, request *tokenexchange.TokenExchangeRequest, rp RelayingParty) (newToken *oauth2.Token, err error) { return CallTokenEndpoint(request, rp) } -//DelegationTokenExchange is the `TokenExchangeRP` interface implementation -//handling the oauth2 token exchange for a delegation token (draft) +//DelegationTokenExchange handles the oauth2 token exchange for a delegation token func DelegationTokenExchange(ctx context.Context, subjectToken string, rp RelayingParty, reqOpts ...tokenexchange.TokenExchangeOption) (newToken *oauth2.Token, err error) { return TokenExchange(ctx, DelegationTokenRequest(subjectToken, reqOpts...), rp) } +//JWTProfileExchange handles the oauth2 jwt profile exchange func JWTProfileExchange(ctx context.Context, assertion *oidc.JWTProfileAssertion, rp RelayingParty) (*oauth2.Token, error) { token, err := generateJWTProfileToken(assertion) if err != nil {