-
Notifications
You must be signed in to change notification settings - Fork 1
Implement Token Based Auth Controller for API token authentication #22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
MichaelJ2324
merged 4 commits into
master
from
copilot/fix-fb06a97d-1d63-465c-951a-34ebb85a8ed6
Oct 8, 2025
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
4e816ff
Initial plan
Copilot 690a28e
Implement Token Based Auth Controller
Copilot a3836d7
Add documentation and remove unused import from AbstractTokenController
Copilot b150865
Support both 'token' and 'api_token' properties for backward compatib…
Copilot File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,101 @@ | ||
| <?php | ||
|
|
||
| namespace MRussell\REST\Auth\Abstracts; | ||
|
|
||
| /** | ||
| * Class AbstractTokenController | ||
| * | ||
| * A simple token-based authentication controller for APIs that use | ||
| * pre-configured API tokens passed via Bearer Authorization header. | ||
| * No authenticate/logout flow required. | ||
| * | ||
| * Usage Example: | ||
| * ```php | ||
| * $auth = new TokenAuthController(); | ||
| * | ||
| * // Use 'token' property (recommended) | ||
| * $auth->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(); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| <?php | ||
|
|
||
| namespace MRussell\REST\Auth; | ||
|
|
||
| use MRussell\REST\Auth\Abstracts\AbstractTokenController; | ||
|
|
||
| class TokenAuthController extends AbstractTokenController {} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,151 @@ | ||
| <?php | ||
|
|
||
| namespace MRussell\REST\Tests\Auth; | ||
|
|
||
| use MRussell\REST\Auth\TokenAuthController; | ||
| use PHPUnit\Framework\TestCase; | ||
|
|
||
| /** | ||
| * Class AbstractTokenControllerTest | ||
| * @package MRussell\REST\Tests\Auth | ||
| * @coversDefaultClass \MRussell\REST\Auth\Abstracts\AbstractTokenController | ||
| * @group AbstractTokenControllerTest | ||
| */ | ||
| class AbstractTokenControllerTest extends TestCase | ||
| { | ||
| public static function setUpBeforeClass(): void | ||
| { | ||
| //Add Setup for static properties here | ||
| } | ||
|
|
||
| public static function tearDownAfterClass(): void | ||
| { | ||
| //Add Tear Down for static properties here | ||
| } | ||
|
|
||
| protected function setUp(): void | ||
| { | ||
| parent::setUp(); | ||
| } | ||
|
|
||
| protected function tearDown(): void | ||
| { | ||
| parent::tearDown(); | ||
| } | ||
|
|
||
| /** | ||
| * @covers ::setCredentials | ||
| * @covers ::isAuthenticated | ||
| */ | ||
| public function testSetCredentials(): void | ||
| { | ||
| $Auth = new TokenAuthController(); | ||
| $this->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); | ||
| } | ||
| } |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.