mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Merge pull request #11276 from appwrite/fix-rule-oauth
Fix: rule oauth flow
This commit is contained in:
@@ -1469,13 +1469,14 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
->inject('devKey')
|
||||
->inject('user')
|
||||
->inject('dbForProject')
|
||||
->inject('dbForPlatform')
|
||||
->inject('geodb')
|
||||
->inject('queueForEvents')
|
||||
->inject('store')
|
||||
->inject('proofForPassword')
|
||||
->inject('proofForToken')
|
||||
->inject('authorization')
|
||||
->action(function (string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response, Document $project, Validator $redirectValidator, Document $devKey, User $user, Database $dbForProject, Reader $geodb, Event $queueForEvents, Store $store, ProofsPassword $proofForPassword, ProofsToken $proofForToken, Authorization $authorization) use ($oauthDefaultSuccess) {
|
||||
->action(function (string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response, Document $project, Validator $redirectValidator, Document $devKey, User $user, Database $dbForProject, Database $dbForPlatform, Reader $geodb, Event $queueForEvents, Store $store, ProofsPassword $proofForPassword, ProofsToken $proofForToken, Authorization $authorization) use ($oauthDefaultSuccess) {
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
|
||||
$port = $request->getPort();
|
||||
$callbackBase = $protocol . '://' . $request->getHostname();
|
||||
@@ -1512,6 +1513,25 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
$state = $defaultState;
|
||||
}
|
||||
|
||||
// Allow redirect to rule URL if related to project
|
||||
//Check if $redirectValidator is instance of Redirect class
|
||||
if ($redirectValidator instanceof Redirect) {
|
||||
$rules = $authorization->skip(fn () => $dbForPlatform->find('rules', [
|
||||
Query::equal('domain', [
|
||||
parse_url($state['success'], PHP_URL_HOST),
|
||||
parse_url($state['failure'], PHP_URL_HOST)
|
||||
]),
|
||||
Query::equal('projectInternalId', [$project->getSequence()]),
|
||||
Query::limit(2)
|
||||
]));
|
||||
|
||||
foreach ($rules as $rule) {
|
||||
$allowedHostnames = $redirectValidator->getAllowedHostnames();
|
||||
$allowedHostnames[] = $rule->getAttribute('domain', '');
|
||||
$redirectValidator->setAllowedHostnames($allowedHostnames);
|
||||
}
|
||||
}
|
||||
|
||||
if ($devKey->isEmpty() && !$redirectValidator->isValid($state['success'])) {
|
||||
throw new Exception(Exception::PROJECT_INVALID_SUCCESS_URL);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,27 @@ class Origin extends Validator
|
||||
{
|
||||
}
|
||||
|
||||
public function setAllowedHostnames(array $allowedHostnames): self
|
||||
{
|
||||
$this->allowedHostnames = $allowedHostnames;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setAllowedSchemes(array $allowedSchemes): self
|
||||
{
|
||||
$this->allowedSchemes = $allowedSchemes;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAllowedHostnames(): array
|
||||
{
|
||||
return $this->allowedHostnames;
|
||||
}
|
||||
|
||||
public function getAllowedSchemes(): array
|
||||
{
|
||||
return $this->allowedSchemes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if Origin is valid.
|
||||
|
||||
@@ -5191,6 +5191,24 @@ class ProjectsConsoleClientTest extends Scope
|
||||
], followRedirects: false);
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
// Also ensure final step blocks unknown redirect URL
|
||||
$response = $this->client->call(Client::METHOD_GET, '/account/sessions/oauth2/' . $provider . '/redirect', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $projectId,
|
||||
'origin' => '',
|
||||
'referer' => 'https://mockserver.com',
|
||||
], [
|
||||
'code' => 'any-code',
|
||||
'state' => \json_encode([
|
||||
'success' => 'https://domain-without-rule.com',
|
||||
'failure' => 'https://domain-without-rule.com'
|
||||
]),
|
||||
'error' => '',
|
||||
'error_description' => '',
|
||||
], followRedirects: false);
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
$this->assertStringContainsString('project_invalid_success_url', $response['body']);
|
||||
|
||||
// Ensure rule's domain can be redirect URL
|
||||
$response = $this->client->call(Client::METHOD_GET, '/account/sessions/oauth2/' . $provider, [
|
||||
'content-type' => 'application/json',
|
||||
@@ -5203,6 +5221,24 @@ class ProjectsConsoleClientTest extends Scope
|
||||
], followRedirects: false);
|
||||
$this->assertEquals(301, $response['headers']['status-code']);
|
||||
|
||||
// Also ensure final step allows redirect URL
|
||||
$response = $this->client->call(Client::METHOD_GET, '/account/sessions/oauth2/' . $provider . '/redirect', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $projectId,
|
||||
'origin' => '',
|
||||
'referer' => 'https://mockserver.com',
|
||||
], [
|
||||
'code' => 'any-code',
|
||||
'state' => \json_encode([
|
||||
'success' => 'https://' . $domain,
|
||||
'failure' => 'https://' . $domain
|
||||
]),
|
||||
'error' => '',
|
||||
'error_deescription' => '',
|
||||
], followRedirects: false);
|
||||
$this->assertEquals(301, $response['headers']['status-code']);
|
||||
$this->assertStringContainsString('https://' . $domain, $response['headers']['location']);
|
||||
|
||||
// Ensure unknown domain cannot be redirect URL
|
||||
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/magic-url', [
|
||||
'content-type' => 'application/json',
|
||||
|
||||
@@ -74,4 +74,60 @@ class OriginTest extends TestCase
|
||||
$this->assertEquals(false, $validator->isValid('random-scheme://localhost'));
|
||||
$this->assertEquals('Invalid Scheme. The scheme used (random-scheme) in the Origin (random-scheme://localhost) is not supported. If you are using a custom scheme, please change it to `appwrite-callback-<PROJECT_ID>`', $validator->getDescription());
|
||||
}
|
||||
|
||||
public function testGetAllowedHostnames(): void
|
||||
{
|
||||
$validator = new Origin(
|
||||
allowedHostnames: ['appwrite.io', 'localhost'],
|
||||
allowedSchemes: ['exp']
|
||||
);
|
||||
|
||||
$this->assertEquals(['appwrite.io', 'localhost'], $validator->getAllowedHostnames());
|
||||
}
|
||||
|
||||
public function testGetAllowedSchemes(): void
|
||||
{
|
||||
$validator = new Origin(
|
||||
allowedHostnames: ['appwrite.io'],
|
||||
allowedSchemes: ['exp', 'appwrite-callback-123']
|
||||
);
|
||||
|
||||
$this->assertEquals(['exp', 'appwrite-callback-123'], $validator->getAllowedSchemes());
|
||||
}
|
||||
|
||||
public function testSetAllowedHostnames(): void
|
||||
{
|
||||
$validator = new Origin(
|
||||
allowedHostnames: ['appwrite.io'],
|
||||
allowedSchemes: ['exp']
|
||||
);
|
||||
|
||||
$this->assertEquals(true, $validator->isValid('https://appwrite.io'));
|
||||
$this->assertEquals(false, $validator->isValid('https://example.com'));
|
||||
|
||||
$result = $validator->setAllowedHostnames(['example.com']);
|
||||
|
||||
$this->assertSame($validator, $result);
|
||||
$this->assertEquals(['example.com'], $validator->getAllowedHostnames());
|
||||
$this->assertEquals(true, $validator->isValid('https://example.com'));
|
||||
$this->assertEquals(false, $validator->isValid('https://appwrite.io'));
|
||||
}
|
||||
|
||||
public function testSetAllowedSchemes(): void
|
||||
{
|
||||
$validator = new Origin(
|
||||
allowedHostnames: ['appwrite.io'],
|
||||
allowedSchemes: ['exp']
|
||||
);
|
||||
|
||||
$this->assertEquals(true, $validator->isValid('exp://'));
|
||||
$this->assertEquals(false, $validator->isValid('appwrite-callback-456://'));
|
||||
|
||||
$result = $validator->setAllowedSchemes(['appwrite-callback-456']);
|
||||
|
||||
$this->assertSame($validator, $result);
|
||||
$this->assertEquals(['appwrite-callback-456'], $validator->getAllowedSchemes());
|
||||
$this->assertEquals(true, $validator->isValid('appwrite-callback-456://'));
|
||||
$this->assertEquals(false, $validator->isValid('exp://'));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user