Skip to content

Commit ef1ff0d

Browse files
committed
fix: remove auth header from non-auth routes
1 parent f39d2b6 commit ef1ff0d

5 files changed

Lines changed: 137 additions & 8 deletions

File tree

src/Resolvers/HeadersResolver.php

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,10 @@ public function resolve(
5757
// Prepare the final headers by merging the resolved headers with the original request headers.
5858
return $this->prepareMappedHeaders(
5959
$requestData,
60-
overrides: [
61-
// Prioritize resolved headers over request headers.
62-
HttpHeaderType::AUTHORIZATION->value => Arr::get($resolvedHeaders, 'authorization')
63-
?? $requestData?->headers?->authorization,
64-
]
60+
overrides: array_filter([
61+
// Prioritize resolved headers over request headers
62+
HttpHeaderType::AUTHORIZATION->value => Arr::get($resolvedHeaders, 'authorization'),
63+
], fn($value) => ! is_null($value))
6564
);
6665
}
6766
}

src/Traits/RequestParsingTrait.php

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,12 @@ protected function prepareMappedHeaders(?RequestData $requestData, array $overri
111111
{
112112
$headersData = $requestData?->headers;
113113

114+
// Only add authorization header if the route requires authentication
115+
$authenticationHeader = $this->routeRequiresAuth($requestData?->routeName) ? $headersData?->authorization : null;
116+
114117
// Map the headers to the correct format as hyphenated capitalization
115118
$mappedHeaders = [
116-
HttpHeaderType::AUTHORIZATION->value => $headersData?->authorization,
119+
HttpHeaderType::AUTHORIZATION->value => $authenticationHeader,
117120
HttpHeaderType::ACCEPT->value => $headersData?->accept,
118121
HttpHeaderType::ACCEPT_LANGUAGE->value => $headersData?->acceptLanguage,
119122
];
@@ -164,4 +167,30 @@ protected function prepareCookies(?RequestData $requestData, array $overrides =
164167
fn ($value) => ! is_null($value)
165168
);
166169
}
170+
171+
/**
172+
* Check if a given route requires authentication by inspecting its middleware.
173+
*
174+
* @param string|null $routeName
175+
*
176+
* @return bool
177+
*/
178+
private function routeRequiresAuth(?string $routeName): bool
179+
{
180+
if ($routeName) {
181+
$middlewareArray = app('router')
182+
->getRoutes()
183+
->getByName($routeName)?->gatherMiddleware() ?? [];
184+
185+
// Check if any middleware has 'auth' prefix
186+
foreach ($middlewareArray as $middleware) {
187+
// catches: 'auth', 'auth:api', 'auth:sanctum', 'auth:web'
188+
if (str_starts_with($middleware, 'auth')) {
189+
return true;
190+
}
191+
}
192+
}
193+
194+
return false;
195+
}
167196
}

tests/Resolvers/HeadersResolverTest.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,11 @@ public function it_tests_if_authentication_headers_are_forwarded(): void
5858
/* SETUP */
5959
$bearerToken = 'your-token-here';
6060
$request = Request::create(
61-
uri: "/sample_trigger_route",
61+
uri: "/auth-protected-route",
6262
method: Request::METHOD_GET,
6363
);
6464
$request->headers->set(HttpHeaderType::AUTHORIZATION->value, 'Bearer ' . $bearerToken);
65+
$request->setRouteResolver(fn () => $this->app['router']->getRoutes()->match($request));
6566
$this->requestData = RequestData::fromRequest($request);
6667

6768
/* EXECUTE */

tests/TestCase.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,5 +100,20 @@ protected function defineRoutes($router): void
100100
->get('/sample-target-route-with-middleware', function () {
101101
return new Response('ok', Response::HTTP_OK);
102102
})->name('sample.target.route.with.middleware');
103+
104+
// Define route with auth middleware
105+
$router->middleware('auth')->get('/auth-protected-route', function () {
106+
return new Response('ok', Response::HTTP_OK);
107+
})->name('auth.protected.route');
108+
109+
// Define public route with no auth middleware
110+
$router->get('/public-route-no-auth', function () {
111+
return new Response('ok', Response::HTTP_OK);
112+
})->name('public.route.no.auth');
113+
114+
// Define route with auth middleware that requires token from login request
115+
$router->middleware('auth')->get('/sample-target-route-requires-token-from-login-request', function () {
116+
return new Response('ok', Response::HTTP_OK);
117+
})->name('sample.target.route.requires.token.from.login.request');
103118
}
104119
}

tests/Traits/RequestParsingTraitTest.php

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,6 @@ public function it_prepares_mapped_headers_with_defaults_and_default_prefetch_he
351351
/* ASSERT */
352352
$this->assertSame(
353353
[
354-
HttpHeaderType::AUTHORIZATION->value => 'Bearer token',
355354
HttpHeaderType::ACCEPT->value => 'application/json',
356355
HttpHeaderType::ACCEPT_LANGUAGE->value => 'en-US',
357356
config('demon-dextral-horn.defaults.prefetch_header') => PrefetchType::AUTO->value,
@@ -388,6 +387,92 @@ public function it_applies_header_overrides(): void
388387
$this->assertSame($acceptValue, Arr::get($headers, HttpHeaderType::ACCEPT->value));
389388
}
390389

390+
#[Test]
391+
public function it_applies_authorization_header_override(): void
392+
{
393+
/* SETUP */
394+
$headersData = new HeadersData(
395+
authorization: null,
396+
accept: 'application/json',
397+
acceptLanguage: 'en-US'
398+
);
399+
$requestData = new RequestData(
400+
uri: '/sample_route',
401+
method: Request::METHOD_GET,
402+
headers: $headersData,
403+
routeName: 'sample.route',
404+
);
405+
$authValue = 'Bearer overridden_token';
406+
$overrides = [
407+
HttpHeaderType::AUTHORIZATION->value => $authValue,
408+
];
409+
410+
/* EXECUTE */
411+
$headers = $this->anonymousClass->callPrepareMappedHeaders($requestData, $overrides);
412+
413+
/* ASSERT */
414+
$this->assertArrayHasKey(HttpHeaderType::AUTHORIZATION->value, $headers);
415+
$this->assertSame($authValue, Arr::get($headers, HttpHeaderType::AUTHORIZATION->value));
416+
}
417+
418+
#[Test]
419+
public function it_forwards_authorization_header_for_route_with_auth_middleware(): void
420+
{
421+
/* SETUP */
422+
$token = 'Bearer sample_token';
423+
$acceptValue = 'application/json';
424+
$languageValue = 'en-US';
425+
$headersData = new HeadersData(
426+
authorization: $token,
427+
accept: $acceptValue,
428+
acceptLanguage: $languageValue
429+
);
430+
$requestData = new RequestData(
431+
uri: '/auth-protected-route',
432+
method: Request::METHOD_GET,
433+
headers: $headersData,
434+
routeName: 'auth.protected.route',
435+
);
436+
437+
/* EXECUTE */
438+
$headers = $this->anonymousClass->callPrepareMappedHeaders($requestData);
439+
440+
/* ASSERT */
441+
$this->assertArrayHasKey(HttpHeaderType::AUTHORIZATION->value, $headers);
442+
$this->assertSame($token, $headers[HttpHeaderType::AUTHORIZATION->value]);
443+
$this->assertSame($acceptValue, $headers[HttpHeaderType::ACCEPT->value]);
444+
$this->assertSame($languageValue, $headers[HttpHeaderType::ACCEPT_LANGUAGE->value]);
445+
$this->assertArrayHasKey(config('demon-dextral-horn.defaults.prefetch_header'), $headers);
446+
}
447+
448+
#[Test]
449+
public function it_does_not_forward_authorization_header_for_public_named_route(): void
450+
{
451+
/* SETUP */
452+
$acceptValue = 'application/json';
453+
$languageValue = 'en-US';
454+
$headersData = new HeadersData(
455+
authorization: 'Bearer token',
456+
accept: $acceptValue,
457+
acceptLanguage: $languageValue
458+
);
459+
$requestData = new RequestData(
460+
uri: '/public-route-no-auth',
461+
method: Request::METHOD_GET,
462+
headers: $headersData,
463+
routeName: 'public.route.no.auth',
464+
);
465+
466+
/* EXECUTE */
467+
$headers = $this->anonymousClass->callPrepareMappedHeaders($requestData);
468+
469+
/* ASSERT */
470+
$this->assertArrayNotHasKey(HttpHeaderType::AUTHORIZATION->value, $headers);
471+
$this->assertSame($acceptValue, $headers[HttpHeaderType::ACCEPT->value]);
472+
$this->assertSame($languageValue, $headers[HttpHeaderType::ACCEPT_LANGUAGE->value]);
473+
$this->assertArrayHasKey(config('demon-dextral-horn.defaults.prefetch_header'), $headers);
474+
}
475+
391476
#[Test]
392477
public function it_filters_out_null_header_values(): void
393478
{

0 commit comments

Comments
 (0)