['account:read']]], tags: ['Account'], responses: [ new OA\Response(response: 200, description: 'Listado de clientes', content: new OA\JsonContent(type: 'array', items: new OA\Items(ref: '#/components/schemas/Client'))), new OA\Response(response: 422, description: 'El token no tiene un usuario asociado (ej. client_credentials)'), ] )] public function index(Request $request) { return response()->json( $this->requireUser($request)->oauthApps()->where('revoked', false)->orderByDesc('created_at')->get() ); } #[OA\Post( path: '/clients', summary: 'Registrar un nuevo cliente OAuth (tu app)', security: [['oauth2' => ['account:write']]], tags: ['Account'], requestBody: new OA\RequestBody( required: true, content: new OA\JsonContent( required: ['name', 'grant_type'], properties: [ new OA\Property(property: 'name', type: 'string'), new OA\Property(property: 'grant_type', type: 'string', enum: ['authorization_code', 'client_credentials', 'password']), new OA\Property(property: 'redirect_uris', type: 'array', items: new OA\Items(type: 'string')), new OA\Property(property: 'confidential', type: 'boolean', default: true), ] ) ), responses: [ new OA\Response(response: 201, description: 'Cliente creado (incluye plain_secret una sola vez)'), new OA\Response(response: 422, description: 'Datos inválidos o token sin usuario asociado'), ] )] public function store(Request $request) { $user = $this->requireUser($request); $data = $request->validate([ 'name' => ['required', 'string', 'max:255'], 'grant_type' => ['required', Rule::in(['authorization_code', 'client_credentials', 'password'])], 'redirect_uris' => ['required_if:grant_type,authorization_code', 'array'], 'redirect_uris.*' => ['url'], 'confidential' => ['boolean'], ]); $confidential = $request->boolean('confidential', true); $grantTypes = match ($data['grant_type']) { 'authorization_code' => ['authorization_code', 'refresh_token'], 'password' => ['password', 'refresh_token'], 'client_credentials' => ['client_credentials'], }; // client_credentials always needs a secret: there is no user present to prove identity. if ($data['grant_type'] === 'client_credentials') { $confidential = true; } $client = $user->oauthApps()->create([ 'name' => $data['name'], 'secret' => $confidential ? Str::random(40) : null, 'provider' => null, 'redirect_uris' => $data['redirect_uris'] ?? [], 'grant_types' => $grantTypes, 'revoked' => false, ]); return response()->json([ 'client' => $client, 'plain_secret' => $client->plainSecret, ], 201); } #[OA\Put( path: '/clients/{client}', summary: 'Editar uno de tus clientes OAuth', security: [['oauth2' => ['account:write']]], tags: ['Account'], parameters: [new OA\Parameter(name: 'client', in: 'path', required: true, schema: new OA\Schema(type: 'string'))], responses: [ new OA\Response(response: 200, description: 'Cliente actualizado', content: new OA\JsonContent(ref: '#/components/schemas/Client')), new OA\Response(response: 404, description: 'No es tuyo o no existe'), ] )] public function update(Request $request, Client $client) { abort_unless($client->owner_id === $this->requireUser($request)->id, 404); $data = $request->validate([ 'name' => ['required', 'string', 'max:255'], 'redirect_uris' => ['array'], 'redirect_uris.*' => ['url'], ]); $client->update([ 'name' => $data['name'], 'redirect_uris' => $data['redirect_uris'] ?? $client->redirect_uris, ]); return response()->json($client); } #[OA\Delete( path: '/clients/{client}', summary: 'Revocar uno de tus clientes OAuth (y sus tokens)', security: [['oauth2' => ['account:write']]], tags: ['Account'], parameters: [new OA\Parameter(name: 'client', in: 'path', required: true, schema: new OA\Schema(type: 'string'))], responses: [ new OA\Response(response: 204, description: 'Revocado'), new OA\Response(response: 404, description: 'No es tuyo o no existe'), ] )] public function destroy(Request $request, Client $client) { abort_unless($client->owner_id === $this->requireUser($request)->id, 404); $client->update(['revoked' => true]); $client->tokens()->update(['revoked' => true]); return response()->noContent(); } private function requireUser(Request $request): User { return $request->user('api') ?? abort(422, __('messages.self_service_requires_user')); } }