diff --git a/app/Http/Controllers/IndieAuthController.php b/app/Http/Controllers/IndieAuthController.php index bff8ebee..a795bce8 100644 --- a/app/Http/Controllers/IndieAuthController.php +++ b/app/Http/Controllers/IndieAuthController.php @@ -188,7 +188,7 @@ class IndieAuthController extends Controller */ public function processRevocationRequest(Request $request): JsonResponse { - MicropubToken::findActive($request->get('token', ''))?->revoke(); + MicropubToken::findActive($request->get('token'))?->revoke(); return response()->json([], 200); } @@ -205,11 +205,11 @@ class IndieAuthController extends Controller */ public function processIntrospectionRequest(Request $request): JsonResponse { - if (! MicropubToken::findActive((string) $request->bearerToken())) { + if (! MicropubToken::findActive($request->bearerToken())) { return response()->json([], 401); } - $token = MicropubToken::findActive((string) $request->get('token', '')); + $token = MicropubToken::findActive($request->get('token')); if (! $token) { return response()->json(['active' => false]); diff --git a/app/Http/Controllers/MicropubMediaController.php b/app/Http/Controllers/MicropubMediaController.php index da7c7dc2..d9f8ea32 100644 --- a/app/Http/Controllers/MicropubMediaController.php +++ b/app/Http/Controllers/MicropubMediaController.php @@ -26,9 +26,7 @@ class MicropubMediaController extends Controller $tokenData = $request->input('token_data'); $scopes = $tokenData['scope']; - if (is_string($scopes)) { - $scopes = explode(' ', $scopes); - } + $scopes = explode(' ', $scopes); if (! in_array('create', $scopes, true)) { return (new MicropubResponses)->insufficientScopeResponse(); } @@ -84,9 +82,7 @@ class MicropubMediaController extends Controller $tokenData = $request->input('token_data'); $scopes = $tokenData['scope']; - if (is_string($scopes)) { - $scopes = explode(' ', $scopes); - } + $scopes = explode(' ', $scopes); if (! in_array('create', $scopes, true)) { return (new MicropubResponses)->insufficientScopeResponse(); } diff --git a/app/Http/Middleware/LinkHeadersMiddleware.php b/app/Http/Middleware/LinkHeadersMiddleware.php index 0a280d44..e2810f87 100644 --- a/app/Http/Middleware/LinkHeadersMiddleware.php +++ b/app/Http/Middleware/LinkHeadersMiddleware.php @@ -18,6 +18,7 @@ class LinkHeadersMiddleware $response->header('Link', '<'.route('indieauth.start').'>; rel="authorization_endpoint"', false); $response->header('Link', '<'.route('indieauth.token').'>; rel="token_endpoint"', false); $response->header('Link', '<'.route('indieauth.revocation').'>; rel="revocation_endpoint"', false); + $response->header('Link', '<'.route('indieauth.introspection').'>; rel="introspection_endpoint"', false); $response->header('Link', '<'.route('micropub-endpoint').'>; rel="micropub"', false); $response->header('Link', '<'.route('webmention-endpoint').'>; rel="webmention"', false); diff --git a/app/Models/MicropubToken.php b/app/Models/MicropubToken.php index e85cb206..c4df41bc 100644 --- a/app/Models/MicropubToken.php +++ b/app/Models/MicropubToken.php @@ -5,11 +5,9 @@ declare(strict_types=1); namespace App\Models; use Illuminate\Database\Eloquent\Attributes\Fillable; -use Illuminate\Database\Eloquent\Attributes\Table; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; -#[Table('micropub_tokens')] #[Fillable(['token_hash', 'client_id', 'me', 'scope'])] class MicropubToken extends Model { @@ -26,11 +24,15 @@ class MicropubToken extends Model } /** - * Find the active (non-revoked) token matching a raw bearer token string. + * Find the active (non-revoked) token matching a raw bearer token value. + * + * Accepts mixed because callers pass request input directly, which PHP + * lets be an array (e.g. a client sending token[]=a) - casting that to + * string would throw, so anything non-string is just treated as absent. */ - public static function findActive(string $rawToken): ?self + public static function findActive(mixed $rawToken): ?self { - if ($rawToken === '') { + if (! is_string($rawToken) || $rawToken === '') { return null; } diff --git a/app/Services/Micropub/Handlers/CardHandler.php b/app/Services/Micropub/Handlers/CardHandler.php index 02e3a066..6b24f21b 100644 --- a/app/Services/Micropub/Handlers/CardHandler.php +++ b/app/Services/Micropub/Handlers/CardHandler.php @@ -24,9 +24,7 @@ class CardHandler implements MicropubHandlerInterface assert($data instanceof CardData); $scopes = $data->tokenData['scope']; - if (is_string($scopes)) { - $scopes = explode(' ', $scopes); - } + $scopes = explode(' ', $scopes); if (! in_array('create', $scopes, true)) { throw new InvalidTokenScopeException; diff --git a/app/Services/Micropub/Handlers/EntryHandler.php b/app/Services/Micropub/Handlers/EntryHandler.php index ef9740f2..48bbb550 100644 --- a/app/Services/Micropub/Handlers/EntryHandler.php +++ b/app/Services/Micropub/Handlers/EntryHandler.php @@ -27,9 +27,7 @@ class EntryHandler implements MicropubHandlerInterface assert($data instanceof EntryData); $scopes = $data->tokenData['scope']; - if (is_string($scopes)) { - $scopes = explode(' ', $scopes); - } + $scopes = explode(' ', $scopes); if (! in_array('create', $scopes, true)) { throw new InvalidTokenScopeException; diff --git a/app/Services/Micropub/Handlers/UpdateHandler.php b/app/Services/Micropub/Handlers/UpdateHandler.php index 49f86063..136a0840 100644 --- a/app/Services/Micropub/Handlers/UpdateHandler.php +++ b/app/Services/Micropub/Handlers/UpdateHandler.php @@ -30,9 +30,7 @@ class UpdateHandler implements MicropubHandlerInterface assert($data instanceof UpdateData); $scopes = $data->tokenData['scope']; - if (is_string($scopes)) { - $scopes = explode(' ', $scopes); - } + $scopes = explode(' ', $scopes); if (! in_array('update', $scopes, true)) { throw new InvalidTokenScopeException; diff --git a/bootstrap/app.php b/bootstrap/app.php index 9c73bdb9..e29a3598 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -17,8 +17,10 @@ return Application::configure(basePath: dirname(__DIR__)) ->append(LinkHeadersMiddleware::class) ->preventRequestForgery( except: [ - 'auth', // This is the IndieAuth auth endpoint - 'token', // This is the IndieAuth token endpoint + 'auth', // This is the IndieAuth auth endpoint + 'token', // This is the IndieAuth token endpoint + 'revocation', // This is the IndieAuth revocation endpoint + 'introspect', // This is the IndieAuth introspection endpoint 'api/post', 'api/media', 'micropub/places', diff --git a/database/migrations/2026_08_13_120924_create_micropub_tokens_table.php b/database/migrations/2026_08_13_120924_create_micropub_tokens_table.php index cb37f28b..e336912f 100644 --- a/database/migrations/2026_08_13_120924_create_micropub_tokens_table.php +++ b/database/migrations/2026_08_13_120924_create_micropub_tokens_table.php @@ -12,7 +12,7 @@ return new class extends Migration { Schema::create('micropub_tokens', function (Blueprint $table) { $table->id(); - $table->string('token_hash')->unique(); + $table->string('token_hash', 64)->unique(); $table->string('client_id'); $table->string('me'); $table->string('scope'); diff --git a/resources/views/admin/tokens/index.blade.php b/resources/views/admin/tokens/index.blade.php index cb7edda9..8836ab07 100644 --- a/resources/views/admin/tokens/index.blade.php +++ b/resources/views/admin/tokens/index.blade.php @@ -14,7 +14,7 @@ @if($token->isRevoked) — revoked {{ $token->revoked_at->diffForHumans() }} @else -