json( $request->user()->oauthApps()->where('revoked', false)->orderByDesc('created_at')->get() ); } /** * Create a new OAuth client (authorization_code+PKCE, client_credentials or password). */ public function store(Request $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 = $request->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); } /** * Update an existing client's name/redirect URIs. */ public function update(Request $request, Client $client) { abort_unless($client->owner_id === $request->user()->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); } /** * Revoke (soft-delete) a client and its tokens. */ public function destroy(Request $request, Client $client) { abort_unless($client->owner_id === $request->user()->id, 404); $client->update(['revoked' => true]); $client->tokens()->update(['revoked' => true]); return response()->noContent(); } }