Fix: OAuth2 token missing provider name#11561
Conversation
📝 WalkthroughWalkthroughThe changes update OAuth2 session handling in the Account controller to wrap generated OAuth tokens in HS256 JWTs containing both Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes 🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
Security Scan Results for PRDocker Image Scan Results
Source Code Scan Results🎉 No vulnerabilities found! |
🔄 PHP-Retry SummaryFlaky tests detected across commits: Commit
|
| Test | Retries | Total Time | Details |
|---|---|---|---|
UsageTest::testDatabaseStatsCollectionsAPI |
1 | 10.19s | Logs |
Commit afd8d8a - 4 flaky tests
| Test | Retries | Total Time | Details |
|---|---|---|---|
UsageTest::testDatabaseStatsCollectionsAPI |
1 | 10.28s | Logs |
RealtimeCustomClientTest::testChannelExecutions |
1 | 12.80s | Logs |
RealtimeCustomClientTest::testRelationshipPayloadHidesRelatedDoc |
1 | 30.19s | Logs |
RealtimeCustomClientTest::testConcurrentRealtimeTrafficCoroutines |
1 | 30.13s | Logs |
✨ Benchmark results
⚡ Benchmark Comparison
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
tests/e2e/Services/Account/AccountCustomClientTest.php (1)
2187-2216: Extract redirect-chain assertions into a helper to reduce drift.The two OAuth redirect walkthroughs are almost identical. A small helper (e.g.,
followOAuthRedirectChain) would keep expectations consistent and make future flow changes safer.Also applies to: 2247-2277
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/e2e/Services/Account/AccountCustomClientTest.php` around lines 2187 - 2216, Extract the repeated OAuth redirect walkthrough into a private helper (e.g., followOAuthRedirectChain) to centralize the GET->301->GET->301->GET->301->GET->200 sequence and reduce duplicated assertions; implement a method that accepts the Client instance and initial URL, performs successive oauthClient->call(Client::METHOD_GET, ..., followRedirects: false) calls, asserts the intermediate 301 status-codes and the expected location prefixes (the same prefixes currently asserted with assertStringStartsWith), checks for the two session cookies ('a_session_' . $this->getProject()['$id'] . '_legacy' and 'a_session_' . $this->getProject()['$id']), returns the final response and extracted oauthUserCookie, and then replace both duplicated blocks (around the current assertions using Client::METHOD_GET and cookie checks) with calls to this helper to reuse the logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@tests/e2e/Services/Account/AccountCustomClientTest.php`:
- Around line 2187-2203: The test reads $response['headers']['location']
repeatedly without checking that the 'location' key exists, which makes it
brittle and triggers static-analysis errors; update the AccountCustomClientTest
method to guard each redirect hop by asserting or checking that
isset($response['headers']['location']) (or array_key_exists) before indexing
and failing the test with a clear message if missing, then use the guarded value
for subsequent Client::call calls and apply the same pattern to the other
redirect hops around lines 2247-2274 to ensure CI stability.
---
Nitpick comments:
In `@tests/e2e/Services/Account/AccountCustomClientTest.php`:
- Around line 2187-2216: Extract the repeated OAuth redirect walkthrough into a
private helper (e.g., followOAuthRedirectChain) to centralize the
GET->301->GET->301->GET->301->GET->200 sequence and reduce duplicated
assertions; implement a method that accepts the Client instance and initial URL,
performs successive oauthClient->call(Client::METHOD_GET, ..., followRedirects:
false) calls, asserts the intermediate 301 status-codes and the expected
location prefixes (the same prefixes currently asserted with
assertStringStartsWith), checks for the two session cookies ('a_session_' .
$this->getProject()['$id'] . '_legacy' and 'a_session_' .
$this->getProject()['$id']), returns the final response and extracted
oauthUserCookie, and then replace both duplicated blocks (around the current
assertions using Client::METHOD_GET and cookie checks) with calls to this helper
to reuse the logic.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: c3c39f67-5cc4-48da-b47e-19f0d03b848a
📒 Files selected for processing (4)
app/config/collections/common.phpapp/controllers/api/account.phpcomposer.jsontests/e2e/Services/Account/AccountCustomClientTest.php
Greptile SummaryThis PR fixes a bug where the OAuth2 provider name was not being preserved when using the token-based OAuth2 flow ( The fix is in two parts:
A new schema attribute ( Key concern: The Confidence Score: 2/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant Client
participant Appwrite
participant OAuthProvider
participant DB as Database
Client->>Appwrite: GET /account/tokens/oauth2/:provider
Appwrite->>OAuthProvider: Redirect to OAuth provider
OAuthProvider->>Appwrite: OAuth callback with code
Appwrite->>DB: createDocument('tokens', {type:OAUTH2, provider:providerName})
Note over DB: provider field now stored (NEW)
Appwrite->>Client: Redirect to success URL with token params
Client->>Appwrite: POST /account/sessions/token
Appwrite->>DB: Verify token document, read provider attribute
Note over DB: Falls back to 'oauth2' if provider missing
Appwrite->>DB: createDocument('sessions', {provider: tokenProvider})
Appwrite->>Client: 201 Session with correct provider name
Last reviewed commit: afd8d8a |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
app/controllers/api/account.php (1)
212-226: Consider handling the missing provider case more gracefully.The current logic throws
USER_INVALID_TOKENat line 219 if the secret decodes as a valid JWT but lacks aproviderclaim. This exception isn't caught by theJWTExceptioncatch block, so it propagates up.While practically unlikely (an attacker would need the symmetric key to craft such a JWT), this conflates two distinct error conditions:
- "This is a malformed OAuth2 JWT"
- "This is not an OAuth2 JWT at all"
Consider restructuring so that a JWT without
provideris treated the same as a non-JWT (fall through), and let the validation at line 244 catch OAuth2 tokens without providers. This would be more defensive:Suggested approach
try { $jwtDecoder = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 60, 0); $payload = $jwtDecoder->decode($secret); - if (empty($payload['provider'])) { - throw new Exception(Exception::USER_INVALID_TOKEN); + if (!empty($payload['provider']) && !empty($payload['secret'])) { + $oauthProvider = $payload['provider']; + $secret = $payload['secret']; } - - $oauthProvider = $payload['provider']; - $secret = $payload['secret']; } catch (\Ahc\Jwt\JWTException) { // Not a JWT — use secret as-is (non-OAuth flows) }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/controllers/api/account.php` around lines 212 - 226, The JWT decode block using $jwtDecoder currently throws Exception::USER_INVALID_TOKEN when $payload['provider'] is empty, which escapes the JWTException catch and conflates "malformed OAuth2 JWT" with "not an OAuth2 JWT"; change the logic in the try block so that if $payload['provider'] is missing you do NOT throw Exception::USER_INVALID_TOKEN but instead treat it like a non-JWT: leave $oauthProvider as null and preserve $secret (or reset $secret to the original raw value) so execution falls through to the existing downstream validation (which will catch invalid OAuth2 tokens), while still only catching \Ahc\Jwt\JWTException for real decode errors.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@app/controllers/api/account.php`:
- Around line 212-226: The JWT decode block using $jwtDecoder currently throws
Exception::USER_INVALID_TOKEN when $payload['provider'] is empty, which escapes
the JWTException catch and conflates "malformed OAuth2 JWT" with "not an OAuth2
JWT"; change the logic in the try block so that if $payload['provider'] is
missing you do NOT throw Exception::USER_INVALID_TOKEN but instead treat it like
a non-JWT: leave $oauthProvider as null and preserve $secret (or reset $secret
to the original raw value) so execution falls through to the existing downstream
validation (which will catch invalid OAuth2 tokens), while still only catching
\Ahc\Jwt\JWTException for real decode errors.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 2d95ddc7-0514-49fc-bc35-8b222de22730
📒 Files selected for processing (2)
app/controllers/api/account.phpcomposer.json
✅ Files skipped from review due to trivial changes (1)
- composer.json
What does this PR do?
Ensures oauth provider is preserved in token oauth flow, same as session oauth flow.
Test Plan
New tests
Related PRs and Issues
x
Checklist