mirror of
https://github.com/HolgerHatGarKeineNode/einundzwanzig-app.git
synced 2026-06-11 02:50:29 +00:00
feat(settings): API token management UI for users
Adds a "API Tokens" settings page so an authenticated user can create and revoke Sanctum personal access tokens for the new authenticated write endpoints — using the official Sanctum API ($user->createToken() / tokens()). - New Volt component settings/api-tokens (create token, one-time plain-text reveal with copy-to-clipboard, list + revoke own tokens). - Registered route settings.api-tokens (country-prefixed, auth group) and added a nav entry in the settings layout. - SEO definition for the new page. - Pest feature tests (create/reveal-once, validation, revoke, ownership scoping) and a Pest browser screenshot test.
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
it('shows the api token management UI and the one-time token reveal', function () {
|
||||
$user = actingAsUser(['name' => 'Lecturer Demo', 'is_lecturer' => true]);
|
||||
|
||||
// Pre-existing token so the "Aktive Tokens" table is populated.
|
||||
$user->createToken('Mein Laptop');
|
||||
|
||||
$page = visit('/de/settings/api-tokens');
|
||||
|
||||
$page->assertSee('API Tokens')
|
||||
->fill('name', 'Externer Kurs-Sync')
|
||||
->click('Token erstellen')
|
||||
->wait(1)
|
||||
->assertSee('Dein neues API Token')
|
||||
->assertSee('Aktive Tokens')
|
||||
->assertNoJavaScriptErrors()
|
||||
->screenshot(filename: 'settings-api-tokens');
|
||||
});
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
use App\Models\User;
|
||||
use Livewire\Livewire;
|
||||
|
||||
it('mounts the api tokens page when authenticated', function () {
|
||||
actingAsUser();
|
||||
|
||||
Livewire::test('settings.api-tokens')->assertStatus(200);
|
||||
});
|
||||
|
||||
it('creates a personal access token and reveals it once', function () {
|
||||
$user = actingAsUser();
|
||||
|
||||
Livewire::test('settings.api-tokens')
|
||||
->set('name', 'Externer Kurs-Sync')
|
||||
->call('createToken')
|
||||
->assertHasNoErrors()
|
||||
->assertDispatched('token-created')
|
||||
->assertSet('name', '')
|
||||
->assertSet('plainTextToken', fn ($token) => is_string($token) && str_contains($token, '|'));
|
||||
|
||||
expect($user->tokens()->where('name', 'Externer Kurs-Sync')->exists())->toBeTrue();
|
||||
});
|
||||
|
||||
it('requires a token name', function () {
|
||||
actingAsUser();
|
||||
|
||||
Livewire::test('settings.api-tokens')
|
||||
->set('name', '')
|
||||
->call('createToken')
|
||||
->assertHasErrors(['name' => 'required']);
|
||||
});
|
||||
|
||||
it('revokes a token', function () {
|
||||
$user = actingAsUser();
|
||||
$token = $user->createToken('to-be-revoked')->accessToken;
|
||||
|
||||
Livewire::test('settings.api-tokens')
|
||||
->call('deleteToken', $token->id)
|
||||
->assertDispatched('token-deleted');
|
||||
|
||||
expect($user->tokens()->whereKey($token->id)->exists())->toBeFalse();
|
||||
});
|
||||
|
||||
it('only lists the authenticated user\'s own tokens', function () {
|
||||
$user = actingAsUser();
|
||||
$user->createToken('mine');
|
||||
|
||||
$other = User::factory()->create();
|
||||
$other->createToken('theirs');
|
||||
|
||||
Livewire::test('settings.api-tokens')
|
||||
->assertViewHas('tokens', fn ($tokens) => $tokens->count() === 1 && $tokens->first()->name === 'mine');
|
||||
});
|
||||
|
||||
it('cannot revoke a token belonging to another user', function () {
|
||||
actingAsUser();
|
||||
$other = User::factory()->create();
|
||||
$foreignToken = $other->createToken('theirs')->accessToken;
|
||||
|
||||
Livewire::test('settings.api-tokens')
|
||||
->call('deleteToken', $foreignToken->id);
|
||||
|
||||
expect($other->tokens()->whereKey($foreignToken->id)->exists())->toBeTrue();
|
||||
});
|
||||
Reference in New Issue
Block a user