Skip to content

🔑 fix: Decode Action OAuth Credentials After Decryption - #14698

Open
lidonius1122 wants to merge 1 commit into
danny-avila:mainfrom
lidonius1122:fix/action-oauth-decode-credentials
Open

🔑 fix: Decode Action OAuth Credentials After Decryption#14698
lidonius1122 wants to merge 1 commit into
danny-avila:mainfrom
lidonius1122:fix/action-oauth-decode-credentials

Conversation

@lidonius1122

Copy link
Copy Markdown
Contributor

Summary

Fixes #14636

Action OAuth client credentials are URL-encoded before encryption: encryptSensitiveValue in api/server/services/ActionService.js applies encodeURIComponent() before encryptV2. The decrypt helpers in ActionService.js reverse both steps, but getAccessToken and refreshAccessToken in packages/api/src/oauth/tokens.ts decrypted with decryptV2 alone — so any client ID or secret containing special characters (@, +, =, /, :) was sent to the provider's token endpoint still URL-encoded (e.g. abc%40domain.com instead of abc@domain.com), causing 401s. Both exchange methods were affected: default_post (body params) and basic_auth_header (base64 header).

This PR adds a decryptSensitiveValue helper in tokens.ts that decodes after decrypting and uses it at all four call sites.

Backward compatibility: encoding-before-encryption was introduced in 299cabd (March 2025); credentials stored before that are encrypted without it. A legacy secret containing a raw % would make decodeURIComponent throw URIError, so the decode is wrapped in try/catch and falls back to the raw decrypted value. api/server/services/ActionService.js is untouched.

Change Type

  • Bug fix (non-breaking change which fixes an issue)

Testing

Added 6 test cases to packages/api/src/oauth/tokens.spec.ts (existing mock/helper patterns):

  • getAccessToken + refreshAccessToken with default_post: request body contains the decoded client_id/client_secret
  • getAccessToken + refreshAccessToken with basic_auth_header: Authorization header base64-decodes to decodedId:decodedSecret
  • Legacy unencoded credentials containing a stray %: no throw, passed through unchanged (both paths)

Verified the decode assertions fail against the previous code (reverting the call sites to decryptV2 makes the 4 decode tests fail), so the tests pin the actual bug.

  • cd packages/api && npx jest src/oauth → 5 suites, 68 tests passed
  • npx tsc --noEmit -p packages/api → clean
  • npx eslint on both touched files → clean

Checklist

  • My code adheres to this project's style guidelines
  • I have performed a self-review of my own code
  • My changes do not introduce new warnings
  • I have written tests demonstrating that my changes are effective or that my feature works
  • Local unit tests pass with my changes

Action client credentials are URL-encoded before encryption, but the OAuth
token exchange decrypted them with `decryptV2` alone, sending values like
`abc%40domain.com` to the provider and causing 401s for secrets containing
`@`, `+`, `=`, `/` or `:`. Decoding falls back to the raw decrypted value so
legacy secrets stored before 299cabd (which contain an unescaped `%`) still work.

Fixes danny-avila#14636
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.

[Bug]: OAuth action token exchange sends URL-encoded credentials to token endpoint

1 participant