diff --git a/internal/boxcli/auth.go b/internal/boxcli/auth.go deleted file mode 100644 index 044742de7f4..00000000000 --- a/internal/boxcli/auth.go +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright 2024 Jetify Inc. and contributors. All rights reserved. -// Use of this source code is governed by the license in the LICENSE file. - -package boxcli - -import ( - "fmt" - "strings" - - "github.com/olekukonko/tablewriter" - "github.com/spf13/cobra" - "go.jetify.com/devbox/internal/build" - "go.jetify.com/devbox/internal/devbox/providers/identity" - "go.jetify.com/devbox/internal/ux" - "go.jetify.com/pkg/api" -) - -func authCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "auth", - Short: "Devbox auth commands", - } - - cmd.AddCommand(loginCmd()) - cmd.AddCommand(logoutCmd()) - cmd.AddCommand(authNewTokenCommand()) - - return cmd -} - -func loginCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "login", - Short: "Login to devbox", - Args: cobra.ExactArgs(0), - RunE: func(cmd *cobra.Command, args []string) error { - c, err := identity.AuthClient(identity.AuthRedirectDefault) - if err != nil { - return err - } - t, err := c.LoginFlow() - if err != nil { - return err - } - // TODO: all uses of IDClaims() are broken when using a static - // non-expiring token (i.e. API_TOKEN) - fmt.Fprintf(cmd.ErrOrStderr(), "Logged in as: %s\n", t.IDClaims().Email) - return nil - }, - } - - return cmd -} - -func logoutCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "logout", - Short: "Logout from devbox", - Args: cobra.ExactArgs(0), - RunE: func(cmd *cobra.Command, args []string) error { - c, err := identity.AuthClient(identity.AuthRedirectDefault) - if err != nil { - return err - } - err = c.LogoutFlow() - if err == nil { - fmt.Fprintln(cmd.OutOrStdout(), "Logged out successfully") - } - return err - }, - } - - return cmd -} - -func authNewTokenCommand() *cobra.Command { - tokensCmd := &cobra.Command{ - Use: "tokens", - Short: "Manage devbox auth tokens", - } - - newCmd := &cobra.Command{ - Use: "new", - Short: "Create a new token", - Args: cobra.ExactArgs(0), - RunE: func(cmd *cobra.Command, args []string) error { - ctx := cmd.Context() - token, err := identity.GenSession(ctx) - if err != nil { - return err - } - client := api.NewClient(ctx, build.JetpackAPIHost(), token) - pat, err := client.CreateToken(ctx) - if err != nil { - // This is a hack because errors are not returning with correct code. - // Once that is fixed, we can switch to use *connect.Error Code() instead. - if strings.Contains(err.Error(), "permission_denied") { - ux.Ferrorf( - cmd.ErrOrStderr(), - "You do not have permission to create a token. Please contact your"+ - " administrator.", - ) - return nil - } - return err - } - ux.Fsuccessf(cmd.OutOrStdout(), "Token created.\n\n") - table := tablewriter.NewWriter(cmd.OutOrStdout()) - // Row lines are configured through the renderer in the new API - if err := table.Bulk([][]string{ - {"Token ID", pat.GetToken().GetId()}, - {"Secret", pat.GetToken().GetSecret()}, - }); err != nil { - return err - } - return table.Render() - }, - } - - tokensCmd.AddCommand(newCmd) - - return tokensCmd -} diff --git a/internal/boxcli/featureflag/auth.go b/internal/boxcli/featureflag/auth.go deleted file mode 100644 index 4aa43a94546..00000000000 --- a/internal/boxcli/featureflag/auth.go +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright 2024 Jetify Inc. and contributors. All rights reserved. -// Use of this source code is governed by the license in the LICENSE file. - -package featureflag - -var Auth = enable("AUTH") diff --git a/internal/boxcli/root.go b/internal/boxcli/root.go index 02126786917..6e86a1180bd 100644 --- a/internal/boxcli/root.go +++ b/internal/boxcli/root.go @@ -13,7 +13,6 @@ import ( "github.com/spf13/cobra" - "go.jetify.com/devbox/internal/boxcli/featureflag" "go.jetify.com/devbox/internal/boxcli/midcobra" "go.jetify.com/devbox/internal/cmdutil" "go.jetify.com/devbox/internal/debug" @@ -55,9 +54,6 @@ func RootCmd() *cobra.Command { // Stable commands command.AddCommand(addCmd()) - if featureflag.Auth.Enabled() { - command.AddCommand(authCmd()) - } command.AddCommand(createCmd()) command.AddCommand(generateCmd()) command.AddCommand(globalCmd()) diff --git a/internal/build/build.go b/internal/build/build.go index c5320979819..7f7033d81a4 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -63,45 +63,6 @@ func OS() string { return osName } -func Issuer() string { - if IsDev { - return "https://laughing-agnesi-vzh2rap9f6.projects.oryapis.com" - } - return "https://accounts.jetify.com" -} - -func ClientID() string { - if IsDev { - return "3945b320-bd31-4313-af27-846b67921acb" - } - return "ff3d4c9c-1ac8-42d9-bef1-f5218bb1a9f6" -} - -func JetpackAPIHost() string { - if IsDev { - return "https://api.jetpack.dev" - } - return "https://api.jetpack.io" -} - -func SuccessRedirect() string { - if IsDev { - return "https://auth.dev-jetify.com/account/login/success" - } - return "https://auth.jetify.com/account/login/success" -} - -func Audience() []string { - return []string{"https://api.jetpack.io"} -} - -func DashboardHostname() string { - if IsDev { - return "http://localhost:8080" - } - return "https://cloud.jetify.com" -} - // SourceDir searches for the source code directory that built the current // binary. func SourceDir() (string, error) { diff --git a/internal/devbox/providers/identity/identity.go b/internal/devbox/providers/identity/identity.go deleted file mode 100644 index 54242018e75..00000000000 --- a/internal/devbox/providers/identity/identity.go +++ /dev/null @@ -1,162 +0,0 @@ -package identity - -import ( - "context" - "errors" - "fmt" - "os" - "path" - "strings" - - "github.com/go-jose/go-jose/v4" - "github.com/go-jose/go-jose/v4/jwt" - "go.jetify.com/devbox/internal/build" - "go.jetify.com/devbox/internal/ux" - "go.jetify.com/pkg/api" - "go.jetify.com/pkg/auth" - "go.jetify.com/pkg/auth/session" - "go.jetify.com/pkg/ids" - "go.jetify.com/typeid/v2" - "golang.org/x/oauth2" -) - -// Common redirect URLs for use with [AuthClient]. -var ( - // AuthRedirectDefault redirects to a generic success page. - AuthRedirectDefault = build.SuccessRedirect() - - // AuthRedirectCache redirects to the "Cache" tab in the dashboard for - // the authenticated organization. - AuthRedirectCache = path.Join(build.DashboardHostname(), "team", "cache") -) - -var scopes = []string{"openid", "offline_access", "email", "profile"} - -var cachedAccessTokenFromAPIToken *session.Token - -// parseAPIToken parses an API token string following the same pattern as other Parse functions -func parseAPIToken(s string) (ids.APIToken, error) { - var zero ids.APIToken - tid, err := typeid.Parse(s) - if err != nil { - return zero, err - } - if tid.Prefix() != ids.APITokenPrefix { - return zero, fmt.Errorf("invalid api_token ID: %s", s) - } - return ids.APIToken{TypeID: tid}, nil -} - -func GenSession(ctx context.Context) (*session.Token, error) { - if t, err := getAccessTokenFromAPIToken(ctx); err != nil || t != nil { - return t, err - } - - c, err := AuthClient(AuthRedirectDefault) - if err != nil { - return nil, err - } - tok, err := c.GetSession(ctx) - if IsRefreshTokenError(err) { - ux.Fwarningf(os.Stderr, "Your session is expired. Please login again.\n") - return c.LoginFlow() - } - return tok, err -} - -func Peek() (*session.Token, error) { - if cachedAccessTokenFromAPIToken != nil { - return cachedAccessTokenFromAPIToken, nil - } - - c, err := AuthClient(AuthRedirectDefault) - if err != nil { - return nil, err - } - tokens, err := c.GetSessions() - if err != nil { - return nil, err - } - - if len(tokens) == 0 { - return nil, auth.ErrNotLoggedIn - } - - return tokens[0].Peek(), nil -} - -// AuthClient returns a new client that redirects to a given URL upon success. -func AuthClient(redirect string) (*auth.Client, error) { - return auth.NewClient( - build.Issuer(), - build.ClientID(), - scopes, - redirect, - build.Audience(), - ) -} - -func getAccessTokenFromAPIToken( - ctx context.Context, -) (*session.Token, error) { - if cachedAccessTokenFromAPIToken == nil { - apiTokenRaw := os.Getenv("DEVBOX_API_TOKEN") - if apiTokenRaw == "" { - return nil, nil - } - - apiToken, err := parseAPIToken(apiTokenRaw) - if err != nil { - return nil, err - } - - apiClient := api.NewClient(ctx, build.JetpackAPIHost(), &session.Token{}) - response, err := apiClient.GetAccessToken(ctx, apiToken) - if err != nil { - return nil, err - } - - // This is not the greatest. This token is missing id, refresh, etc. - // It may be better to change api.NewClient() to take a token string instead. - cachedAccessTokenFromAPIToken = &session.Token{ - Token: oauth2.Token{ - AccessToken: response.AccessToken, - }, - } - } - - return cachedAccessTokenFromAPIToken, nil -} - -func GetOrgSlug(ctx context.Context) (string, error) { - tok, err := GenSession(ctx) - if err != nil { - return "", err - } - - if tok.IDToken == "" { - return "", errors.New("ID token is not present") - } - - jwt, err := jwt.ParseSigned(tok.IDToken, []jose.SignatureAlgorithm{jose.RS256}) - if err != nil { - return "", err - } - - claims := map[string]any{} - if err = jwt.UnsafeClaimsWithoutVerification(&claims); err != nil { - return "", err - } - - return claims["org_trusted_metadata"].(map[string]any)["slug"].(string), nil -} - -// invalid_grant or invalid_request usually means the refresh token is expired, revoked, or -// malformed. this belongs in opensource/pkg/auth -func IsRefreshTokenError(err error) bool { - if err == nil { - return false - } - return strings.Contains(err.Error(), "invalid_grant") || - strings.Contains(err.Error(), "invalid_request") -} diff --git a/internal/telemetry/segment.go b/internal/telemetry/segment.go index c5f04f569ac..358aac4d65a 100644 --- a/internal/telemetry/segment.go +++ b/internal/telemetry/segment.go @@ -68,7 +68,6 @@ func newTrackMessage(name string, meta Metadata) *segment.Track { "command_args": meta.CommandFlags, "duration": dur.Milliseconds(), "nix_version": nixVersion, - "org_id": orgID(), "packages": meta.Packages, "shell": os.Getenv(envir.Shell), "shell_access": shellAccess(), diff --git a/internal/telemetry/telemetry.go b/internal/telemetry/telemetry.go index 41471aaff37..63773ebbdf3 100644 --- a/internal/telemetry/telemetry.go +++ b/internal/telemetry/telemetry.go @@ -26,7 +26,6 @@ import ( "github.com/pkg/errors" segment "github.com/segmentio/analytics-go" "go.jetify.com/devbox/internal/boxcli/usererr" - "go.jetify.com/devbox/internal/devbox/providers/identity" "go.jetify.com/devbox/nix" "go.jetify.com/devbox/internal/build" @@ -68,11 +67,6 @@ func Start() { } func userID() string { - // TODO, once we add access token parsing, use that instead of id token. - // that will work with API_TOKEN as well. - if tok, err := identity.Peek(); err == nil && tok.IDClaims() != nil { - return tok.IDClaims().Subject - } if username := os.Getenv(envir.GitHubUsername); username != "" { const uidSalt = "d6134cd5-347d-4b7c-a2d0-295c0f677948" const githubPrefix = "github:" @@ -84,15 +78,6 @@ func userID() string { return "" } -func orgID() string { - // TODO, once we add access token parsing, use that instead of id token. - // that will work with API_TOKEN as well. - if tok, err := identity.Peek(); err == nil && tok.IDClaims() != nil { - return tok.IDClaims().OrgID - } - return "" -} - // Stop stops gathering telemetry and flushes buffered events to disk. func Stop() { if !started || !needsFlush.Load() {