diff --git a/src/Auth/Abstracts/AbstractTokenController.php b/src/Auth/Abstracts/AbstractTokenController.php new file mode 100644 index 0000000..29a389b --- /dev/null +++ b/src/Auth/Abstracts/AbstractTokenController.php @@ -0,0 +1,101 @@ +setCredentials(['token' => 'your-api-token-here']); + * + * // Or use 'api_token' property (backward compatibility) + * $auth->setCredentials(['api_token' => 'your-api-token-here']); + * + * // The token will be automatically added to requests as: + * // Authorization: Bearer your-api-token-here + * + * // Check if authenticated + * if ($auth->isAuthenticated()) { + * // Token is set and ready to use + * } + * ``` + * + * @package MRussell\REST\Auth\Abstracts + */ +abstract class AbstractTokenController extends AbstractBasicController +{ + public const DEFAULT_AUTH_TYPE = 'Bearer'; + + protected string $authType = self::DEFAULT_AUTH_TYPE; + + /** + * Token-based auth doesn't require authenticate/logout actions + */ + protected static array $_DEFAULT_AUTH_ACTIONS = []; + + /** + * @inheritdoc + */ + public function setCredentials(array $credentials): static + { + parent::setCredentials($credentials); + + // If a token is provided in credentials, set it directly + // Support both 'token' and 'api_token' for backward compatibility + if (isset($credentials['token'])) { + $this->setToken($credentials['token']); + } elseif (isset($credentials['api_token'])) { + $this->setToken($credentials['api_token']); + } + + return $this; + } + + /** + * Token-based auth is authenticated if a token is present + * @inheritdoc + */ + public function isAuthenticated(): bool + { + return !empty($this->token); + } + + /** + * For token-based auth, authentication is always successful if token is set + * No API call needed + * @inheritdoc + */ + public function authenticate(): bool + { + return $this->isAuthenticated(); + } + + /** + * For token-based auth, logout just clears the token + * No API call needed + * @inheritdoc + */ + public function logout(): bool + { + $this->clearToken(); + $this->removeCachedToken(); + return true; + } + + /** + * Get the Value to be set on the Auth Header + * For token auth, just use the token directly + */ + protected function getAuthHeaderValue(): string + { + return $this->authType . " " . $this->getToken(); + } +} diff --git a/src/Auth/TokenAuthController.php b/src/Auth/TokenAuthController.php new file mode 100644 index 0000000..5f9c6a0 --- /dev/null +++ b/src/Auth/TokenAuthController.php @@ -0,0 +1,7 @@ +assertEquals(false, $Auth->isAuthenticated()); + + $Auth->setCredentials(['token' => 'my-api-token-12345']); + $this->assertEquals(true, $Auth->isAuthenticated()); + $this->assertEquals('my-api-token-12345', $Auth->getToken()); + } + + /** + * @covers ::setCredentials + * @covers ::isAuthenticated + */ + public function testSetCredentialsWithApiToken(): void + { + $Auth = new TokenAuthController(); + $this->assertEquals(false, $Auth->isAuthenticated()); + + // Test backward compatibility with 'api_token' property + $Auth->setCredentials(['api_token' => 'my-api-token-67890']); + $this->assertEquals(true, $Auth->isAuthenticated()); + $this->assertEquals('my-api-token-67890', $Auth->getToken()); + } + + /** + * @covers ::setCredentials + * @covers ::isAuthenticated + */ + public function testSetCredentialsTokenTakesPrecedence(): void + { + $Auth = new TokenAuthController(); + + // If both 'token' and 'api_token' are provided, 'token' should take precedence + $Auth->setCredentials([ + 'token' => 'token-value', + 'api_token' => 'api-token-value' + ]); + $this->assertEquals(true, $Auth->isAuthenticated()); + $this->assertEquals('token-value', $Auth->getToken()); + } + + /** + * @covers ::authenticate + */ + public function testAuthenticate(): void + { + $Auth = new TokenAuthController(); + + // Authentication should fail without token + $this->assertEquals(false, $Auth->authenticate()); + + // Authentication should succeed with token + $Auth->setCredentials(['token' => 'my-api-token-12345']); + $this->assertEquals(true, $Auth->authenticate()); + } + + /** + * @covers ::logout + * @covers ::isAuthenticated + */ + public function testLogout(): void + { + $Auth = new TokenAuthController(); + $Auth->setCredentials(['token' => 'my-api-token-12345']); + + $this->assertEquals(true, $Auth->isAuthenticated()); + $this->assertEquals(true, $Auth->logout()); + $this->assertEquals(false, $Auth->isAuthenticated()); + } + + /** + * @covers ::__construct + */ + public function testNoAuthActions(): void + { + $Auth = new TokenAuthController(); + + // Token auth should not have authenticate/logout actions + $actions = $Auth->getActions(); + $this->assertEmpty($actions); + } + + /** + * Test setting token directly + */ + public function testSetToken(): void + { + $Auth = new TokenAuthController(); + $Auth->setToken('direct-token-67890'); + + $this->assertEquals(true, $Auth->isAuthenticated()); + $this->assertEquals('direct-token-67890', $Auth->getToken()); + } + + /** + * @covers ::getAuthHeaderValue + */ + public function testAuthHeaderValue(): void + { + $Auth = new TokenAuthController(); + $Auth->setCredentials(['token' => 'my-api-token-12345']); + + // Use reflection to test protected method + $class = new \ReflectionClass($Auth); + $method = $class->getMethod('getAuthHeaderValue'); + $method->setAccessible(true); + + $headerValue = $method->invoke($Auth); + $this->assertEquals('Bearer my-api-token-12345', $headerValue); + } +}