-
Notifications
You must be signed in to change notification settings - Fork 25
Add stackit-cli compatible authentication to the SDK #3865
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Add stackit-cli compatible authentication to the SDK #3865
Conversation
Add new cliauth package that enables SDK applications to use STACKIT CLI provider credentials without direct CLI dependency. This implementation supports: - Reading credentials from system keyring or file fallback - Automatic OAuth2 token refresh with configurable endpoints - Multiple CLI profile support with profile resolution - Bidirectional credential sync (writeback after refresh) - Thread-safe RoundTripper implementation for HTTP clients - Background token refresh with context-based lifecycle management Storage locations: - System Keyring (macOS Keychain, Linux Secret Service, Windows Credential Manager) - File fallback: ~/.stackit/cli-api-auth-storage.txt
Add WithCLIProviderAuth and WithCLIBackgroundTokenRefresh configuration
options to the SDK config package. This enables easy integration of CLI
provider authentication into SDK clients.
Features:
- WithCLIProviderAuth: Configure SDK to use CLI credentials with profile support
- WithCLIBackgroundTokenRefresh: Enable background token refresh with context
- Profile resolution from parameter, env var, config file, or default
Usage example:
client, err := dns.NewAPIClient(
config.WithCLIProviderAuth(""),
)
Add unit tests for CLI provider authentication functionality: - credentials_test.go: Tests for credential reading, profile resolution, keyring/file fallback behavior - token_refresh_test.go: Tests for token expiration check and refresh logic - flow_test.go: Tests for RoundTripper implementation and automatic refresh - cli_auth_test.go: Tests for SDK config integration Tests cover: - Profile resolution order (explicit, env var, config file, default) - Storage location fallback (keyring -> file) - Token refresh with mock OAuth endpoints - Thread-safe concurrent access - Error handling for missing credentials and expired tokens
Fix test expectations to match the actual implementation: - Update keyring service name from 'stackit-cli-provider' to 'stackit-cli-api' - Update file paths from 'cli-provider-auth-storage.txt' to 'cli-api-auth-storage.txt' - Rewrite config tests to match the actual API (profile string instead of provider object) - Fix helper functions in test files All tests now pass successfully.
|
Please advise on versioning. Do you have conventions you use? |
This commit adds support for CLI-based authentication in the Terraform provider, enabling users to authenticate using credentials from the STACKIT CLI without managing separate service account credentials. Changes: - Add cli_auth boolean attribute to enable CLI authentication - Add cli_profile string attribute for profile selection - Implement authentication priority: explicit credentials > CLI > env vars - Integrate with SDK's WithCLIProviderAuth() configuration option The implementation follows the explicit opt-in pattern requested in RFC stackitcloud#880, requiring users to set cli_auth = true to enable the feature. Profile resolution follows the standard precedence: explicit config > STACKIT_CLI_PROFILE env var > ~/.config/stackit/cli-profile.txt > default. This change depends on SDK PR stackitcloud/stackit-sdk-go#3865 which adds the core CLI authentication functionality, and CLI PR stackitcloud/stackit-cli#1130 which implements the provider credential storage. Closes stackitcloud#719 Related to stackitcloud#880
This commit adds support for CLI-based authentication in the Terraform provider, enabling users to authenticate using credentials from the STACKIT CLI without managing separate service account credentials. Changes: - Add cli_auth boolean attribute to enable CLI authentication - Add cli_profile string attribute for profile selection - Implement authentication priority: explicit credentials > CLI > env vars - Integrate with SDK's WithCLIProviderAuth() configuration option The implementation follows the explicit opt-in pattern requested in RFC stackitcloud#880, requiring users to set cli_auth = true to enable the feature. Profile resolution follows the standard precedence: explicit config > STACKIT_CLI_PROFILE env var > ~/.config/stackit/cli-profile.txt > default. This change depends on SDK PR stackitcloud/stackit-sdk-go#3865 which adds the core CLI authentication functionality, and CLI PR stackitcloud/stackit-cli#1130 which implements the provider credential storage. Closes stackitcloud#719 Related to stackitcloud#880
Add replace directive to use SDK fork with CLI authentication support from PR stackitcloud/stackit-sdk-go#3865 until it's merged and released. This allows the provider to be built and tested with the CLI auth functionality before the SDK changes are officially released. The replace directive references commit 25b6b99bd648 from github.com/franklouwers/stackit-sdk-go/core which includes the core/cliauth package and config.WithCLIProviderAuth() function. Once SDK PR #3865 is merged and a new SDK version is released, this replace directive should be removed and the provider updated to require the new SDK version.
Add comprehensive example demonstrating how to use CLI provider authentication to access STACKIT CLI credentials from applications. The example covers: - Default profile authentication - Specific profile authentication - Direct credential access for advanced use cases Also updated the existing authentication example to reference the new CLI provider authentication option.
@franklouwers Thanks for your contribution across the CLI, SDK and Terraform provider! I created internal tickets to review them soon. Regarding versioning we do a minor bump, when something is added, like the CLI Authentication. Please adjust the version file to v0.21.0 Line 1 in 2b4b998
Create a new entry in core/CHANGELOG.md and in the global CHANGELOG.md, there you can define the changes you made. |
|
This PR was marked as stale after 7 days of inactivity and will be closed after another 7 days of further inactivity. If this PR should be kept open, just add a comment, remove the stale label or push new commits to it. |
|
Keep open |
|
This PR was marked as stale after 7 days of inactivity and will be closed after another 7 days of further inactivity. If this PR should be kept open, just add a comment, remove the stale label or push new commits to it. |
cgoetz-inovex
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your contribution! Especially splitting into several commits and thorough docs.
config/profile path constructions differs from CLI
| return len(s) >= len(substr) && (s == substr || len(s) > len(substr) && | ||
| (s[:len(substr)] == substr || s[len(s)-len(substr):] == substr || | ||
| containsMiddle(s, substr))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| return len(s) >= len(substr) && (s == substr || len(s) > len(substr) && | |
| (s[:len(substr)] == substr || s[len(s)-len(substr):] == substr || | |
| containsMiddle(s, substr))) | |
| return strings.Index(s, substr) != -1 |
| homeDir, err := os.UserHomeDir() | ||
| if err != nil { | ||
| return "", fmt.Errorf("get home dir: %w", err) | ||
| } | ||
|
|
||
| profilePath := filepath.Join(homeDir, ".config", "stackit", "cli-profile.txt") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| homeDir, err := os.UserHomeDir() | |
| if err != nil { | |
| return "", fmt.Errorf("get home dir: %w", err) | |
| } | |
| profilePath := filepath.Join(homeDir, ".config", "stackit", "cli-profile.txt") | |
| configDir, err := os.UserConfigDir() | |
| if err != nil { | |
| return "", fmt.Errorf("get config dir: %w", err) | |
| } | |
| profilePath := filepath.Join(configDir, "stackit", "cli-profile.txt") |
current impl fails on MacOS, probably windows
| homeDir, err := os.UserHomeDir() | ||
| if err != nil { | ||
| return "", fmt.Errorf("get home dir: %w", err) | ||
| } | ||
|
|
||
| if profile == defaultProfile { | ||
| return filepath.Join(homeDir, ".stackit", "cli-api-auth-storage.txt"), nil | ||
| } | ||
| return filepath.Join(homeDir, ".stackit", "profiles", profile, "cli-api-auth-storage.txt"), nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same as above, also the path doesn't look right, see https://github.com/stackitcloud/stackit-cli/blob/e537facbe88ad99e3cf30074757b797dbf6cb8df/internal/pkg/auth/storage.go#L189
| // | ||
| // If baseTransport is nil, http.DefaultTransport is used. | ||
| // If httpClient is nil, a default client is created for token refresh operations. | ||
| func NewCLIProviderFlow(profile string, baseTransport http.RoundTripper, httpClient *http.Client) (*CLIProviderFlow, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to discuss: I'd remove this method in favor of the WithContext variant. Currently used in tests and the examples. I think it would be nice if the examples would show usage without goroutine leaks. It's fine in the example per se because the process only runs for a short amount of time. But nonetheless I'd prefer a leak free example.
When refreshing I also suspect a nil pointer panic, when this variant is used.
| // Check if context was canceled | ||
| err := r.flow.refreshContext.Err() | ||
| if err != nil { | ||
| return fmt.Errorf("context canceled during wait: %w", err) | ||
| } | ||
|
|
||
| // Sleep briefly before checking again | ||
| time.Sleep(r.timeBetweenContextCheck) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| // Check if context was canceled | |
| err := r.flow.refreshContext.Err() | |
| if err != nil { | |
| return fmt.Errorf("context canceled during wait: %w", err) | |
| } | |
| // Sleep briefly before checking again | |
| time.Sleep(r.timeBetweenContextCheck) | |
| select { | |
| case <- r.flow.refreshContext.Done(): | |
| return r.flow.refreshContext.Err() | |
| case <- time.After(r.timeBetweenContextCheck): | |
| // do nothing | |
| } |
Not sure if this one matters, because the default timeBetweenContextCheck is just a second.
| } | ||
|
|
||
| // Wait for background refresh to trigger | ||
| time.Sleep(3 * time.Second) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
slow test, we could use https://pkg.go.dev/testing/synctest here to circumvent this sleep. But it was only added in go 1.24. We'd have to add this version to CI and guard the test with a build tag to only execute on 1.24.
Not sure if worth it.
CLI Provider Authentication Support for STACKIT SDK
Summary
This PR adds comprehensive support for using STACKIT CLI provider credentials directly in the SDK, enabling seamless authentication across CLI and SDK-based tools without requiring re-authentication. (requires stackitcloud/stackit-cli#1130)
Key Features
cliauthpackage enables SDK applications to read and use credentials stored by the STACKIT CLIArchitecture
The implementation consists of four logical commits:
Core Authentication Package (
core/cliauth):credentials.go: Credential reading from keyring/file with profile resolutiontoken_refresh.go: OAuth2 token refresh with configurable endpointsflow.go: Thread-safehttp.RoundTripperimplementation for automatic auth injectionbackground_refresh.go: Background token refresh with context-based lifecycledoc.go: Comprehensive package documentation with usage examplesSDK Configuration Integration (
core/config/cli_auth.go):WithCLIProviderAuth(profile): Configure SDK clients to use CLI credentialsWithCLIBackgroundTokenRefresh(ctx): Enable background token refreshUsage Example
Storage Locations
System Keyring (preferred):
stackit-cli-api(default) orstackit-cli-api/{profile}File Fallback:
~/.stackit/cli-api-auth-storage.txt~/.stackit/profiles/{profile}/cli-api-auth-storage.txtProfile Resolution Order
STACKIT_CLI_PROFILEenvironment variable~/.config/stackit/cli-profile.txtconfig file"default"fallbackBackward Compatibility
Related Issues
Checklist
make fmtexamples/directory)make test(will be checked by CI)make lint(will be checked by CI)