Skip to content

Commit e811877

Browse files
authored
feat(auth blocks): let authors pick the tab the block opens on (#197)
AwsAuth, GoogleAuth and GitAuth each hard-coded their starting tab, so a runbook whose users always authenticate one way still landed them on the wrong form and made them click across. A new `defaultTab` prop chooses the starting tab instead; it seeds the initial state only, so the user's own tab clicks still own it from then on. MDX props are untyped, so each block validates the value and falls back rather than rendering an empty pane: - AwsAuth: 'credentials' | 'sso' | 'profile', default 'credentials' - GoogleAuth: 'service_account' | 'oauth' | 'gcloud', default 'service_account' - GitAuth: 'oauth' | 'pat', validated against the selected provider — GitLab has no OAuth flow, so 'oauth' falls back to 'pat' there. The prop is re-applied on a provider switch, which previously always reset to the provider's own default. Docs and the aws-auth/google-auth feature demos cover the new prop.
1 parent 351470d commit e811877

22 files changed

Lines changed: 311 additions & 12 deletions

File tree

docs/src/content/docs/authoring/blocks/AwsAuth.mdx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,18 @@ The AwsAuth block provides three ways to authenticate:
3131
| **AWS SSO** | Use AWS IAM Identity Center (formerly AWS SSO) |
3232
| **Local Profile** | Use a profile from `~/.aws/credentials` |
3333

34+
Static Credentials is the tab users land on. Set `defaultTab` to open on a different one:
35+
36+
```mdx
37+
<AwsAuth
38+
id="aws-auth"
39+
defaultTab="sso"
40+
ssoStartUrl="https://my-company.awsapps.com/start"
41+
/>
42+
```
43+
44+
The user can still switch tabs — `defaultTab` only chooses the starting one.
45+
3446
## Props
3547

3648
| Prop | Type | Default | Description |
@@ -39,6 +51,7 @@ The AwsAuth block provides three ways to authenticate:
3951
| `title` | string | "AWS Authentication" | Display title shown in the UI |
4052
| `description` | string | - | Description of the authentication purpose |
4153
| `defaultRegion` | string | "us-east-1" | Default AWS region for CLI commands. Sets `AWS_REGION` environment variable |
54+
| `defaultTab` | `'credentials'`, `'sso'`, or `'profile'` | `'credentials'` | Which authentication tab the block opens on. An unrecognized value falls back to `'credentials'` |
4255
| `detectCredentials` | `false` or `CredentialSource[]` | `['env']` | Whether and how to detect existing credentials. See [Credential Detection](#credential-detection) |
4356
| `ssoStartUrl` | string | - | AWS SSO start URL (e.g., `https://my-company.awsapps.com/start`). Required for SSO |
4457
| `ssoRegion` | string | "us-east-1" | AWS region where IAM Identity Center is configured |

docs/src/content/docs/authoring/blocks/GitAuth.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ Only `gitlab.com` and `github.com` are supported today. Self-managed GitLab and
6666
| `oauthClientId` | `string` | No | Gruntwork's app | Custom GitHub OAuth App client ID. GitHub only. |
6767
| `oauthScopes` | `string[]` | No | `['repo']` (GitHub) | OAuth scopes to request. |
6868
| `detectCredentials` | `false \| GitCredentialSource[]` | No | `['env', 'cli']` | Credential auto-detection sources, applied to the selected provider. Set to `false` to disable. |
69+
| `defaultTab` | `'oauth' \| 'pat'` | No | `'oauth'` (GitHub), `'pat'` (GitLab) | Which authentication tab the block opens on. GitLab has no OAuth flow, so `'oauth'` falls back to `'pat'` there. Re-applied when the user switches providers. |
6970
| `inputsId` | `string \| string[]` | No || Reference one or more `<Inputs>` blocks for template expressions in props. |
7071

7172
## Usage

docs/src/content/docs/authoring/blocks/GitHubAuth.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ The GitHubAuth block supports GitHub.com, GitHub Enterprise Cloud and GitHub Ent
6262
| `detectCredentials` | `false` or `CredentialSource[]` | `['env', 'cli']` | Whether and how the block should automatically detect credentials in the user's environment |
6363
| `oauthClientId` | string | Gruntwork default | Custom OAuth App client ID |
6464
| `oauthScopes` | string[] | `["repo"]` | OAuth scopes to request |
65+
| `defaultTab` | `'oauth'` or `'pat'` | `'oauth'` | Which authentication tab the block opens on |
6566

6667
## Usage
6768

docs/src/content/docs/authoring/blocks/GoogleAuth.mdx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,14 @@ The GoogleAuth block provides three ways to authenticate:
3333

3434
All three tabs also offer an optional **Default Region** picker, which seeds the region environment variables for subsequent commands.
3535

36+
Service Account Key is the tab users land on. Set `defaultTab` to open on a different one:
37+
38+
```mdx
39+
<GoogleAuth id="gcp-auth" defaultTab="gcloud" />
40+
```
41+
42+
The user can still switch tabs — `defaultTab` only chooses the starting one.
43+
3644
<Aside type="note">
3745
Runbooks never shells out to the `gcloud` binary. Service account keys are validated by minting a real access token, sign-in uses a loopback OAuth redirect, and the gcloud tab reads gcloud's own configuration files directly. You do not need the Google Cloud CLI installed for any of the three tabs — though the gcloud tab is only useful if you have used it before to create credentials.
3846
</Aside>
@@ -48,6 +56,7 @@ Runbooks never shells out to the `gcloud` binary. Service account keys are valid
4856
| `defaultRegion` | `string` || Default compute region for subsequent commands. Sets `GOOGLE_CLOUD_REGION`, `CLOUDSDK_COMPUTE_REGION`, and `GOOGLE_REGION` |
4957
| `defaultZone` | `string` || Default compute zone for subsequent commands. Sets `CLOUDSDK_COMPUTE_ZONE` and `GOOGLE_ZONE` |
5058
| `gcloudConfiguration` | `string` || Pre-select a named gcloud configuration in the gcloud Config tab (supports template expressions) |
59+
| `defaultTab` | `'service_account'`, `'oauth'`, or `'gcloud'` | `'service_account'` | Which authentication tab the block opens on. An unrecognized value falls back to `'service_account'` |
5160
| `scopes` | `string[]` | `cloud-platform`, `userinfo.email`, `openid` | OAuth scopes requested by Google Sign-In. When set, also **required** of any auto-detected or gcloud user ADC this block will accept (service-account keys are exempt) |
5261
| `oauthClientId` | `string` || Client ID of a Google Cloud "Desktop app" OAuth client. Must be paired with `oauthClientSecret`. Mutually exclusive with `oauthClientFile`. See [Using your own OAuth client](#using-your-own-oauth-client) |
5362
| `oauthClientSecret` | `string` || Client secret issued alongside the Desktop OAuth client. Required whenever `oauthClientId` is set. Per RFC 8252 this value is not confidential; Google simply issues one with every Desktop client |

testdata/feature-demos/aws-auth/runbook.mdx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,25 @@ The simplest form of AwsAuth. With no `detectCredentials` prop, it defaults to `
4040
usePty={false}
4141
/>
4242

43+
### 1a. Choosing the Default Tab
44+
45+
The block opens on the Static Credentials tab. `defaultTab` picks a different starting tab -- `"credentials"`, `"sso"`, or `"profile"`. The user can still switch tabs; this only chooses which one is selected first.
46+
47+
```mdx
48+
<AwsAuth id="aws-auth-sso-first" defaultTab="sso" ssoStartUrl="..." />
49+
```
50+
51+
<AwsAuth
52+
id="aws-auth-sso-first"
53+
title="SSO First"
54+
description="Opens on the AWS SSO tab instead of Static Credentials."
55+
defaultTab="sso"
56+
defaultRegion="us-west-2"
57+
ssoRegion="us-west-2"
58+
ssoStartUrl="https://d-9267d384ee.awsapps.com/start"
59+
detectCredentials={false}
60+
/>
61+
4362
---
4463

4564
## 2. Credential Detection Options

testdata/feature-demos/google-auth/runbook.mdx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,22 @@ The simplest form of GoogleAuth. With no `detectCredentials` prop, it defaults t
5252
failMessage="No usable Google Cloud credentials in the session environment."
5353
/>
5454

55+
### 1a. Choosing the Default Tab
56+
57+
The block opens on the Service Account Key tab. `defaultTab` picks a different starting tab -- `"service_account"`, `"oauth"`, or `"gcloud"`. The user can still switch tabs; this only chooses which one is selected first.
58+
59+
```mdx
60+
<GoogleAuth id="google-auth-gcloud-first" defaultTab="gcloud" />
61+
```
62+
63+
<GoogleAuth
64+
id="google-auth-gcloud-first"
65+
title="gcloud Config First"
66+
description="Opens on the gcloud Config tab instead of Service Account Key."
67+
defaultTab="gcloud"
68+
detectCredentials={false}
69+
/>
70+
5571
---
5672

5773
## 2. Credential Detection Options

web/src/components/mdx/AwsAuth/AwsAuth.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ function AwsAuthInteractive({
3636
ssoRoleName,
3737
defaultRegion = "us-east-1",
3838
detectCredentials = ['env'], // Default: auto-detect from env vars
39+
defaultTab,
3940
inputsId,
4041
}: AwsAuthProps) {
4142
const validationError = useMemo((): AppError | null => {
@@ -78,6 +79,7 @@ function AwsAuthInteractive({
7879
ssoRoleName,
7980
defaultRegion,
8081
detectCredentials,
82+
defaultTab,
8183
})
8284

8385
// Track block render on mount
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { describe, it, expect, vi, afterEach } from 'vitest'
2+
import { renderHook } from '@testing-library/react'
3+
import { useAwsAuth } from '../useAwsAuth'
4+
5+
/**
6+
* Which tab the block opens on is decided once, at mount, from the author's
7+
* `defaultTab` prop. Detection is switched off in these tests so no IPC runs —
8+
* the starting tab is the only behaviour under test.
9+
*/
10+
11+
vi.mock('@/contexts/useRunbook', () => ({
12+
useRunbookContext: () => ({ registerOutputs: vi.fn(), blockOutputs: {} }),
13+
}))
14+
vi.mock('@/contexts/useSession', () => ({
15+
useSession: () => ({ isReady: true }),
16+
}))
17+
18+
const originalApi = window.api
19+
20+
afterEach(() => {
21+
window.api = originalApi
22+
})
23+
24+
const renderAwsAuth = (defaultTab?: string) =>
25+
renderHook(() =>
26+
useAwsAuth({
27+
id: 'aws',
28+
ssoRegion: 'us-east-1',
29+
defaultRegion: 'us-east-1',
30+
detectCredentials: false,
31+
defaultTab,
32+
}),
33+
)
34+
35+
describe('useAwsAuth — defaultTab', () => {
36+
it('opens on Static Credentials when no defaultTab is set', () => {
37+
expect(renderAwsAuth().result.current.authMethod).toBe('credentials')
38+
})
39+
40+
it('opens on the tab the author asked for', () => {
41+
expect(renderAwsAuth('sso').result.current.authMethod).toBe('sso')
42+
expect(renderAwsAuth('profile').result.current.authMethod).toBe('profile')
43+
})
44+
45+
it('falls back to Static Credentials for an unrecognized tab name', () => {
46+
// MDX props are untyped, so a typo must not leave the block formless.
47+
expect(renderAwsAuth('sso-tab').result.current.authMethod).toBe('credentials')
48+
})
49+
})

web/src/components/mdx/AwsAuth/hooks/useAwsAuth.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import type {
1414
AwsCredentialSource,
1515
DetectedAwsCredentials,
1616
} from "../types"
17+
import { resolveDefaultAuthMethod } from "../utils"
1718

1819
interface UseAwsAuthOptions {
1920
id: string
@@ -23,6 +24,8 @@ interface UseAwsAuthOptions {
2324
ssoRoleName?: string
2425
defaultRegion: string
2526
detectCredentials?: false | AwsCredentialSource[]
27+
/** Tab to open on; validated by resolveDefaultAuthMethod. */
28+
defaultTab?: string
2629
}
2730

2831
export function useAwsAuth({
@@ -33,12 +36,16 @@ export function useAwsAuth({
3336
ssoRoleName,
3437
defaultRegion,
3538
detectCredentials = ['env'], // Default: auto-detect from env vars
39+
defaultTab,
3640
}: UseAwsAuthOptions) {
3741
const { registerOutputs, blockOutputs } = useRunbookContext()
3842
const { isReady: sessionReady } = useSession()
3943

4044
// Core auth state
41-
const [authMethod, setAuthMethod] = useState<AuthMethod>('credentials')
45+
// The starting tab is the author's `defaultTab` (validated), not a constant.
46+
// Only the initial value is taken from the prop — the user's tab clicks own
47+
// it from then on.
48+
const [authMethod, setAuthMethod] = useState<AuthMethod>(() => resolveDefaultAuthMethod(defaultTab))
4249
const [authStatus, setAuthStatus] = useState<AuthStatus>('pending')
4350
const [errorMessage, setErrorMessage] = useState<string | null>(null)
4451
const [warningMessage, setWarningMessage] = useState<string | null>(null)

web/src/components/mdx/AwsAuth/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,12 @@ export interface AwsAuthProps {
7474
ssoRoleName?: string
7575
/** Default AWS region for CLI commands that don't specify a region */
7676
defaultRegion?: string
77+
/**
78+
* Which authentication tab the block opens on: 'credentials' (Static
79+
* Credentials), 'sso' (AWS SSO), or 'profile' (Local Profile).
80+
* Default: 'credentials'. An unrecognized value falls back to the default.
81+
*/
82+
defaultTab?: AuthMethod
7783
/**
7884
* Credential detection configuration.
7985
* - `false`: Disable auto-detection, show manual auth only

0 commit comments

Comments
 (0)