Skip to content

Commit ea6f1db

Browse files
Integralistphamann
andauthored
fix(sso): re-auth on profile switch + support MAUA (#1226)
* fix(sso): add select_account for MAUA (multiple account user auth) * fix(sso): re-auth * refactor(sso): only set hints if non-empty string * refactor(sso): only set select_account for profile create * refactor(sso): update code and tests to use /current_customer endpoint * refactor(sso): update error message to reflect new API call Co-authored-by: Patrick Hamann <[email protected]> * fix(sso): remove unnecessary header * doc(sso): correct CurrentCustomerResponse annotation * doc(testutil): correct CurrentCustomerClient/CurrentCustomerResponse annotation * fix(profile/list): use corrert format type for output * fix(profile/delete): spacing around success output * fix(profile/list): spacing around success output * fix(profile/switch): make sure auth server is running --------- Co-authored-by: Patrick Hamann <[email protected]>
1 parent 4aecc67 commit ea6f1db

File tree

11 files changed

+253
-22
lines changed

11 files changed

+253
-22
lines changed

pkg/app/run.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -622,7 +622,7 @@ func commandCollectsData(command string) bool {
622622
// requires just the authentication server to be running.
623623
func commandRequiresAuthServer(command string) bool {
624624
switch command {
625-
case "profile create", "profile update":
625+
case "profile create", "profile switch", "profile update":
626626
return true
627627
}
628628
return false

pkg/commands/commands.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@ func Define(
354354
profileCreate := profile.NewCreateCommand(profileCmdRoot.CmdClause, data, ssoCmdRoot)
355355
profileDelete := profile.NewDeleteCommand(profileCmdRoot.CmdClause, data)
356356
profileList := profile.NewListCommand(profileCmdRoot.CmdClause, data)
357-
profileSwitch := profile.NewSwitchCommand(profileCmdRoot.CmdClause, data)
357+
profileSwitch := profile.NewSwitchCommand(profileCmdRoot.CmdClause, data, ssoCmdRoot)
358358
profileToken := profile.NewTokenCommand(profileCmdRoot.CmdClause, data)
359359
profileUpdate := profile.NewUpdateCommand(profileCmdRoot.CmdClause, data, ssoCmdRoot)
360360
purgeCmdRoot := purge.NewRootCommand(app, data)

pkg/commands/profile/delete.go

+3
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error {
3232
if err := c.Globals.Config.Write(c.Globals.ConfigPath); err != nil {
3333
return err
3434
}
35+
if c.Globals.Verbose() {
36+
text.Break(out)
37+
}
3538
text.Success(out, "Profile '%s' deleted", c.profile)
3639

3740
if _, p := profile.Default(c.Globals.Config.Profiles); p == nil && len(c.Globals.Config.Profiles) > 0 {

pkg/commands/profile/list.go

+7
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error {
5656
if p == nil {
5757
text.Warning(out, profile.NoDefaults)
5858
} else {
59+
if c.Globals.Verbose() {
60+
text.Break(out)
61+
}
5962
text.Info(out, "Default profile highlighted in red.\n\n")
6063
display(name, p, out, text.BoldRed)
6164
}
@@ -76,4 +79,8 @@ func display(k string, v *config.Profile, out io.Writer, style func(a ...any) st
7679
text.Output(out, "%s: %s", style("Email"), v.Email)
7780
text.Output(out, "%s: %s", style("Token"), v.Token)
7881
text.Output(out, "%s: %t", style("SSO"), !auth.IsLongLivedToken(v))
82+
if !auth.IsLongLivedToken(v) {
83+
text.Output(out, "%s: %s", style("Customer ID"), v.CustomerID)
84+
text.Output(out, "%s: %s", style("Customer Name"), v.CustomerName)
85+
}
7986
}

pkg/commands/profile/profile_test.go

+4
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,8 @@ func TestProfileList(t *testing.T) {
437437
"access_token": "",
438438
"access_token_created": 0,
439439
"access_token_ttl": 0,
440+
"customer_id": "",
441+
"customer_name": "",
440442
"default": false,
441443
"email": "[email protected]",
442444
"refresh_token": "",
@@ -448,6 +450,8 @@ func TestProfileList(t *testing.T) {
448450
"access_token": "",
449451
"access_token_created": 0,
450452
"access_token_ttl": 0,
453+
"customer_id": "",
454+
"customer_name": "",
451455
"default": false,
452456
"email": "[email protected]",
453457
"refresh_token": "",

pkg/commands/profile/switch.go

+33-6
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"io"
77

88
"github.com/fastly/cli/pkg/argparser"
9+
"github.com/fastly/cli/pkg/commands/sso"
910
"github.com/fastly/cli/pkg/global"
1011
"github.com/fastly/cli/pkg/profile"
1112
"github.com/fastly/cli/pkg/text"
@@ -16,32 +17,58 @@ type SwitchCommand struct {
1617
argparser.Base
1718

1819
profile string
20+
ssoCmd *sso.RootCommand
1921
}
2022

2123
// NewSwitchCommand returns a usable command registered under the parent.
22-
func NewSwitchCommand(parent argparser.Registerer, g *global.Data) *SwitchCommand {
24+
func NewSwitchCommand(parent argparser.Registerer, g *global.Data, ssoCmd *sso.RootCommand) *SwitchCommand {
2325
var c SwitchCommand
2426
c.Globals = g
27+
c.ssoCmd = ssoCmd
2528
c.CmdClause = parent.Command("switch", "Switch user profile")
2629
c.CmdClause.Arg("profile", "Profile to switch to").Short('p').Required().StringVar(&c.profile)
2730
return &c
2831
}
2932

3033
// Exec invokes the application logic for the command.
31-
func (c *SwitchCommand) Exec(_ io.Reader, out io.Writer) error {
32-
var ok bool
34+
func (c *SwitchCommand) Exec(in io.Reader, out io.Writer) error {
35+
// We get the named profile to check if it's an SSO-based profile.
36+
// If we're switching to an SSO-based profile, then we need to re-auth.
37+
p := profile.Get(c.profile, c.Globals.Config.Profiles)
38+
if p == nil {
39+
err := fmt.Errorf(profile.DoesNotExist, c.profile)
40+
c.Globals.ErrLog.Add(err)
41+
return err
42+
}
43+
if isSSOToken(p) {
44+
// IMPORTANT: We need to set profile fields for `sso` command.
45+
//
46+
// This is so the `sso` command will use this information to trigger the
47+
// correct authentication flow.
48+
c.ssoCmd.InvokedFromProfileSwitch = true
49+
c.ssoCmd.ProfileSwitchName = c.profile
50+
c.ssoCmd.ProfileSwitchEmail = p.Email
51+
c.ssoCmd.ProfileSwitchCustomerID = p.CustomerID
52+
c.ssoCmd.ProfileDefault = true
53+
54+
err := c.ssoCmd.Exec(in, out)
55+
if err != nil {
56+
return fmt.Errorf("failed to authenticate: %w", err)
57+
}
58+
text.Success(out, "\nProfile switched to '%s'", c.profile)
59+
return nil
60+
}
3361

3462
// We call SetDefault for its side effect of resetting all other profiles to have
3563
// their Default field set to false.
36-
p, ok := profile.SetDefault(c.profile, c.Globals.Config.Profiles)
64+
ps, ok := profile.SetDefault(c.profile, c.Globals.Config.Profiles)
3765
if !ok {
3866
msg := fmt.Sprintf(profile.DoesNotExist, c.profile)
3967
err := errors.New(msg)
4068
c.Globals.ErrLog.Add(err)
4169
return err
4270
}
43-
44-
c.Globals.Config.Profiles = p
71+
c.Globals.Config.Profiles = ps
4572

4673
if err := c.Globals.Config.Write(c.Globals.ConfigPath); err != nil {
4774
c.Globals.ErrLog.Add(err)

0 commit comments

Comments
 (0)