From b99bb6b89549615e69514d82858d2552936cd92f Mon Sep 17 00:00:00 2001 From: Sean Trantalis Date: Mon, 26 Aug 2024 12:09:34 -0400 Subject: [PATCH 1/3] fix: create new http client to ignore tls verification --- pkg/auth/auth.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go index a6d4e691..23008b64 100644 --- a/pkg/auth/auth.go +++ b/pkg/auth/auth.go @@ -3,9 +3,11 @@ package auth import ( "context" "crypto/rand" + "crypto/tls" "encoding/json" "errors" "fmt" + "net/http" "net/url" "os" "time" @@ -190,7 +192,16 @@ func GetTokenWithClientCreds(ctx context.Context, endpoint string, clientId stri return nil, err } - rp, err := oidcrp.NewRelyingPartyOIDC(ctx, pc.issuer, clientId, clientSecret, "", []string{"email"}) + // Create a new HTTP client with the ability to skip TLS verification + client := &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: tlsNoVerify, + }, + }, + } + + rp, err := oidcrp.NewRelyingPartyOIDC(ctx, pc.issuer, clientId, clientSecret, "", []string{"email"}, oidcrp.WithHTTPClient(client)) if err != nil { return nil, err } From aff117c0e704886e06a38d3241952026033b34e1 Mon Sep 17 00:00:00 2001 From: Ryan Schumacher Date: Mon, 26 Aug 2024 21:42:30 -0500 Subject: [PATCH 2/3] Tweaks Close #340 Close #316 --- .gitignore | 4 +++ cmd/root.go | 16 ++++++++--- docs/man/_index.md | 3 +++ pkg/auth/auth.go | 67 ++++++++++++++++++++++++++++++---------------- pkg/utils/http.go | 16 +++++++++++ 5 files changed, 80 insertions(+), 26 deletions(-) create mode 100644 pkg/utils/http.go diff --git a/.gitignore b/.gitignore index c3c02e6c..7c83eab3 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,10 @@ otdfctl.yaml # Ignore the tructl binary otdfctl +otdfctl_testbuild + +# Misc +creds.json # Hugo public/ diff --git a/cmd/root.go b/cmd/root.go index 888339aa..1da73a41 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -66,6 +66,8 @@ func InitProfile(cmd *cobra.Command, onlyNew bool) *profiles.ProfileStore { // TODO make this a preRun hook func NewHandler(cmd *cobra.Command) handlers.Handler { fh := cli.NewFlagHelper(cmd) + + // Non-profile flags host := fh.GetOptionalString("host") tlsNoVerify := fh.GetOptionalBool("tls-no-verify") withClientCreds := fh.GetOptionalString("with-client-creds") @@ -73,9 +75,11 @@ func NewHandler(cmd *cobra.Command) handlers.Handler { // if global flags are set then validate and create a temporary profile in memory var cp *profiles.ProfileStore - if host != "" || withClientCreds != "" || withClientCredsFile != "" { - err := errors.New("when using global flags --host, --with-client-creds, or --with-client-creds-file, " + - "profiles will not be used and all required flags must be set") + if host != "" || tlsNoVerify || withClientCreds != "" || withClientCredsFile != "" { + err := errors.New( + "when using global flags --host, --tls-no-verify, --with-client-creds, or --with-client-creds-file, " + + "profiles will not be used and all required flags must be set", + ) // host must be set if host == "" { @@ -170,6 +174,12 @@ func init() { rootCmd.GetDocFlag("version").Description, ) + RootCmd.PersistentFlags().String( + rootCmd.GetDocFlag("profile").Name, + rootCmd.GetDocFlag("profile").Default, + rootCmd.GetDocFlag("profile").Description, + ) + RootCmd.PersistentFlags().String( rootCmd.GetDocFlag("host").Name, rootCmd.GetDocFlag("host").Default, diff --git a/docs/man/_index.md b/docs/man/_index.md index afd73fc2..4173ae86 100644 --- a/docs/man/_index.md +++ b/docs/man/_index.md @@ -7,6 +7,9 @@ command: - name: version description: show version default: false + - name: profile + description: profile to use for interacting with the platform + default: - name: host description: Hostname of the platform (i.e. https://localhost) default: diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go index 23008b64..27ea24d2 100644 --- a/pkg/auth/auth.go +++ b/pkg/auth/auth.go @@ -3,11 +3,9 @@ package auth import ( "context" "crypto/rand" - "crypto/tls" "encoding/json" "errors" "fmt" - "net/http" "net/url" "os" "time" @@ -40,6 +38,12 @@ type platformConfiguration struct { publicClientID string } +type oidcClientCredentials struct { + clientID string + clientSecret string + isPublic bool +} + // Retrieves credentials by reading specified file func GetClientCredsFromFile(filepath string) (ClientCredentials, error) { creds := ClientCredentials{} @@ -187,25 +191,13 @@ func GetTokenWithProfile(ctx context.Context, profile *profiles.ProfileStore) (* // Uses the OAuth2 client credentials flow to obtain a token. func GetTokenWithClientCreds(ctx context.Context, endpoint string, clientId string, clientSecret string, tlsNoVerify bool) (*oauth2.Token, error) { - pc, err := getPlatformConfiguration(endpoint, "", tlsNoVerify) - if err != nil && !errors.Is(err, sdk.ErrPlatformPublicClientIDNotFound) { - return nil, err - } - - // Create a new HTTP client with the ability to skip TLS verification - client := &http.Client{ - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{ - InsecureSkipVerify: tlsNoVerify, - }, - }, - } - - rp, err := oidcrp.NewRelyingPartyOIDC(ctx, pc.issuer, clientId, clientSecret, "", []string{"email"}, oidcrp.WithHTTPClient(client)) + rp, err := newOidcRelyingParty(ctx, endpoint, tlsNoVerify, oidcClientCredentials{ + clientID: clientId, + clientSecret: clientSecret, + }) if err != nil { return nil, err } - return oidcrp.ClientCredentials(ctx, rp, url.Values{}) } @@ -279,15 +271,44 @@ func LoginWithPKCE(host, publicClientID string, tlsNoVerify bool) (*oauth2.Token // Revokes the access token func RevokeAccessToken(endpoint, publicClientID, refreshToken string, tlsNoVerify bool) error { - pCfg, err := getPlatformConfiguration(endpoint, publicClientID, tlsNoVerify) + rp, err := newOidcRelyingParty(context.Background(), endpoint, tlsNoVerify, oidcClientCredentials{ + clientID: publicClientID, + isPublic: true, + }) if err != nil { - return fmt.Errorf("failed to get platform configuration: %w", err) + return err + } + return oidcrp.RevokeToken(context.Background(), rp, refreshToken, "refresh_token") +} + +func newOidcRelyingParty(ctx context.Context, endpoint string, tlsNoVerify bool, clientCreds oidcClientCredentials) (oidcrp.RelyingParty, error) { + if clientCreds.clientID == "" { + return nil, errors.New("client ID is required") + } + if clientCreds.clientSecret == "" && !clientCreds.isPublic { + return nil, errors.New("client secret is required") + } + if clientCreds.clientSecret != "" && clientCreds.isPublic { + return nil, errors.New("client secret must be empty for public clients") } - rp, err := oidcrp.NewRelyingPartyOIDC(context.Background(), pCfg.issuer, pCfg.publicClientID, "", "", nil) + var pcClient string + if clientCreds.isPublic { + pcClient = clientCreds.clientID + } + + pc, err := getPlatformConfiguration(endpoint, pcClient, tlsNoVerify) if err != nil { - return err + return nil, err } - return oidcrp.RevokeToken(context.Background(), rp, refreshToken, "refresh_token") + return oidcrp.NewRelyingPartyOIDC( + ctx, + pc.issuer, + clientCreds.clientID, + clientCreds.clientSecret, + "", + nil, + oidcrp.WithHTTPClient(utils.NewHttpClient(tlsNoVerify)), + ) } diff --git a/pkg/utils/http.go b/pkg/utils/http.go new file mode 100644 index 00000000..48efa113 --- /dev/null +++ b/pkg/utils/http.go @@ -0,0 +1,16 @@ +package utils + +import ( + "crypto/tls" + "net/http" +) + +func NewHttpClient(tlsNoVerify bool) *http.Client { + return &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: tlsNoVerify, + }, + }, + } +} From fa9a35eb77e5edf82d095752f5d11aad1f01a1d1 Mon Sep 17 00:00:00 2001 From: Ryan Schumacher Date: Tue, 27 Aug 2024 08:34:10 -0500 Subject: [PATCH 3/3] Pass cmd contexts --- cmd/auth-login.go | 8 ++++---- cmd/auth-logout.go | 8 +++++++- pkg/auth/auth.go | 13 ++++++------- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/cmd/auth-login.go b/cmd/auth-login.go index 66d45730..0e7a093d 100644 --- a/cmd/auth-login.go +++ b/cmd/auth-login.go @@ -17,7 +17,7 @@ func auth_codeLogin(cmd *cobra.Command, args []string) { printer := cli.NewPrinter(true) printer.Println("Initiating login...") - tok, publicClientID, err := auth.LoginWithPKCE(cp.GetEndpoint(), clientID, tlsNoVerify) + tok, publicClientID, err := auth.LoginWithPKCE(cmd.Context(), cp.GetEndpoint(), clientID, tlsNoVerify) if err != nil { cli.ExitWithError("could not authenticate", err) } @@ -28,9 +28,9 @@ func auth_codeLogin(cmd *cobra.Command, args []string) { AuthType: profiles.PROFILE_AUTH_TYPE_ACCESS_TOKEN, AccessToken: profiles.AuthCredentialsAccessToken{ PublicClientID: publicClientID, - AccessToken: tok.AccessToken, - Expiration: tok.Expiry.Unix(), - RefreshToken: tok.RefreshToken, + AccessToken: tok.AccessToken, + Expiration: tok.Expiry.Unix(), + RefreshToken: tok.RefreshToken, }, }); err != nil { cli.ExitWithError("failed to set auth credentials", err) diff --git a/cmd/auth-logout.go b/cmd/auth-logout.go index 6b850109..d161b6da 100644 --- a/cmd/auth-logout.go +++ b/cmd/auth-logout.go @@ -19,7 +19,13 @@ func auth_logout(cmd *cobra.Command, args []string) { creds := cp.GetAuthCredentials() if creds.AuthType == profiles.PROFILE_AUTH_TYPE_ACCESS_TOKEN { printer.Println("Revoking access token...") - if err := auth.RevokeAccessToken(cp.GetEndpoint(), creds.AccessToken.PublicClientID, creds.AccessToken.RefreshToken, tlsNoVerify); err != nil { + if err := auth.RevokeAccessToken( + cmd.Context(), + cp.GetEndpoint(), + creds.AccessToken.PublicClientID, + creds.AccessToken.RefreshToken, + tlsNoVerify, + ); err != nil { printer.Println("failed") cli.ExitWithError("An error occurred while revoking the access token", err) } diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go index 27ea24d2..4b9c7df7 100644 --- a/pkg/auth/auth.go +++ b/pkg/auth/auth.go @@ -203,7 +203,7 @@ func GetTokenWithClientCreds(ctx context.Context, endpoint string, clientId stri // Facilitates an auth code PKCE flow to obtain OIDC tokens. // Spawns a local server to handle the callback and opens a browser window in each respective OS. -func Login(platformEndpoint, tokenURL, authURL, publicClientID string) (*oauth2.Token, error) { +func Login(ctx context.Context, platformEndpoint, tokenURL, authURL, publicClientID string) (*oauth2.Token, error) { // Generate random hash and encryption keys for cookie handling hashKey := make([]byte, 16) encryptKey := make([]byte, 16) @@ -228,7 +228,6 @@ func Login(platformEndpoint, tokenURL, authURL, publicClientID string) (*oauth2. }, } - ctx := context.Background() cookiehandler := httphelper.NewCookieHandler(hashKey, encryptKey) relyingParty, err := oidcrp.NewRelyingPartyOAuth(conf, @@ -255,13 +254,13 @@ func Login(platformEndpoint, tokenURL, authURL, publicClientID string) (*oauth2. } // Logs in using the auth code PKCE flow driven by the platform well-known idP OIDC configuration. -func LoginWithPKCE(host, publicClientID string, tlsNoVerify bool) (*oauth2.Token, string, error) { +func LoginWithPKCE(ctx context.Context, host, publicClientID string, tlsNoVerify bool) (*oauth2.Token, string, error) { pc, err := getPlatformConfiguration(host, publicClientID, tlsNoVerify) if err != nil { return nil, "", fmt.Errorf("failed to get platform configuration: %w", err) } - tok, err := Login(host, pc.tokenEndpoint, pc.authzEndpoint, pc.publicClientID) + tok, err := Login(ctx, host, pc.tokenEndpoint, pc.authzEndpoint, pc.publicClientID) if err != nil { return nil, "", fmt.Errorf("failed to login: %w", err) } @@ -270,15 +269,15 @@ func LoginWithPKCE(host, publicClientID string, tlsNoVerify bool) (*oauth2.Token } // Revokes the access token -func RevokeAccessToken(endpoint, publicClientID, refreshToken string, tlsNoVerify bool) error { - rp, err := newOidcRelyingParty(context.Background(), endpoint, tlsNoVerify, oidcClientCredentials{ +func RevokeAccessToken(ctx context.Context, endpoint, publicClientID, refreshToken string, tlsNoVerify bool) error { + rp, err := newOidcRelyingParty(ctx, endpoint, tlsNoVerify, oidcClientCredentials{ clientID: publicClientID, isPublic: true, }) if err != nil { return err } - return oidcrp.RevokeToken(context.Background(), rp, refreshToken, "refresh_token") + return oidcrp.RevokeToken(ctx, rp, refreshToken, "refresh_token") } func newOidcRelyingParty(ctx context.Context, endpoint string, tlsNoVerify bool, clientCreds oidcClientCredentials) (oidcrp.RelyingParty, error) {