403Webshell
Server IP : 35.80.110.71  /  Your IP : 216.73.216.221
Web Server : Apache/2.4.58 (Ubuntu)
System : Linux ip-172-31-21-44 6.17.0-1019-aws #19~24.04.1-Ubuntu SMP Tue Jun 23 18:53:06 UTC 2026 x86_64
User : ubuntu ( 1000)
PHP Version : 8.3.31
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : ON  |  Pkexec : OFF
Directory :  /var/www/switchyard/current/app/Services/ClickUp/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /var/www/switchyard/current/app/Services/ClickUp/ClickUpOAuthClient.php
<?php

namespace App\Services\ClickUp;

use App\Models\ClickUpConnection;
use App\Models\Workspace;
use Illuminate\Http\Client\PendingRequest;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Str;
use RuntimeException;

/**
 * ClickUp OAuth v2 flow.
 *
 * Authorize URL: https://app.clickup.com/api?client_id=…&redirect_uri=…&state=…
 * Token URL:     POST https://api.clickup.com/api/v2/oauth/token
 *
 * ClickUp's OAuth v2 does NOT support PKCE and does NOT return a refresh token
 * or expiry on the token response — tokens are long-lived until the user
 * revokes them in their ClickUp account. The state-in-session check is the
 * CSRF defense; ensureFresh() is a no-op until that changes.
 */
class ClickUpOAuthClient
{
    private const SESSION_PREFIX = 'clickup.oauth.';
    private const STATE_TTL_SECONDS = 600;

    public function authorizeUrl(Workspace $workspace): string
    {
        $state = Str::random(40);

        Session::put(self::SESSION_PREFIX.$state, [
            'workspace_id' => $workspace->id,
            'created_at' => now()->timestamp,
        ]);

        return config('switchyard.clickup.authorize_base').'?'.http_build_query([
            'client_id' => $this->clientId(),
            'redirect_uri' => $this->redirectUri(),
            'state' => $state,
        ]);
    }

    /**
     * @return array{workspace_id: string}
     */
    public function consumeState(string $state): array
    {
        $payload = Session::pull(self::SESSION_PREFIX.$state);

        if (! is_array($payload) || ! isset($payload['workspace_id'])) {
            throw new RuntimeException('Unknown or expired OAuth state.');
        }

        if (isset($payload['created_at']) && (now()->timestamp - $payload['created_at']) > self::STATE_TTL_SECONDS) {
            throw new RuntimeException('Expired OAuth state.');
        }

        return ['workspace_id' => $payload['workspace_id']];
    }

    public function exchangeCode(string $code): string
    {
        $response = $this->http()->asForm()->post(
            config('switchyard.clickup.api_base').'/oauth/token',
            [
                'client_id' => $this->clientId(),
                'client_secret' => $this->clientSecret(),
                'code' => $code,
            ],
        );

        if (! $response->ok()) {
            Log::warning('ClickUp OAuth token exchange failed', [
                'status' => $response->status(),
                'body' => $response->body(),
            ]);

            throw new RuntimeException('ClickUp rejected the OAuth code (status '.$response->status().').');
        }

        $token = (string) $response->json('access_token');

        if ($token === '') {
            throw new RuntimeException('ClickUp returned an empty access_token.');
        }

        return $token;
    }

    /**
     * Fetch the teams (orgs) the access token can see. The first team is the
     * canonical `team_id` for the connection — most installs only have one.
     *
     * @return array{teams: array<int, array{id: string, name: string}>}
     */
    public function fetchAuthorizedTeams(string $accessToken): array
    {
        $response = $this->http()
            ->withHeaders(['Authorization' => $accessToken])
            ->get(config('switchyard.clickup.api_base').'/team');

        if (! $response->ok()) {
            throw new RuntimeException('Could not list ClickUp teams (status '.$response->status().').');
        }

        return [
            'teams' => array_map(
                fn (array $t) => ['id' => (string) $t['id'], 'name' => (string) $t['name']],
                (array) $response->json('teams', []),
            ),
        ];
    }

    public function persistTokens(string $workspaceId, string $accessToken, ?string $teamId = null): ClickUpConnection
    {
        $connection = ClickUpConnection::firstOrNew(['workspace_id' => $workspaceId]);
        $connection->access_token = $accessToken;
        $connection->team_id = $teamId ?? $connection->team_id ?? 'pending';
        $connection->connected_at = now();
        $connection->disconnected_at = null;
        $connection->last_sync_error = null;
        $connection->save();

        return $connection;
    }

    /**
     * No-op for ClickUp v2 — tokens don't expire. Kept on the surface so a
     * future ClickUp v3 (or another provider) drops in without touching
     * callers.
     */
    public function ensureFresh(ClickUpConnection $connection): ClickUpConnection
    {
        return $connection;
    }

    private function http(): PendingRequest
    {
        return Http::acceptJson()->timeout(15);
    }

    private function clientId(): string
    {
        $value = (string) config('switchyard.clickup.client_id');
        if ($value === '') {
            throw new RuntimeException('CLICKUP_CLIENT_ID is not set.');
        }

        return $value;
    }

    private function clientSecret(): string
    {
        $value = (string) config('switchyard.clickup.client_secret');
        if ($value === '') {
            throw new RuntimeException('CLICKUP_CLIENT_SECRET is not set.');
        }

        return $value;
    }

    private function redirectUri(): string
    {
        $value = (string) config('switchyard.clickup.redirect_uri');
        if ($value === '') {
            throw new RuntimeException('CLICKUP_REDIRECT_URI is not set.');
        }

        return $value;
    }
}

Youez - 2016 - github.com/yon3zu
LinuXploit