diff --git a/internal/cli/cli.go b/internal/cli/cli.go index 6c82ef0a0..8ef80e8ec 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -111,6 +111,13 @@ func (c *cli) setupWithAuthentication(ctx context.Context) error { } } + if errors.Is(err, config.ErrMalformedToken) { + return fmt.Errorf("authentication token is corrupted, please run: %s\n\n%s", + ansi.Cyan("auth0 logout && auth0 login"), + ansi.Yellow("Note: Token handling was enhanced in v1.18.0+ to prevent malformed tokens."), + ) + } + api, err := initializeManagementClient(tenant.Domain, tenant.GetAccessToken()) if err != nil { return err diff --git a/internal/config/tenant.go b/internal/config/tenant.go index 91e3ecafb..de216a0c9 100644 --- a/internal/config/tenant.go +++ b/internal/config/tenant.go @@ -8,6 +8,8 @@ import ( "slices" "time" + "github.com/lestrrat-go/jwx/v2/jwt" + "github.com/auth0/auth0-cli/internal/auth" "github.com/auth0/auth0-cli/internal/keyring" ) @@ -17,6 +19,8 @@ const accessTokenExpThreshold = 5 * time.Minute var ( // ErrInvalidToken is thrown when the token is invalid. ErrInvalidToken = errors.New("token is invalid") + // ErrMalformedToken indicates a corrupted JWT token was found in keyring. + ErrMalformedToken = errors.New("corrupted authentication token detected") ) type ErrTokenMissingRequiredScopes struct { @@ -115,11 +119,16 @@ func (t *Tenant) CheckAuthenticationStatus() error { } accessToken := t.GetAccessToken() - if accessToken != "" && !t.HasExpiredToken() { - return nil + if accessToken == "" || t.HasExpiredToken() { + return ErrInvalidToken + } + + // Validate that the access token is a well-formed JWT token. + if _, err := jwt.ParseInsecure([]byte(accessToken)); err != nil { + return ErrMalformedToken } - return ErrInvalidToken + return nil } // RegenerateAccessToken regenerates the access token for the tenant. diff --git a/internal/keyring/keyring.go b/internal/keyring/keyring.go index 6a946eca4..bdb64a097 100644 --- a/internal/keyring/keyring.go +++ b/internal/keyring/keyring.go @@ -71,6 +71,16 @@ func DeleteSecretsForTenant(tenant string) error { } func StoreAccessToken(tenant, value string) error { + // First, clear any existing chunks to prevent concatenation issues. + for i := 0; i < secretAccessTokenMaxChunks; i++ { + if err := keyring.Delete(secretClientSecret, tenant); err != nil { + if !errors.Is(err, keyring.ErrNotFound) { + return fmt.Errorf("failed to delete client secret from keyring: %s", err) + } + } + } + + // Now store the new token in chunks. chunks := chunk(value, secretAccessTokenChunkSizeInBytes) for i := 0; i < len(chunks); i++ {