Skip to content

Feat/s3 session token#199

Merged
nbesimi merged 6 commits intodevfrom
feat/s3-session-token
Feb 12, 2026
Merged

Feat/s3 session token#199
nbesimi merged 6 commits intodevfrom
feat/s3-session-token

Conversation

@jasir99
Copy link
Contributor

@jasir99 jasir99 commented Feb 11, 2026

Summary by CodeRabbit

  • New Features
    • Added optional AWS Session Token support for S3 connections across the app.
    • New "Session Token (Optional)" input in Cloud Explorer and Data Lake connection forms.
    • Session tokens are stored, retrieved, and deleted securely alongside existing AWS credentials.
    • Bucket browsing and content views accept temporary AWS credentials when provided.
    • Session tokens are kept separate from persisted provider configuration.

@jasir99 jasir99 requested a review from Nuri1977 February 11, 2026 15:04
@coderabbitai
Copy link

coderabbitai bot commented Feb 11, 2026

📝 Walkthrough

Walkthrough

Adds optional AWS S3 session token support end-to-end: UI input, secure-storage set/get/delete, types, persistence, propagation to backend services, and inclusion of sessionToken when constructing S3 credentials and DuckLake secret payloads.

Changes

Cohort / File(s) Summary
Backend: cloud auth & S3 client
src/main/helpers/cloudAuth.helper.ts, src/main/services/cloudExplorer.service.ts
Conditionally include an escaped SESSION_TOKEN line in DuckLake secret SQL when present; pass a credentials object to S3Client that includes sessionToken when provided. Note: some secret fields now use raw values and session token is SQL-escaped.
Backend: DuckLake credential flow
src/main/services/duckLake.service.ts, src/main/services/duckLake/adapters/base.adapter.ts, src/main/services/duckLake/instanceStore.service.ts
Persist and retrieve optional AWS sessionToken in secure storage, include it in returned credentials and in DuckLake S3 secret payloads when present, and remove it during credential cleanup.
Renderer: Cloud Explorer & DataLake UIs
src/renderer/components/cloudExplorer/ConnectionForm.tsx, src/renderer/components/cloudExplorer/ExplorerBucketContent.tsx, src/renderer/components/cloudExplorer/ExplorerBuckets.tsx, src/renderer/components/dataLake/DataLakeConnectionSelector.tsx, src/renderer/components/dataLake/DataLakeConnectionWizard.tsx
Add sessionToken to form/model and UI (new input fields), wire storage set/get/delete calls for session tokens, ensure sessionToken is stored separately from providerConfig and propagated to runtime configs.
Renderer: Secure storage hook & types
src/renderer/hooks/useSecureStorage.ts, src/types/duckLake.ts, src/types/frontend.ts
Expose setCloudAwsSessionToken, getCloudAwsSessionToken, deleteCloudAwsSessionToken; add optional sessionToken?: string to frontend and DuckLake S3 types.
Frontend: other components
src/renderer/components/...
Various components updated to read/write sessionToken via secure storage and propagate it through UI flows (Explorer and DataLake components).

Sequence Diagram

sequenceDiagram
    participant User as User
    participant UI as ConnectionForm / Explorer UI
    participant Storage as SecureStorage
    participant Backend as Backend Service (DuckLake / cloudExplorer)
    participant AWS as AWS S3 Client

    User->>UI: Enter accessKeyId, secretAccessKey, optional sessionToken
    UI->>Storage: setCloudAwsSessionToken(token, connId)
    Storage-->>UI: ack
    UI->>Backend: submit connection (providerConfig without token)
    Backend->>Storage: getCloudAwsSessionToken(connId)
    Storage-->>Backend: sessionToken?
    Backend->>Backend: build credentials {accessKeyId, secretAccessKey, sessionToken?}
    Backend->>AWS: initialize S3Client(credentials)
    AWS-->>Backend: ready
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

enhancement

Suggested reviewers

  • nbesimi

Poem

🐰 I hopped through fields of keys and notes,
Hid tiny tokens in my secret coats,
From input nib to backend burrow,
Temporary carrots safe and thorough,
A hop, a hop — secure cloud oats 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title "Feat/s3 session token" clearly describes the main feature added: AWS S3 session token support across multiple components and services.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/s3-session-token

No actionable comments were generated in the recent review. 🎉

🧹 Recent nitpick comments
src/renderer/components/cloudExplorer/ConnectionForm.tsx (2)

548-557: Consider adding a show/hide toggle for consistency with the Secret Access Key field.

The session token field uses type="password" (good), but unlike the Secret Access Key field (line 527–546), it lacks a visibility toggle (InputProps with the eye icon). This is a minor UX inconsistency for users who may want to verify their pasted token.

Proposed fix
             <TextField
               label="Session Token (Optional)"
               placeholder="Temporary session token for temporary credentials"
-              type="password"
+              type={showPassword ? 'text' : 'password'}
               fullWidth
               margin="normal"
               value={formData.sessionToken}
               onChange={(e) => handleChange('sessionToken', e.target.value)}
               helperText="Optional: Required when using temporary AWS credentials (e.g., from STS AssumeRole)"
+              InputProps={{
+                endAdornment: (
+                  <InputAdornment position="end">
+                    <IconButton
+                      aria-label="toggle password visibility"
+                      onClick={() => setShowPassword(!showPassword)}
+                      edge="end"
+                    >
+                      {showPassword ? <VisibilityOff /> : <Visibility />}
+                    </IconButton>
+                  </InputAdornment>
+                ),
+              }}
             />

158-158: Redundant initialization from config — session token is always stripped before saving.

Line 158 reads sessionToken from config, but handleSubmit (lines 395–397) deletes it from the persisted config. On edit, this will always resolve to ''. The actual value is correctly loaded from secure storage in the second useEffect (lines 207–212), so this line is harmless but dead code.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/main/services/duckLake/adapters/base.adapter.ts (1)

349-363: ⚠️ Potential issue | 🟠 Major

SQL injection risk: sessionToken is interpolated directly into the query string.

This follows the same pattern as accessKeyId, secretAccessKey, etc., but any of these values containing a single quote (') would break the SQL. The sessionToken value originates from secure storage, which reduces risk, but consider escaping single quotes (as done for escapedToken in the GCS path at line 423) for defense-in-depth.

🛡️ Suggested defensive escaping
         if (sessionToken) {
+          const escapedSessionToken = sessionToken.replace(/'/g, "''");
           secretQuery += `,
-  SESSION_TOKEN '${sessionToken}'`;
+  SESSION_TOKEN '${escapedSessionToken}'`;
         }

Note: The same escaping concern applies to the existing accessKeyId, secretAccessKey, region, and endpoint values, but that's a pre-existing issue outside the scope of this PR.

src/main/services/cloudExplorer.service.ts (1)

29-53: ⚠️ Potential issue | 🔴 Critical

sessionToken is not passed during S3 connection validation in DuckLakeService.

In src/main/services/duckLake.service.ts at lines 731–735, the validateStorageConnection method calls CloudExplorerService.testConnection('aws', ...) without including sessionToken:

success = await CloudExplorerService.testConnection('aws', {
  region: fullConfig.s3.region,
  accessKeyId: fullConfig.s3.accessKeyId,
  secretAccessKey: fullConfig.s3.secretAccessKey,
});

This will fail for users who authenticate with temporary STS credentials (where the session token is mandatory). The sessionToken should be included:

🐛 Proposed fix in duckLake.service.ts (lines 731-735)
 success = await CloudExplorerService.testConnection('aws', {
   region: fullConfig.s3.region,
   accessKeyId: fullConfig.s3.accessKeyId,
   secretAccessKey: fullConfig.s3.secretAccessKey,
+  sessionToken: fullConfig.s3.sessionToken,
 });
src/main/services/duckLake/instanceStore.service.ts (1)

434-447: ⚠️ Potential issue | 🔴 Critical

Security: sessionToken is not stripped from the persisted storage config, leaking it to plaintext JSON on disk.

secretAccessKey is explicitly deleted at Line 438 before writing to the JSON file, but sessionToken is not. This means the session token will be written in cleartext to instances.json, defeating the purpose of storing it in secure storage (Lines 254-260).

🔒 Proposed fix: strip sessionToken alongside secretAccessKey
       if (storagePersisted?.s3) {
         delete (storagePersisted.s3 as any).secretAccessKey;
+        delete (storagePersisted.s3 as any).sessionToken;
       }
🤖 Fix all issues with AI agents
In `@src/main/helpers/cloudAuth.helper.ts`:
- Around line 110-122: The session token is directly interpolated into the SQL
in the sessionTokenClause, creating an SQL injection risk; update the code that
builds sessionTokenClause (used in the CREATE OR REPLACE SECRET s3_secret block)
to escape single quotes in awsConfig.sessionToken (e.g., replace each ' with
''), or use an existing SQL-escaping helper if available, so the value is safely
quoted before concatenation into the returned SQL string.

In `@src/renderer/components/dataLake/DataLakeConnectionSelector.tsx`:
- Around line 275-279: providerConfig currently includes the temporary
sessionToken and is passed to createConnection.mutateAsync, which causes the
token to be persisted in plaintext; before calling createConnection.mutateAsync
(or before any persistence), remove or set sessionToken to undefined on
providerConfig (or clone providerConfig and delete the sessionToken property) so
only the non-sensitive fields are sent/stored; locate the providerConfig
assignment in DataLakeConnectionSelector.tsx and ensure the call site for
createConnection.mutateAsync uses the sanitized config without sessionToken.
🧹 Nitpick comments (2)
src/renderer/components/cloudExplorer/ExplorerBucketContent.tsx (1)

101-112: Consider parallelizing the two independent credential fetches.

getCloudAwsSecret and getCloudAwsSessionToken are independent calls that could run concurrently via Promise.all, similar to how retrieveCredentials does it in instanceStore.service.ts (Lines 324-327).

♻️ Suggested parallel fetch
         if (connection.provider === 'aws') {
-          const secret = await getCloudAwsSecret(connection.id);
-          const sessionToken = await getCloudAwsSessionToken(connection.id);
+          const [secret, sessionToken] = await Promise.all([
+            getCloudAwsSecret(connection.id),
+            getCloudAwsSessionToken(connection.id),
+          ]);
           (
             config as {
               secretAccessKey?: string;
src/renderer/components/cloudExplorer/ConnectionForm.tsx (1)

144-151: Consider parallelizing the two independent credential fetches.

Same pattern as flagged in ExplorerBucketContent.tsx — the secret and session token fetches are independent.

♻️ Suggested parallel fetch
         } else if (provider === 'aws') {
-          const stored = await getCloudAwsSecret(id);
-          const storedSessionToken = await getCloudAwsSessionToken(id);
-          setFormData((prev) => ({
-            ...prev,
-            secretAccessKey: stored || '',
-            sessionToken: storedSessionToken || '',
-          }));
+          const [stored, storedSessionToken] = await Promise.all([
+            getCloudAwsSecret(id),
+            getCloudAwsSessionToken(id),
+          ]);
+          setFormData((prev) => ({
+            ...prev,
+            secretAccessKey: stored || '',
+            sessionToken: storedSessionToken || '',
+          }));

@jasir99 jasir99 requested a review from nbesimi February 11, 2026 15:11
- Escape single quotes in AWS session tokens to prevent SQL injection
- Remove sessionToken from providerConfig before persistence

Session tokens are still stored securely via setCloudAwsSessionToken
but are no longer persisted in connection configs or vulnerable to
SQL injection in secret queries.
# Conflicts:
#	src/main/helpers/cloudAuth.helper.ts
#	src/renderer/components/cloudExplorer/ConnectionForm.tsx
#	src/renderer/components/cloudExplorer/ExplorerBucketContent.tsx
#	src/renderer/components/cloudExplorer/ExplorerBuckets.tsx
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/renderer/components/cloudExplorer/ConnectionForm.tsx`:
- Around line 383-394: The form currently writes setCloudAwsSessionToken only
when formData.sessionToken is non-empty and never removes a previously stored
token; update the useSecureStorage destructuring in ConnectionForm to include
deleteCloudAwsSessionToken, then change the AWS branch where
setCloudAwsSessionToken is called (referencing formData.sessionToken,
setCloudAwsSessionToken, deleteCloudAwsSessionToken, connId) so that if
formData.sessionToken.trim() is non-empty you call
setCloudAwsSessionToken(connId) else call deleteCloudAwsSessionToken(connId);
also remove sessionToken from config as already done (rawConfig/config handling
stays the same).
🧹 Nitpick comments (1)
src/renderer/components/cloudExplorer/ConnectionForm.tsx (1)

545-554: Session Token field lacks a show/hide toggle, unlike Secret Access Key.

The Secret Access Key field has a visibility toggle (showPassword state + eye icon), but the Session Token field is hardcoded to type="password" with no way to reveal the value. Minor UX inconsistency — consider reusing the same toggle pattern for parity, especially since session tokens are long and users may want to verify what they pasted.

@nbesimi nbesimi merged commit 908d49a into dev Feb 12, 2026
3 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Feb 13, 2026
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.

3 participants