Skip to content

feat(plugins): config profiles, credential_process, SSO, AssumeRole, ElastiCache, Keyspaces (#1567)#1572

Merged
datlechin merged 9 commits into
mainfrom
feat/1567-aws-unified-auth
Jun 3, 2026
Merged

feat(plugins): config profiles, credential_process, SSO, AssumeRole, ElastiCache, Keyspaces (#1567)#1572
datlechin merged 9 commits into
mainfrom
feat/1567-aws-unified-auth

Conversation

@datlechin
Copy link
Copy Markdown
Member

Fixes #1567. AWS-backed connections could not use profiles defined in ~/.aws/config (the screenshot in the issue: a DynamoDB "AWS Profile" connection failing with "Profile not found or incomplete in ~/.aws/credentials"). The root cause was that each AWS plugin reimplemented credential resolution, and the copies only read ~/.aws/credentials. This unifies AWS credential resolution into one shared layer in TableProPluginKit and uses it everywhere, then extends it to full AWS-CLI parity across all AWS-backed connections.

What changed, by stage

  1. Foundation. Moved AWSCredentials, AWSCredentialResolver, AWSSSO, AWSAuthError, plus new AWSConfigFile/AWSSigV4/AWSSTS into TableProPluginKit/AWS/ as public API so the app and every plugin share one implementation. Additive and ABI-safe (no currentPluginKitVersion bump). credential_process (which uses Process) is gated to macOS so the iOS PluginKit target still builds. Credentials now carry an expiration for refresh.
  2. DynamoDB (the reported bug). Deleted the in-plugin resolver and uses the shared one: reads ~/.aws/config + ~/.aws/credentials, supports credential_process and SSO, re-resolves on expiry, and shows accurate errors.
  3. Profile picker. The connection form shows an editable combo box of profiles discovered from ~/.aws/config + ~/.aws/credentials (normalizing [profile X]/[X]), with a free-text fallback. Implemented as an additive ConnectionField.dynamicOptions (no @frozen change).
  4. AssumeRole + in-app SSO. role_arn + source_profile chaining via STS AssumeRole (with external_id, duration_seconds, credential_source = Environment). When an SSO session has expired, a prompt runs the OIDC device-login flow in the browser and refreshes the cached token.
  5. ElastiCache Redis IAM. New AWS IAM auth mode on Redis; TablePro generates the ElastiCache IAM token and uses it as the password.
  6. Keyspaces / Cassandra SigV4. SigV4 challenge-response authenticator wired into the cpp-driver, matching AWS's documented Keyspaces algorithm.

Tests

Unit tests for every deterministic/protocol piece: dual-file profile resolution (incl. the config-only case that reproduces the bug), SSO parsing/token-cache/role-fetch, STS AssumeRole response parsing, SSO device-login parsing/polling/cache, and the RDS/ElastiCache/Keyspaces token generators. All AWS suites pass; SwiftLint --strict clean on changed lines.

Needs live-AWS verification (please test before release)

The deterministic logic is unit-tested against AWS's documented algorithms, but these live paths can't be verified in CI and need real AWS:

  • ElastiCache — a live IAM-enabled cache over TLS; the replication-group-id input may need tuning per cluster type.
  • Keyspaces — the SigV4 handshake against a live Keyspaces endpoint (signer matches the reference algorithm; cpp-driver callbacks compile).
  • In-app SSO device login + MFA — a real SSO portal and browser hand-off. MFA-protected assume-role profiles intentionally return a clear "not supported yet" message.

Release

DynamoDB and Cassandra are registry-only plugins and need a registry re-release after merge; Redis is bundled. The PluginKit changes are additive (no version bump).

@datlechin datlechin changed the title feat(plugins): unified AWS auth — config profiles, credential_process, SSO, AssumeRole, ElastiCache, Keyspaces (#1567) feat(plugins): config profiles, credential_process, SSO, AssumeRole, ElastiCache, Keyspaces (#1567) Jun 3, 2026
@mintlify
Copy link
Copy Markdown

mintlify Bot commented Jun 3, 2026

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
TablePro 🟢 Ready View Preview Jun 3, 2026, 2:41 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

let output = outputPipe.fileHandleForReading.readDataToEndOfFile()
let errorOutput = errorPipe.fileHandleForReading.readDataToEndOfFile()

P2 Badge Drain credential_process stderr without blocking

For a credential_process that fails while writing a large amount to stderr, this reads stdout to EOF before draining stderr, so the child can block on a full stderr pipe and never close stdout, hanging the connection attempt indefinitely. Drain both pipes concurrently (or merge/discard stderr while the process runs) before waiting for termination.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

} catch let error as AWSAuthError {
throw DynamoDBError.authFailed(error.localizedDescription)
} catch let error as AWSSSOError {
throw DynamoDBError.authFailed(error.localizedDescription)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve SSO errors for DynamoDB login recovery

When a DynamoDB SSO profile has a missing or expired token cache, AWSCredentialResolver throws AWSSSOError, but this catch converts it into DynamoDBError.authFailed. The test-connection flow only offers the browser sign-in path when AWSSSOLoginService.isSSOExpired(error) receives an AWSSSOError directly, so DynamoDB SSO users hit the generic connection failure instead of the new login prompt. Preserve the underlying SSO error (or teach the detector about this wrapper) for the awsAuthMethod == "sso" path.

Useful? React with 👍 / 👎.

@datlechin
Copy link
Copy Markdown
Member Author

Addressed the review and the failing PluginKit ABI Gate:

  • ABI (was the real failure): the gate flagged that ConnectionField.init had gained a dynamicOptions parameter — a breaking change for already-compiled registry plugins that call that initializer. Reworked it: the original init is untouched, dynamicOptions is now an additive optional var set via a withDynamicOptions(_:) chaining method. Re-ran scripts/check-pluginkit-abi.sh against the PR base: the remaining interface diff is purely additive (no removed/changed symbols), so no currentPluginKitVersion bump is needed. The gate stays red because it flags any interface change for human review; this one is additive.
  • ElastiCache (High): removed the silent fallback to the endpoint host. The cache name (replication group ID) is now a required field, and connecting without it (or without TLS) fails with a clear, actionable error instead of signing a token with the wrong host.
  • TLS enforcement: ElastiCache and Keyspaces IAM now error early if SSL is disabled (both require in-transit encryption).
  • Field unification: the relational plugins' inline AWS field list is gone; MySQL/PostgreSQL/Redis/Cassandra all use the shared AWSAuthFields.standard(), removing the duplication.
  • AssumeRole: role_session_name is now sanitized to the STS-allowed character set.

Build green, all AWS/ConnectionField suites pass, SwiftLint --strict clean on changed lines.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7b07814978

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

return try await runCredentialProcess(command, profileName: profileName)
}

throw AWSAuthError.profileIncomplete(profileName)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Resolve SSO source profiles before marking profile incomplete

When an AssumeRole profile uses source_profile that points at an SSO profile, the recursive resolveProfileChain call loads settings containing sso_account_id/sso_role_name but no static keys or credential_process, so it falls through here with profileIncomplete instead of fetching the SSO role credentials to use as the STS base credentials. This breaks a supported shared-config shape such as [profile app-admin] role_arn=... source_profile=sso-base with [profile sso-base] sso_session=..., even though both SSO and AssumeRole are otherwise implemented in this change.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 528052a849

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

) -> String {
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(identifier: "UTC")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Use the Keyspaces SigV4 challenge timestamp format

When awsAuth is enabled for Amazon Keyspaces, this timestamp is placed in both the amzdate response field and the string-to-sign; the AWS Cassandra SigV4 plugins generate YYYY-MM-DDTHH:mm:ss.SSSZ, not the normal compact SigV4 YYYYMMDDTHHmmssZ format used here. Because the server verifies the challenge response against that Keyspaces-specific value, IAM authentication for Keyspaces will fail even with valid credentials.

Useful? React with 👍 / 👎.

section: .advanced
),
]
] + AWSAuthFields.standard() + [AWSAuthFields.elastiCacheReplicationGroupField()]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Implement Redis IAM tokens before exposing AWS auth

Selecting one of these new Redis AWS IAM options hides the password and shows the replication group field, but RedisPluginDriver.connect() still constructs RedisPluginConnection with only config.password and never calls AWSCredentialResolver or ElastiCacheAuthTokenGenerator. In that IAM-auth configuration the password is empty, so the connection skips AUTH entirely and ElastiCache IAM users cannot connect despite the UI offering the mode.

Useful? React with 👍 / 👎.

@datlechin datlechin merged commit e4d66a9 into main Jun 3, 2026
4 of 7 checks passed
@datlechin datlechin deleted the feat/1567-aws-unified-auth branch June 3, 2026 18:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feat request: support ~/.aws/config profiles and credential_process for AWS IAM auth in dynamo/redis etc

1 participant