Skip to content

Feat/clean dropbox oauth#737

Open
jeffrey-martinez wants to merge 3 commits into
masterfrom
feat/clean-dropbox-oauth
Open

Feat/clean dropbox oauth#737
jeffrey-martinez wants to merge 3 commits into
masterfrom
feat/clean-dropbox-oauth

Conversation

@jeffrey-martinez
Copy link
Copy Markdown
Contributor

Refactored the getAccessTokenFromCode method in dropbox.ts to use gaxios and transmit OAuth parameters (code, client_secret, etc.) in the HTTP request body encoded as application/x-www-form-urlencoded, instead of passing them as URL query parameters in a POST request

Refactored the getAccessTokenFromCode method in dropbox.ts to use gaxios and transmit OAuth parameters (code, client_secret, etc.) in the HTTP request body encoded as application/x-www-form-urlencoded, instead of passing them as URL query parameters in a POST request
@jeffrey-martinez jeffrey-martinez requested a review from a team as a code owner April 27, 2026 23:15
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request enhances OAuth state management and security across several actions. Key updates include adding support for both encrypted and unencrypted state payloads in Airtable, migrating Dropbox token exchange to a more secure form-encoded body request using gaxios to prevent secret leakage in logs, and fixing a bug where state_url was not correctly mapped from the request root. Additionally, the oauthMaybeEncryptTokens method now returns raw objects instead of strings when encryption is disabled to ensure gaxios sets the correct Content-Type header. Review feedback identifies critical safety issues in the Airtable implementation where null checks are missing after token extraction and suggests simplifying the authentication check logic to improve robustness.

Comment on lines +57 to +61
const stateJson = await this.oauthExtractTokensFromStateJson(request.params.state_json, request.webhookId);
tokens = airtable_tokens_1.AirtableTokens.fromJson(stateJson);
accessToken = tokens.access_token;
const encrypted = await this.oauthMaybeEncryptTokens(new airtable_tokens_1.AirtableTokens(tokens.refresh_token, accessToken, tokens.redirectUri), request.webhookId);
state.data = typeof encrypted === "string" ? encrypted : JSON.stringify(encrypted);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

The call to AirtableTokens.fromJson(stateJson) is unsafe because oauthExtractTokensFromStateJson returns null if decryption fails or the JSON is malformed. This will cause a crash when attempting to access properties on null. You should ensure stateJson is truthy before proceeding.

Suggested change
const stateJson = await this.oauthExtractTokensFromStateJson(request.params.state_json, request.webhookId);
tokens = airtable_tokens_1.AirtableTokens.fromJson(stateJson);
accessToken = tokens.access_token;
const encrypted = await this.oauthMaybeEncryptTokens(new airtable_tokens_1.AirtableTokens(tokens.refresh_token, accessToken, tokens.redirectUri), request.webhookId);
state.data = typeof encrypted === "string" ? encrypted : JSON.stringify(encrypted);
const stateJson = await this.oauthExtractTokensFromStateJson(request.params.state_json, request.webhookId);
if (stateJson) {
tokens = airtable_tokens_1.AirtableTokens.fromJson(stateJson);
accessToken = tokens.access_token;
const encrypted = await this.oauthMaybeEncryptTokens(new airtable_tokens_1.AirtableTokens(tokens.refresh_token, accessToken, tokens.redirectUri), request.webhookId);
state.data = typeof encrypted === "string" ? encrypted : JSON.stringify(encrypted);
}

Comment on lines +136 to +138
const stateJson = await this.oauthExtractTokensFromStateJson(request.params.state_json, request.webhookId);
tokens = airtable_tokens_1.AirtableTokens.fromJson(stateJson);
accessToken = tokens.access_token;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

Similar to the issue in the execute method, AirtableTokens.fromJson(stateJson) will throw an error if stateJson is null. A check for a truthy stateJson is required here as well.

Suggested change
const stateJson = await this.oauthExtractTokensFromStateJson(request.params.state_json, request.webhookId);
tokens = airtable_tokens_1.AirtableTokens.fromJson(stateJson);
accessToken = tokens.access_token;
const stateJson = await this.oauthExtractTokensFromStateJson(request.params.state_json, request.webhookId);
if (stateJson) {
tokens = airtable_tokens_1.AirtableTokens.fromJson(stateJson);
accessToken = tokens.access_token;
}

Comment on lines +219 to 228
const parsedState = JSON.parse(request.params.state_json);
if (parsedState.cid && parsedState.payload) {
const stateJson = await this.oauthExtractTokensFromStateJson(request.params.state_json, request.webhookId);
return !!stateJson;
}
else {
// Keeping the literal old code to ensure no regressions for unencrypted payloads
const tokens = airtable_tokens_1.AirtableTokens.fromJson(parsedState);
return !!tokens.access_token;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

The JSON.parse call on request.params.state_json is unsafe as it is not wrapped in a try...catch block, which could lead to unhandled exceptions if the input is malformed. Furthermore, the logic can be significantly simplified by relying on oauthExtractTokensFromStateJson, which already handles both encrypted and unencrypted states safely and returns the parsed object.

Suggested change
const parsedState = JSON.parse(request.params.state_json);
if (parsedState.cid && parsedState.payload) {
const stateJson = await this.oauthExtractTokensFromStateJson(request.params.state_json, request.webhookId);
return !!stateJson;
}
else {
// Keeping the literal old code to ensure no regressions for unencrypted payloads
const tokens = airtable_tokens_1.AirtableTokens.fromJson(parsedState);
return !!tokens.access_token;
}
const stateJson = await this.oauthExtractTokensFromStateJson(request.params.state_json, request.webhookId);
if (stateJson) {
const tokens = airtable_tokens_1.AirtableTokens.fromJson(stateJson);
return !!tokens.access_token;
}

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.

1 participant