summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Weipert <git@mail.dweipert.de>2025-08-14 14:37:56 +0200
committerDaniel Weipert <git@mail.dweipert.de>2025-08-14 14:37:56 +0200
commitc135fcf9041c604b32827a1cb027010bca5915ab (patch)
tree95a2331d5f0bc4a736aa0e716330c39b3fa1ea26
parent3f4b51b99a4f4dc41dbdce7f34afe7e15d3d426e (diff)
POST login possible
-rw-r--r--.gitignore3
-rw-r--r--Readme.md2
-rw-r--r--src/App.php1
-rw-r--r--src/Controllers/LoginController.php25
-rw-r--r--src/ErrorResponse.php16
-rw-r--r--src/Errors/ErrorCode.php (renamed from src/ErrorCode.php)2
-rw-r--r--src/Errors/ErrorResponse.php30
-rw-r--r--src/Errors/Exception.php21
-rw-r--r--src/Errors/RateLimitError.php20
-rw-r--r--src/Errors/UnknownError.php16
-rw-r--r--src/Router/Router.php (renamed from src/Router.php)23
-rw-r--r--src/Router/routes_client_server.php (renamed from src/routes.php)18
-rw-r--r--src/Router/routes_server_server.php24
-rw-r--r--src/Support/Parser.php27
-rw-r--r--src/Types/AuthenticationType.php14
15 files changed, 202 insertions, 40 deletions
diff --git a/.gitignore b/.gitignore
index ca29c74..62a128a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,4 +3,5 @@
.env
-credentials.json
+/credentials.json
+/store/
diff --git a/Readme.md b/Readme.md
index fda8853..9064374 100644
--- a/Readme.md
+++ b/Readme.md
@@ -1 +1 @@
-Matrix Specification: https://spec.matrix.org/v1.5/
+Matrix Specification: https://spec.matrix.org/v1.15/
diff --git a/src/App.php b/src/App.php
index 33f71ef..9b1edf9 100644
--- a/src/App.php
+++ b/src/App.php
@@ -2,6 +2,7 @@
namespace App;
+use App\Router\Router;
use Symfony\Component\Dotenv\Dotenv;
class App
diff --git a/src/Controllers/LoginController.php b/src/Controllers/LoginController.php
index f5ca3be..d48628b 100644
--- a/src/Controllers/LoginController.php
+++ b/src/Controllers/LoginController.php
@@ -2,6 +2,8 @@
namespace App\Controllers;
+use App\Errors\UnknownError;
+use App\Support\Parser;
use App\Types\LoginFlow;
use App\Types\LoginType;
use Symfony\Component\HttpFoundation\Request;
@@ -10,6 +12,10 @@ use Symfony\Component\HttpFoundation\JsonResponse;
class LoginController
{
+ /**
+ * GET /_matrix/client/r0/login
+ * GET /_matrix/client/v3/login
+ */
public function supportedLoginTypes(): Response
{
return new JsonResponse([
@@ -19,16 +25,33 @@ class LoginController
]);
}
+ /**
+ * POST /_matrix/client/v3/login
+ */
public function login(): Response
{
$request = Request::createFromGlobals();
+ $content = json_decode($request->getContent(), true);
+
+ // validate login type
+ $loginType = null;
+ try {
+ $loginType = LoginType::from($content["type"]);
+ } catch (\ValueError $error) {
+ throw new UnknownError("Bad login type.", Response::HTTP_BAD_REQUEST);
+ }
+
+ // get user name
+ $user = Parser::parseUser($content["identifier"]["user"]);
+
+ #if ($loginType == LoginType::PASSWORD) {}
return new JsonResponse([
"access_token" => "abc123",
"device_id" => "ABC",
"expires_in_ms" => 60000,
"refresh_token" => "def456",
- "user_id" => "@php:localhost",
+ "user_id" => "@{$user["username"]}:{$_ENV["DOMAIN"]}",
#"well_known" => [],
]);
}
diff --git a/src/ErrorResponse.php b/src/ErrorResponse.php
deleted file mode 100644
index 05a77d1..0000000
--- a/src/ErrorResponse.php
+++ /dev/null
@@ -1,16 +0,0 @@
-<?php
-
-namespace App;
-
-use Symfony\Component\HttpFoundation\JsonResponse;
-
-class ErrorResponse extends JsonResponse
-{
- public function __construct(ErrorCode $code, string $message)
- {
- parent::__construct([
- "errcode" => $code,
- "error" => $message,
- ]);
- }
-}
diff --git a/src/ErrorCode.php b/src/Errors/ErrorCode.php
index 2149d37..b59f0a1 100644
--- a/src/ErrorCode.php
+++ b/src/Errors/ErrorCode.php
@@ -1,6 +1,6 @@
<?php
-namespace App;
+namespace App\Errors;
enum ErrorCode: string
{
diff --git a/src/Errors/ErrorResponse.php b/src/Errors/ErrorResponse.php
new file mode 100644
index 0000000..6248659
--- /dev/null
+++ b/src/Errors/ErrorResponse.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace App\Errors;
+
+use Symfony\Component\HttpFoundation\JsonResponse;
+
+class ErrorResponse extends JsonResponse
+{
+ public function __construct(ErrorCode $code, string $message, int $httpCode)
+ {
+ parent::__construct(
+ [
+ "errcode" => $code,
+ "error" => $message,
+ ],
+ $httpCode
+ );
+ }
+
+ public static function fromException(Exception $exception): self
+ {
+ $self = new self($exception->getErrorCode(), $exception->getMessage(), $exception->getCode());
+
+ $self->setData(
+ json_decode($self->data, true) + $exception->getAdditionalData()
+ );
+
+ return $self;
+ }
+}
diff --git a/src/Errors/Exception.php b/src/Errors/Exception.php
new file mode 100644
index 0000000..ccb124e
--- /dev/null
+++ b/src/Errors/Exception.php
@@ -0,0 +1,21 @@
+<?php
+
+namespace App\Errors;
+
+abstract class Exception extends \RuntimeException
+{
+ public function __construct(private ErrorCode $errorCode, string $message, int $httpCode)
+ {
+ parent::__construct($message, $httpCode);
+ }
+
+ public function getErrorCode(): ErrorCode
+ {
+ return $this->errorCode;
+ }
+
+ /**
+ * @return void
+ */
+ abstract public function getAdditionalData(): array;
+}
diff --git a/src/Errors/RateLimitError.php b/src/Errors/RateLimitError.php
new file mode 100644
index 0000000..d84f66a
--- /dev/null
+++ b/src/Errors/RateLimitError.php
@@ -0,0 +1,20 @@
+<?php
+
+namespace App\Errors;
+
+use Symfony\Component\HttpFoundation\Response;
+
+class RateLimitError extends Exception
+{
+ public function __construct(private int $retryAfter)
+ {
+ parent::__construct(ErrorCode::LIMIT_EXCEEDED, "Too many requests", Response::HTTP_TOO_MANY_REQUESTS);
+ }
+
+ public function getAdditionalData(): array
+ {
+ return [
+ "retry_after_ms" => $this->retryAfter,
+ ];
+ }
+}
diff --git a/src/Errors/UnknownError.php b/src/Errors/UnknownError.php
new file mode 100644
index 0000000..f861597
--- /dev/null
+++ b/src/Errors/UnknownError.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace App\Errors;
+
+class UnknownError extends Exception
+{
+ public function __construct(string $message, int $httpCode)
+ {
+ parent::__construct(ErrorCode::UNKNOWN, $message, $httpCode);
+ }
+
+ public function getAdditionalData(): array
+ {
+ return [];
+ }
+}
diff --git a/src/Router.php b/src/Router/Router.php
index 437d995..b167142 100644
--- a/src/Router.php
+++ b/src/Router/Router.php
@@ -1,9 +1,15 @@
<?php
-namespace App;
+namespace App\Router;
+use App\Errors\ErrorCode;
+use App\Errors\ErrorResponse;
+use App\Errors\Exception;
+use App\Singleton;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\Routing\Exception\MethodNotAllowedException;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
use Symfony\Component\Routing\Loader\Configurator\RouteConfigurator;
use Symfony\Component\Routing\Matcher\UrlMatcher;
use Symfony\Component\Routing\RequestContext;
@@ -54,8 +60,14 @@ class Router
$method = $match["_controller"][1];
return (new $class)->$method();
+ } catch (Exception $exception) {
+ return ErrorResponse::fromException($exception);
+ } catch (ResourceNotFoundException $exception) {
+ return new ErrorResponse(ErrorCode::NOT_FOUND, "404", Response::HTTP_NOT_FOUND);
+ } catch (MethodNotAllowedException $exception) {
+ return new ErrorResponse(ErrorCode::FORBIDDEN, "403", Response::HTTP_FORBIDDEN);
} catch (\Exception $exception) {
- return new ErrorResponse(ErrorCode::UNKNOWN, "Unknown error occured");
+ return new ErrorResponse(ErrorCode::UNKNOWN, "Unknown error occured", Response::HTTP_INTERNAL_SERVER_ERROR);
}
}
@@ -64,7 +76,10 @@ class Router
*/
private function addRoutes(): void
{
- $routes = include_once(__DIR__ . "/routes.php");
- $routes($this->configurator);
+ $routesClientServer = include_once(__DIR__ . "/routes_client_server.php");
+ $routesClientServer($this->configurator);
+
+ $routesServerServer = include_once(__DIR__ . "/routes_server_server.php");
+ $routesServerServer($this->configurator);
}
}
diff --git a/src/routes.php b/src/Router/routes_client_server.php
index 2d1c3e0..eff0be5 100644
--- a/src/routes.php
+++ b/src/Router/routes_client_server.php
@@ -1,6 +1,6 @@
<?php
-namespace App;
+namespace App\Router;
use App\Controllers\LoginController;
use App\Controllers\ServerDiscoveryController;
@@ -10,11 +10,6 @@ use Symfony\Component\Routing\Loader\Configurator\RouteConfigurator;
return function (RouteConfigurator $routes): void
{
$routes
- ->add("well_known_matrix_server", "/.well-known/matrix/server")
- ->controller([ServerDiscoveryController::class, "server"])
- ->methods(["GET"]);
-
- $routes
->add("well_known_matrix_client", "/.well-known/matrix/client")
->controller([ServerDiscoveryController::class, "client"])
->methods(["GET"]);
@@ -25,19 +20,10 @@ return function (RouteConfigurator $routes): void
->methods(["GET"]);
$routes
- ->add("matrix_federation_version", "/_matrix/federation/v1/version")
- ->controller([ServerImplementationController::class, "version"])
- ->methods(["GET"]);
-
- $routes
->add("matrix_client_versions", "/_matrix/client/versions")
->controller([ServerImplementationController::class, "versions"])
->methods(["GET"]);
- # /_matrix/key/v2/server
- # /_matrix/key/v2/query
- # /_matrix/key/v2/query/{serverName}
-
$supportedLoginTypes = [LoginController::class, "supportedLoginTypes"];
$routes
->add("matrix_client_r0_login_types", "/_matrix/client/r0/login")
@@ -50,6 +36,6 @@ return function (RouteConfigurator $routes): void
$routes
->add("matrix_client_v3_login", "/_matrix/client/v3/login")
- ->controller($supportedLoginTypes)
+ ->controller([LoginController::class, "login"])
->methods(["POST"]);
};
diff --git a/src/Router/routes_server_server.php b/src/Router/routes_server_server.php
new file mode 100644
index 0000000..2e85a17
--- /dev/null
+++ b/src/Router/routes_server_server.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace App\Router;
+
+use App\Controllers\ServerDiscoveryController;
+use App\Controllers\ServerImplementationController;
+use Symfony\Component\Routing\Loader\Configurator\RouteConfigurator;
+
+return function (RouteConfigurator $routes): void
+{
+ $routes
+ ->add("well_known_matrix_server", "/.well-known/matrix/server")
+ ->controller([ServerDiscoveryController::class, "server"])
+ ->methods(["GET"]);
+
+ $routes
+ ->add("matrix_federation_version", "/_matrix/federation/v1/version")
+ ->controller([ServerImplementationController::class, "version"])
+ ->methods(["GET"]);
+
+ # /_matrix/key/v2/server
+ # /_matrix/key/v2/query
+ # /_matrix/key/v2/query/{serverName}
+};
diff --git a/src/Support/Parser.php b/src/Support/Parser.php
new file mode 100644
index 0000000..d850de1
--- /dev/null
+++ b/src/Support/Parser.php
@@ -0,0 +1,27 @@
+<?php
+
+namespace App\Support;
+
+class Parser
+{
+ /**
+ * @return array<string, string>
+ */
+ public static function parseUser(string $user): array
+ {
+ $username = $user;
+ $server = "";
+
+ if (str_starts_with($user, "@")) {
+ $username = substr($user, 1);
+ $usernameParts = explode(":", $username);
+ $username = $usernameParts[0];
+ $server = $usernameParts[1];
+ }
+
+ return [
+ "username" => $username,
+ "server" => $server,
+ ];
+ }
+}
diff --git a/src/Types/AuthenticationType.php b/src/Types/AuthenticationType.php
new file mode 100644
index 0000000..dc768d0
--- /dev/null
+++ b/src/Types/AuthenticationType.php
@@ -0,0 +1,14 @@
+<?php
+
+namespace App\Types;
+
+enum AuthenticationType: string
+{
+ case PASSWORD = "m.login.password";
+ case RECAPTCHA = "m.login.recaptcha";
+ case SSO = "m.login.sso";
+ case EMAIL_IDENTITY = "m.login.email.identity";
+ case MSISDN = "m.login.msisdn";
+ case DUMMY = "m.login.dummy";
+ case REGISTRATION_TOKEN = "m.login.registration_token";
+}