Skip to content

Commit 0471005

Browse files
committed
Redirect unauthenticated browser requests to login page
- AuthMiddleware now detects browser vs API requests - Browser requests (Accept: text/html) redirect to /session/begin - API requests (Accept: application/json or XMLHttpRequest) return JSON 401 - Add docker:test:reset npm script for quick database reset
1 parent 8b71072 commit 0471005

File tree

2 files changed

+53
-0
lines changed

2 files changed

+53
-0
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
"docker:dev:stop": "docker compose -f docker/docker-compose.yaml --profile dev stop",
4242
"docker:test": "cp docker/Config.php src/Include/Config.php && docker compose -f docker/docker-compose.yaml --profile test up -d",
4343
"docker:test:down": "docker compose -f docker/docker-compose.yaml --profile test down -v",
44+
"docker:test:reset": "docker compose -f docker/docker-compose.yaml --profile ci down -v && docker compose -f docker/docker-compose.yaml -f docker/docker-compose.gh-actions.yaml --profile ci up -d --build",
4445
"docker:test:login:db": "docker compose -f docker/docker-compose.yaml --profile test exec database bash",
4546
"docker:test:login:web": "docker compose -f docker/docker-compose.yaml --profile test exec webserver-test bash",
4647
"docker:test:logs": "docker compose -f docker/docker-compose.yaml --profile test logs -f --tail=10",

src/ChurchCRM/Slim/Middleware/AuthMiddleware.php

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use ChurchCRM\Authentication\AuthenticationManager;
66
use ChurchCRM\Authentication\Requests\APITokenAuthenticationRequest;
7+
use ChurchCRM\dto\SystemURLs;
78
use ChurchCRM\Utils\LoggerUtils;
89
use Laminas\Diactoros\Response;
910
use Psr\Http\Message\ServerRequestInterface;
@@ -50,6 +51,12 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface
5051
'path' => $request->getUri()->getPath(),
5152
'method' => $request->getMethod()
5253
]);
54+
55+
// Check if this is a browser request - redirect to login instead of JSON error
56+
if ($this->isBrowserRequest($request)) {
57+
return $this->redirectToLogin();
58+
}
59+
5360
$response = new Response();
5461
$errorBody = json_encode(['error' => gettext('No logged in user'), 'code' => 401]);
5562
$response->getBody()->write($errorBody);
@@ -69,4 +76,49 @@ private function isPath(ServerRequestInterface $request, string $pathPart): bool
6976

7077
return false;
7178
}
79+
80+
/**
81+
* Check if request is from a browser (expects HTML) vs API client (expects JSON)
82+
*/
83+
private function isBrowserRequest(ServerRequestInterface $request): bool
84+
{
85+
$path = $request->getUri()->getPath();
86+
87+
// API routes should always return JSON
88+
if (str_contains($path, '/api/')) {
89+
return false;
90+
}
91+
92+
// Check Accept header - browsers typically send text/html
93+
$acceptHeader = $request->getHeaderLine('Accept');
94+
if (!empty($acceptHeader)) {
95+
// If client explicitly wants JSON, it's an API request
96+
if (str_contains($acceptHeader, 'application/json') && !str_contains($acceptHeader, 'text/html')) {
97+
return false;
98+
}
99+
// If client accepts HTML, treat as browser
100+
if (str_contains($acceptHeader, 'text/html')) {
101+
return true;
102+
}
103+
}
104+
105+
// Check X-Requested-With header (AJAX requests)
106+
if ($request->getHeaderLine('X-Requested-With') === 'XMLHttpRequest') {
107+
return false;
108+
}
109+
110+
// Default to browser for non-API routes
111+
return true;
112+
}
113+
114+
/**
115+
* Redirect to the login page
116+
*/
117+
private function redirectToLogin(): ResponseInterface
118+
{
119+
$response = new Response();
120+
$redirectUrl = SystemURLs::getRootPath() . '/session/begin';
121+
122+
return $response->withStatus(302)->withHeader('Location', $redirectUrl);
123+
}
72124
}

0 commit comments

Comments
 (0)