shouldReceive('getMiddlewareStack') ->andReturn([$class]); $handler ->shouldReceive('handle') ->once() ->andReturn(new Response(200)); $request = new ServerRequest('GET', '/'); $middleware = new JwtMiddleware(); $response = $middleware->process($request, $handler); $this->assertEquals(CodesEnum::OK->value, $response->getStatusCode()); } /** * @throws \JsonException */ public function testIgnoresJwtAttributeButNoToken() { $class = $this->getClass(); $handler = \Mockery::mock(Dispatcher::class); $handler->shouldReceive('getMiddlewareStack') ->andReturn([$class]); $request = new ServerRequest('GET', '/'); $middleware = new JwtMiddleware(); $response = $middleware->process($request, $handler); $this->assertEquals(CodesEnum::UNAUTHORIZED->value, $response->getStatusCode()); } /** * @throws \JsonException */ public function testInvalidToken() { $class = $this->getClass(); $handler = \Mockery::mock(Dispatcher::class); $handler->shouldReceive('getMiddlewareStack') ->andReturn([$class]); $request = new ServerRequest('GET', '/'); $request = $request->withHeader('Authorization', 'Bearer ' . 'invalid_token_string'); $middleware = new JwtMiddleware(); $response = $middleware->process($request, $handler); $this->assertEquals(CodesEnum::UNAUTHORIZED->value, $response->getStatusCode()); $this->assertStringContainsString( 'Unauthorized: Invalid token', $response->getBody()->getContents() ); } /** * @throws \JsonException */ public function testJwtAttributeWithTokenButWrongAud() { $class = $this->getClass(); $handler = \Mockery::mock(Dispatcher::class); $handler->shouldReceive('getMiddlewareStack') ->andReturn([$class]); $request = new ServerRequest('GET', '/'); $request = $request->withHeader('Authorization', 'Bearer ' . $this->getJwt()); $middleware = new JwtMiddleware(); $response = $middleware->process($request, $handler); $this->assertEquals(CodesEnum::UNAUTHORIZED->value, $response->getStatusCode()); $this->assertStringContainsString( 'The token is not allowed to be used by this audience', $response->getBody()->getContents() ); } /** * @throws \JsonException */ public function testJwtAttributeWithTokenButWrongIss() { Config::set('jwt.audience', 'https://client-app.io'); $class = $this->getClass(); $handler = \Mockery::mock(Dispatcher::class); $handler->shouldReceive('getMiddlewareStack') ->andReturn([$class]); $request = new ServerRequest('GET', '/'); $request = $request->withHeader('Authorization', 'Bearer ' . $this->getJwt()); $middleware = new JwtMiddleware(); $response = $middleware->process($request, $handler); $this->assertEquals(CodesEnum::UNAUTHORIZED->value, $response->getStatusCode()); $this->assertStringContainsString( 'The token was not issued by the given issuers', $response->getBody()->getContents() ); } /** * @throws \JsonException */ public function testJwtAttributeWithTokenWithDiffIssuer() { Config::set('jwt.audience', 'https://client-app.io'); Config::set('jwt.issuer', 'https://different-issuer.io'); $class = $this->getClass(); $handler = \Mockery::mock(Dispatcher::class); $handler->shouldReceive('getMiddlewareStack') ->andReturn([$class]); $request = new ServerRequest('GET', '/'); $request = $request->withHeader('Authorization', 'Bearer ' . $this->getJwt()); $middleware = new JwtMiddleware(); $response = $middleware->process($request, $handler); $this->assertEquals(CodesEnum::UNAUTHORIZED->value, $response->getStatusCode()); $this->assertStringContainsString( 'The token was not issued by the given issuers', $response->getBody()->getContents() ); } public function testJwtAttributeWithToken() { Config::set('jwt.audience', 'https://client-app.io'); Config::set('jwt.issuer', 'https://api.my-awesome-app.io'); $class = $this->getClass(); $handler = \Mockery::mock(Dispatcher::class); $handler->shouldReceive('getMiddlewareStack') ->andReturn([$class]); $handler ->shouldReceive('handle') ->once() ->andReturn(new Response(200)); $request = new ServerRequest('GET', '/'); $request = $request->withHeader('Authorization', 'Bearer ' . $this->getJwt()); $middleware = new JwtMiddleware(); $response = $middleware->process($request, $handler); $this->assertEquals(CodesEnum::OK->value, $response->getStatusCode()); } /** * @throws \JsonException */ public function testJwtFromJwkEndpoint() { Config::set('jwt.audience', 'https://client-app.io'); Config::set('jwt.issuer', 'https://api.my-awesome-app.io'); Redis::partialMock()->shouldReceive('get')->andReturn(null); Redis::shouldReceive('set')->andReturn('OK'); Guzzle::partialMock()->shouldReceive('get') ->with('https://test.com/.well-known/openid-configuration') ->andReturn(new Response(200, [], json_encode([ 'jwks_uri' => 'https://test.com/keys' ], JSON_THROW_ON_ERROR))); Guzzle::shouldReceive('get') ->with('https://test.com/keys') ->andReturn(new Response(200, [], self::TEST_JWKS_JSON)); Config::set('jwt.signing_key', 'https://test.com/.well-known/openid-configuration'); $class = $this->getClass(); $handler = \Mockery::mock(Dispatcher::class); $handler->shouldReceive('getMiddlewareStack') ->andReturn([$class]); $handler ->shouldReceive('handle') ->once() ->andReturn(new Response(200)); $request = new ServerRequest('GET', '/'); $request = $request->withHeader('Authorization', 'Bearer ' . $this->getJwtRsa()); $middleware = new JwtMiddleware(); $response = $middleware->process($request, $handler); $this->assertEquals(CodesEnum::OK->value, $response->getStatusCode()); } /** * @throws \JsonException */ public function testCatchesInvalidJwksUrl() { Config::set('jwt.signing_key', 'https://test.com/.well-known/openid-configuration'); Redis::partialMock()->shouldReceive('get')->andReturn(null); Redis::shouldReceive('set')->andReturn('OK'); Guzzle::partialMock()->shouldReceive('get') ->with('https://test.com/.well-known/openid-configuration') ->andReturn(new Response(200, [], json_encode([], JSON_THROW_ON_ERROR))); $class = $this->getClass(); $handler = \Mockery::mock(Dispatcher::class); $handler->shouldReceive('getMiddlewareStack') ->andReturn([$class]); $request = new ServerRequest('GET', '/'); $request = $request->withHeader('Authorization', 'Bearer ' . $this->getJwtRsa()); $middleware = new JwtMiddleware(); $response = $middleware->process($request, $handler); $this->assertEquals(CodesEnum::INTERNAL_SERVER_ERROR->value, $response->getStatusCode()); } private function getJwtRsa(): string { $key = InMemory::plainText(self::TEST_RSA_PRIVATE_KEY); $signer = new \Lcobucci\JWT\Signer\Rsa\Sha256(); $token = new JwtFacade()->issue( $signer, $key, static fn( Builder $builder, DateTimeImmutable $issuedAt ): Builder => $builder ->issuedBy('https://api.my-awesome-app.io') ->permittedFor('https://client-app.io') ->expiresAt($issuedAt->modify('+10 minutes')) ); return $token->toString(); } private function getJwt(): string { $key = InMemory::plainText(self::TEST_SIGNING_KEY); $signer = new Sha256(); $token = new JwtFacade()->issue( $signer, $key, static fn( Builder $builder, DateTimeImmutable $issuedAt ): Builder => $builder ->issuedBy('https://api.my-awesome-app.io') ->permittedFor('https://client-app.io') ->expiresAt($issuedAt->modify('+10 minutes')) ); return $token->toString(); } }