Skip to content

Commit c3f2ce4

Browse files
authored
Add managed auth region flags (#253)
## Summary - add `--region` to managed auth connection create, update, and login commands - validate supported regions and show the configured region in connection output - preserve the existing connection region when update and login omit the flag ## Validation - `make test` - `make build` - command-boundary coverage for create, update, and login with explicit and omitted regions - verified region flags in command help output <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > CLI flag plumbing and SDK bump with a small vault authorize type migration; no auth credential or payment logic changes. > > **Overview** > Adds **`--region`** (`us-east`, `eu-west`, `ap-southeast`) to **`kernel auth connections create`**, **`update`**, and **`login`**, wiring the value through to the managed-auth browser config via existing **`parseRegionFlag`** validation. Connection **get/create/update** summaries now show a **Browser Region** row when the API returns one. > > Documentation in **README** matches the create/update/login semantics (defaults, future sessions vs one-off login override). **Tests** cover API mapping, invalid regions, and table output. > > **kernel-go-sdk** is bumped to **v0.103.0**; **vaults** authorize operations use the new SDK request types instead of **`shared/constant`**. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit ffc590b. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: tnsardesai <18272584+tnsardesai@users.noreply.github.com>
1 parent de979fc commit c3f2ce4

3 files changed

Lines changed: 205 additions & 0 deletions

File tree

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -983,16 +983,19 @@ Managed auth connections (`kernel auth connections`). The commands below are new
983983
- `--per-page <n>` - Items per page (default: 20)
984984
- `--output json`, `-o json` - Output raw JSON array
985985
- `kernel auth connections create` - New flags:
986+
- `--region us-east|eu-west|ap-southeast` - Region for this connection's login, reauth, and health-check browser sessions. Defaults to `us-east`.
986987
- `--proxy-id <id>` / `--proxy-name <name>` / `--proxy-mode direct|default` - Proxy configuration for this connection's login, reauth, and health-check browser sessions (mutually exclusive). Omit to derive the default from stealth.
987988
- `--stealth` - Whether those browser sessions run in stealth mode (default: true); use `--stealth=false` to disable
988989
- `--telemetry=all` / `--telemetry=off` / `--telemetry=<categories>` - Default telemetry for this connection's browser sessions. Same semantics as `kernel browsers create`
989990
- `--telemetry-export-otlp <id-or-name>` - Export this connection's captured telemetry over OTLP to one of the org's configured destinations. Implies `--telemetry=all` when `--telemetry` is not set. Use `=off` to disable export.
990991
- `kernel auth connections update <id>` - New flags:
992+
- `--region us-east|eu-west|ap-southeast` - Update the region for browser sessions created after this command. Active sessions don't move.
991993
- `--proxy-id <id>` / `--proxy-name <name>` / `--proxy-mode direct|default` - Proxy configuration for future browser sessions (mutually exclusive). Use `--proxy-mode=default` to drop a selected proxy rather than passing an empty value.
992994
- `--stealth` - Set whether future browser sessions run in stealth mode; use `--stealth=false` to disable
993995
- `--telemetry=all` / `--telemetry=off` / `--telemetry=<categories>` - Update telemetry for future browser sessions
994996
- `--telemetry-export-otlp <id-or-name>` - Update where future sessions export captured telemetry. Naming a destination requires passing `--telemetry` in the same command, since the API validates capture and export together and enabling capture here would replace the connection's current category selection. Use `=off` to disable export.
995997
- `kernel auth connections login <id>` - New flags:
998+
- `--region us-east|eu-west|ap-southeast` - Region override for this login only. Omit it to inherit the connection region.
996999
- `--proxy-id <id>` / `--proxy-name <name>` / `--proxy-mode direct|default` - Proxy override for this login's browser session (mutually exclusive); omitted properties inherit the connection defaults
9971000
- `--stealth` - Stealth override for this login's browser session; use `--stealth=false` to disable
9981001
- `--telemetry=all` / `--telemetry=off` / `--telemetry=<categories>` - Telemetry override for this login only, merged onto the connection's config

cmd/auth_connections.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ type AuthConnectionCreateInput struct {
4949
ProxyID string
5050
ProxyName string
5151
ProxyMode string
52+
Region string
5253
Stealth BoolFlag
5354
SaveCredentials bool
5455
NoSaveCredentials bool
@@ -84,6 +85,7 @@ type AuthConnectionUpdateInput struct {
8485
ProxyName string
8586
ProxyNameSet bool
8687
ProxyMode string
88+
Region string
8789
Stealth BoolFlag
8890
SaveCredentials BoolFlag
8991
HealthCheckInterval int
@@ -115,6 +117,7 @@ type AuthConnectionLoginInput struct {
115117
ProxyID string
116118
ProxyName string
117119
ProxyMode string
120+
Region string
118121
Stealth BoolFlag
119122
RecordSession BoolFlag
120123
Telemetry string
@@ -213,6 +216,14 @@ func (c AuthConnectionCmd) Create(ctx context.Context, in AuthConnectionCreateIn
213216
params.ManagedAuthCreateRequest.Browser.Proxy = proxy
214217
}
215218

219+
if in.Region != "" {
220+
region, err := parseRegionFlag(in.Region)
221+
if err != nil {
222+
return err
223+
}
224+
params.ManagedAuthCreateRequest.Browser.Region = kernel.ManagedAuthBrowserConfigRegion(region)
225+
}
226+
216227
if in.Stealth.Set {
217228
params.ManagedAuthCreateRequest.Browser.Stealth = kernel.Opt(in.Stealth.Value)
218229
}
@@ -285,6 +296,9 @@ func printManagedAuthSummary(auth *kernel.ManagedAuth) {
285296
// its login, reauthentication, and health-check sessions.
286297
func managedAuthBrowserRows(cfg kernel.ManagedAuthBrowserConfig) pterm.TableData {
287298
rows := pterm.TableData{}
299+
if cfg.Region != "" {
300+
rows = append(rows, []string{"Browser Region", string(cfg.Region)})
301+
}
288302
if proxy := formatBrowserProxyConfig(cfg.Proxy); proxy != "" {
289303
rows = append(rows, []string{"Browser Proxy", proxy})
290304
}
@@ -374,6 +388,15 @@ func (c AuthConnectionCmd) Update(ctx context.Context, in AuthConnectionUpdateIn
374388
hasChanges = true
375389
}
376390

391+
if in.Region != "" {
392+
region, err := parseRegionFlag(in.Region)
393+
if err != nil {
394+
return err
395+
}
396+
params.ManagedAuthUpdateRequest.Browser.Region = kernel.ManagedAuthBrowserConfigRegion(region)
397+
hasChanges = true
398+
}
399+
377400
if in.Stealth.Set {
378401
params.ManagedAuthUpdateRequest.Browser.Stealth = kernel.Opt(in.Stealth.Value)
379402
hasChanges = true
@@ -764,6 +787,14 @@ func (c AuthConnectionCmd) Login(ctx context.Context, in AuthConnectionLoginInpu
764787
params.Browser.Proxy = proxy
765788
}
766789

790+
if in.Region != "" {
791+
region, err := parseRegionFlag(in.Region)
792+
if err != nil {
793+
return err
794+
}
795+
params.Browser.Region = kernel.ManagedAuthBrowserConfigRegion(region)
796+
}
797+
767798
if in.Stealth.Set {
768799
params.Browser.Stealth = kernel.Opt(in.Stealth.Value)
769800
}
@@ -1224,6 +1255,7 @@ func init() {
12241255
authConnectionsCreateCmd.Flags().String("proxy-id", "", "Proxy ID to use for this connection's browser sessions (mutually exclusive with --proxy-name and --proxy-mode)")
12251256
authConnectionsCreateCmd.Flags().String("proxy-name", "", "Proxy name to use for this connection's browser sessions (mutually exclusive with --proxy-id and --proxy-mode)")
12261257
authConnectionsCreateCmd.Flags().String("proxy-mode", "", "Proxy egress mode instead of a selected proxy: 'direct' for no proxy regardless of stealth, or 'default' for the stealth-derived default")
1258+
authConnectionsCreateCmd.Flags().String("region", "", "Geographic region for browser sessions: 'us-east', 'eu-west', or 'ap-southeast'. Defaults to us-east")
12271259
authConnectionsCreateCmd.Flags().Bool("stealth", true, "Run this connection's browser sessions in stealth mode; use --stealth=false to disable")
12281260
authConnectionsCreateCmd.Flags().Bool("no-save-credentials", false, "Disable saving credentials after successful login")
12291261
authConnectionsCreateCmd.Flags().Int("health-check-interval", 0, "Interval in seconds between health checks. Defaults to 3600 or your plan minimum, whichever is larger. The maximum is 86400; the minimum depends on your plan (Enterprise 300, Startup 1200, Hobbyist 3600, Free 21600)")
@@ -1250,6 +1282,7 @@ func init() {
12501282
authConnectionsUpdateCmd.Flags().String("proxy-id", "", "Proxy ID to use for future browser sessions (mutually exclusive with --proxy-name and --proxy-mode)")
12511283
authConnectionsUpdateCmd.Flags().String("proxy-name", "", "Proxy name to use for future browser sessions (mutually exclusive with --proxy-id and --proxy-mode)")
12521284
authConnectionsUpdateCmd.Flags().String("proxy-mode", "", "Proxy egress mode instead of a selected proxy: 'direct' for no proxy regardless of stealth, or 'default' to drop a selected proxy and use the stealth-derived default")
1285+
authConnectionsUpdateCmd.Flags().String("region", "", "Geographic region for future browser sessions: 'us-east', 'eu-west', or 'ap-southeast'")
12531286
authConnectionsUpdateCmd.Flags().Bool("stealth", true, "Set whether future browser sessions run in stealth mode; use --stealth=false to disable")
12541287
authConnectionsUpdateCmd.Flags().Bool("save-credentials", false, "Enable saving credentials after successful login")
12551288
authConnectionsUpdateCmd.Flags().Bool("no-save-credentials", false, "Disable saving credentials after successful login")
@@ -1282,6 +1315,7 @@ func init() {
12821315
authConnectionsLoginCmd.Flags().String("proxy-id", "", "Proxy ID to use for this login (mutually exclusive with --proxy-name and --proxy-mode)")
12831316
authConnectionsLoginCmd.Flags().String("proxy-name", "", "Proxy name to use for this login (mutually exclusive with --proxy-id and --proxy-mode)")
12841317
authConnectionsLoginCmd.Flags().String("proxy-mode", "", "Proxy egress mode for this login instead of a selected proxy: 'direct' for no proxy regardless of stealth, or 'default' for the stealth-derived default")
1318+
authConnectionsLoginCmd.Flags().String("region", "", "Geographic region override for this login: 'us-east', 'eu-west', or 'ap-southeast'")
12851319
authConnectionsLoginCmd.Flags().Bool("stealth", true, "Override stealth mode for this login's browser session; use --stealth=false to disable")
12861320
authConnectionsLoginCmd.Flags().Bool("record-session", false, "Override whether this login's browser session is recorded; use --record-session=false to disable")
12871321
authConnectionsLoginCmd.Flags().String("telemetry", "", "Telemetry override for this login only, merged onto the connection's config: --telemetry=all, --telemetry=off, or --telemetry=console,network")
@@ -1334,6 +1368,7 @@ func runAuthConnectionsCreate(cmd *cobra.Command, args []string) error {
13341368
proxyID, _ := cmd.Flags().GetString("proxy-id")
13351369
proxyName, _ := cmd.Flags().GetString("proxy-name")
13361370
proxyMode, _ := cmd.Flags().GetString("proxy-mode")
1371+
region, _ := cmd.Flags().GetString("region")
13371372
noSaveCredentials, _ := cmd.Flags().GetBool("no-save-credentials")
13381373
healthCheckInterval, _ := cmd.Flags().GetInt("health-check-interval")
13391374
noHealthChecks, _ := cmd.Flags().GetBool("no-health-checks")
@@ -1355,6 +1390,7 @@ func runAuthConnectionsCreate(cmd *cobra.Command, args []string) error {
13551390
ProxyID: proxyID,
13561391
ProxyName: proxyName,
13571392
ProxyMode: proxyMode,
1393+
Region: region,
13581394
Stealth: readBoolFlag(cmd.Flags(), "stealth"),
13591395
NoSaveCredentials: noSaveCredentials,
13601396
HealthCheckInterval: healthCheckInterval,
@@ -1391,6 +1427,7 @@ func runAuthConnectionsUpdate(cmd *cobra.Command, args []string) error {
13911427
proxyID, _ := cmd.Flags().GetString("proxy-id")
13921428
proxyName, _ := cmd.Flags().GetString("proxy-name")
13931429
proxyMode, _ := cmd.Flags().GetString("proxy-mode")
1430+
region, _ := cmd.Flags().GetString("region")
13941431
saveCredentials, _ := cmd.Flags().GetBool("save-credentials")
13951432
noSaveCredentials, _ := cmd.Flags().GetBool("no-save-credentials")
13961433
healthCheckInterval, _ := cmd.Flags().GetInt("health-check-interval")
@@ -1440,6 +1477,7 @@ func runAuthConnectionsUpdate(cmd *cobra.Command, args []string) error {
14401477
ProxyName: proxyName,
14411478
ProxyNameSet: cmd.Flags().Changed("proxy-name"),
14421479
ProxyMode: proxyMode,
1480+
Region: region,
14431481
Stealth: readBoolFlag(cmd.Flags(), "stealth"),
14441482
SaveCredentials: saveCredentialsFlag,
14451483
HealthCheckInterval: healthCheckInterval,
@@ -1492,6 +1530,7 @@ func runAuthConnectionsLogin(cmd *cobra.Command, args []string) error {
14921530
proxyID, _ := cmd.Flags().GetString("proxy-id")
14931531
proxyName, _ := cmd.Flags().GetString("proxy-name")
14941532
proxyMode, _ := cmd.Flags().GetString("proxy-mode")
1533+
region, _ := cmd.Flags().GetString("region")
14951534
telemetry, _ := cmd.Flags().GetString("telemetry")
14961535
telemetryExport, _ := cmd.Flags().GetString("telemetry-export-otlp")
14971536

@@ -1502,6 +1541,7 @@ func runAuthConnectionsLogin(cmd *cobra.Command, args []string) error {
15021541
ProxyID: proxyID,
15031542
ProxyName: proxyName,
15041543
ProxyMode: proxyMode,
1544+
Region: region,
15051545
Stealth: readBoolFlag(cmd.Flags(), "stealth"),
15061546
RecordSession: readBoolFlag(cmd.Flags(), "record-session"),
15071547
Telemetry: telemetry,

cmd/auth_connections_test.go

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,18 @@ import (
66
"encoding/json"
77
"errors"
88
"io"
9+
"net/http"
10+
"net/http/httptest"
911
"os"
1012
"testing"
1113

14+
"github.com/kernel/cli/pkg/util"
1215
"github.com/kernel/kernel-go-sdk"
1316
"github.com/kernel/kernel-go-sdk/option"
1417
"github.com/kernel/kernel-go-sdk/packages/pagination"
1518
"github.com/kernel/kernel-go-sdk/packages/ssestream"
19+
"github.com/spf13/cobra"
20+
"github.com/spf13/pflag"
1621
"github.com/stretchr/testify/assert"
1722
"github.com/stretchr/testify/require"
1823
)
@@ -333,6 +338,163 @@ func TestAuthConnectionsCreate_ProviderWithPath_DoesNotSetAuto(t *testing.T) {
333338
assert.False(t, cred.Auto.Valid(), "auto should remain unset when --credential-path is explicit")
334339
}
335340

341+
func TestAuthConnectionsRegion_CreateUpdateAndLogin(t *testing.T) {
342+
var createParams kernel.AuthConnectionNewParams
343+
var updateParams kernel.AuthConnectionUpdateParams
344+
var loginParams kernel.AuthConnectionLoginParams
345+
fake := &FakeAuthConnectionService{
346+
NewFunc: func(ctx context.Context, body kernel.AuthConnectionNewParams, opts ...option.RequestOption) (*kernel.ManagedAuth, error) {
347+
createParams = body
348+
return &kernel.ManagedAuth{ID: "conn-new"}, nil
349+
},
350+
UpdateFunc: func(ctx context.Context, id string, body kernel.AuthConnectionUpdateParams, opts ...option.RequestOption) (*kernel.ManagedAuth, error) {
351+
updateParams = body
352+
return &kernel.ManagedAuth{ID: id}, nil
353+
},
354+
LoginFunc: func(ctx context.Context, id string, body kernel.AuthConnectionLoginParams, opts ...option.RequestOption) (*kernel.LoginResponse, error) {
355+
loginParams = body
356+
return &kernel.LoginResponse{}, nil
357+
},
358+
}
359+
c := AuthConnectionCmd{svc: fake}
360+
361+
require.NoError(t, c.Create(context.Background(), AuthConnectionCreateInput{
362+
Domain: "example.com", ProfileName: "work", Region: "eu-west", Output: "json",
363+
}))
364+
assert.Equal(t, kernel.ManagedAuthBrowserConfigRegionEuWest, createParams.ManagedAuthCreateRequest.Browser.Region)
365+
366+
require.NoError(t, c.Update(context.Background(), AuthConnectionUpdateInput{
367+
ID: "conn-new", Region: "ap-southeast", Output: "json",
368+
}))
369+
assert.Equal(t, kernel.ManagedAuthBrowserConfigRegionApSoutheast, updateParams.ManagedAuthUpdateRequest.Browser.Region)
370+
371+
require.NoError(t, c.Login(context.Background(), AuthConnectionLoginInput{
372+
ID: "conn-new", Region: "us-east", Output: "json",
373+
}))
374+
assert.Equal(t, kernel.ManagedAuthBrowserConfigRegionUsEast, loginParams.Browser.Region)
375+
}
376+
377+
func runAuthConnectionCommand(t *testing.T, command *cobra.Command, method, path string, args ...string) map[string]any {
378+
t.Helper()
379+
380+
var body map[string]any
381+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
382+
assert.Equal(t, method, r.Method)
383+
assert.Equal(t, path, r.URL.Path)
384+
require.NoError(t, json.NewDecoder(r.Body).Decode(&body))
385+
w.Header().Set("Content-Type", "application/json")
386+
_, _ = io.WriteString(w, `{"id":"conn-1","domain":"example.com","profile_name":"work","status":"NEEDS_AUTH","save_credentials":true,"record_session":false,"flow_type":"LOGIN","hosted_url":"https://auth.example.com","flow_expires_at":"2030-01-01T00:00:00Z"}`)
387+
}))
388+
t.Cleanup(server.Close)
389+
390+
resetFlags := func() {
391+
command.Flags().VisitAll(func(flag *pflag.Flag) {
392+
if flag.Changed {
393+
require.NoError(t, flag.Value.Set(flag.DefValue))
394+
flag.Changed = false
395+
}
396+
})
397+
}
398+
resetFlags()
399+
t.Cleanup(resetFlags)
400+
client := kernel.NewClient(option.WithBaseURL(server.URL), option.WithAPIKey("test"))
401+
originalContext := command.Context()
402+
command.SetContext(context.WithValue(context.Background(), util.KernelClientKey, client))
403+
t.Cleanup(func() { command.SetContext(originalContext) })
404+
require.NoError(t, command.Flags().Parse(args))
405+
positional := command.Flags().Args()
406+
require.NoError(t, command.Args(command, positional))
407+
408+
var runErr error
409+
_ = captureStdout(t, func() { runErr = command.RunE(command, positional) })
410+
require.NoError(t, runErr)
411+
return body
412+
}
413+
414+
func TestAuthConnectionsRegion_CommandFlags(t *testing.T) {
415+
tests := []struct {
416+
name string
417+
command *cobra.Command
418+
method string
419+
path string
420+
args []string
421+
wantRegion string
422+
}{
423+
{
424+
name: "create maps region",
425+
command: authConnectionsCreateCmd,
426+
method: http.MethodPost,
427+
path: "/auth/connections",
428+
args: []string{"--domain", "example.com", "--profile-name", "work", "--region", "eu-west", "--output", "json"},
429+
wantRegion: "eu-west",
430+
},
431+
{
432+
name: "create omits region",
433+
command: authConnectionsCreateCmd,
434+
method: http.MethodPost,
435+
path: "/auth/connections",
436+
args: []string{"--domain", "example.com", "--profile-name", "work", "--output", "json"},
437+
},
438+
{
439+
name: "update maps region",
440+
command: authConnectionsUpdateCmd,
441+
method: http.MethodPatch,
442+
path: "/auth/connections/conn-1",
443+
args: []string{"conn-1", "--region", "ap-southeast", "--output", "json"},
444+
wantRegion: "ap-southeast",
445+
},
446+
{
447+
name: "update omits region",
448+
command: authConnectionsUpdateCmd,
449+
method: http.MethodPatch,
450+
path: "/auth/connections/conn-1",
451+
args: []string{"conn-1", "--login-url", "https://example.com/login", "--output", "json"},
452+
},
453+
{
454+
name: "login maps region",
455+
command: authConnectionsLoginCmd,
456+
method: http.MethodPost,
457+
path: "/auth/connections/conn-1/login",
458+
args: []string{"conn-1", "--region", "us-east", "--output", "json"},
459+
wantRegion: "us-east",
460+
},
461+
{
462+
name: "login omits region",
463+
command: authConnectionsLoginCmd,
464+
method: http.MethodPost,
465+
path: "/auth/connections/conn-1/login",
466+
args: []string{"conn-1", "--output", "json"},
467+
},
468+
}
469+
470+
for _, tt := range tests {
471+
t.Run(tt.name, func(t *testing.T) {
472+
body := runAuthConnectionCommand(t, tt.command, tt.method, tt.path, tt.args...)
473+
browser, _ := body["browser"].(map[string]any)
474+
if tt.wantRegion == "" {
475+
assert.NotContains(t, browser, "region")
476+
return
477+
}
478+
assert.Equal(t, tt.wantRegion, browser["region"])
479+
})
480+
}
481+
}
482+
483+
func TestAuthConnectionsRegion_RejectsUnknownValue(t *testing.T) {
484+
c := AuthConnectionCmd{svc: &FakeAuthConnectionService{}}
485+
err := c.Create(context.Background(), AuthConnectionCreateInput{
486+
Domain: "example.com", ProfileName: "work", Region: "emea", Output: "json",
487+
})
488+
assert.ErrorContains(t, err, "invalid --region value")
489+
}
490+
491+
func TestManagedAuthBrowserRows_IncludesRegion(t *testing.T) {
492+
rows := managedAuthBrowserRows(kernel.ManagedAuthBrowserConfig{
493+
Region: kernel.ManagedAuthBrowserConfigRegionEuWest,
494+
})
495+
assert.Contains(t, rows, []string{"Browser Region", "eu-west"})
496+
}
497+
336498
// --credential-auto should still be honored (it was a no-op redundant flag
337499
// before the default changed, but callers may pass it for clarity).
338500
func TestAuthConnectionsCreate_ProviderWithExplicitAuto_SetsAuto(t *testing.T) {

0 commit comments

Comments
 (0)