> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Socialite - Laravel Bluesky

> Autenticazione OAuth di Laravel Bluesky. Configurazione e uso di Socialite con gli endpoint DPoP e PAR specifici di AT Protocol.

## Panoramica

L'OAuth di Bluesky è basato su AT Protocol e differisce sostanzialmente dai provider Socialite tipici come GitHub o Google.

<Warning>
  L'implementazione dell'OAuth di Bluesky è fondamentalmente diversa da quella degli altri provider Socialite. Usa DPoP (Demonstrated Proof of Possession) e l'endpoint PAR (Pushed Authorization Requests). Non è necessario `client_secret`: si usa invece una chiave privata.
</Warning>

### Differenze rispetto al normale OAuth

| Elemento                   | Socialite tradizionale        | Bluesky Socialite                              |
| -------------------------- | ----------------------------- | ---------------------------------------------- |
| Metodo di autenticazione   | OAuth 2.0                     | AT Protocol OAuth (DPoP)                       |
| Identificazione del client | `client_id` + `client_secret` | Chiave privata + URL di `client-metadata.json` |
| `client_id`                | Stringa fissa registrata      | URL di `client-metadata.json`                  |
| Identificatore dell'utente | Email, ecc.                   | DID (`did:plc:...`) o handle                   |
| Login hint                 | Non necessario                | Puoi passare handle / DID / URL del PDS        |

### Flusso di autenticazione

```mermaid theme={null}
sequenceDiagram
    participant User as Utente
    participant App as App<br>Laravel
    participant Bluesky as Server<br>Bluesky

    User->>App: Invia form di login (inserisce l'handle)
    App->>Bluesky: Richiesta PAR (include login_hint)
    Bluesky-->>App: request_uri
    App->>Bluesky: Redirect all'endpoint di autorizzazione
    User->>Bluesky: Approva il login su Bluesky
    Bluesky-->>App: Callback (code + iss)
    App->>Bluesky: Scambio di token con DPoP
    Bluesky-->>App: Access token + OAuthSession
    App->>User: Login completato
```

## Installazione e configurazione

### Creazione della chiave privata

Prima genera la chiave privata. Puoi farlo senza registrarti su Bluesky.

```bash theme={null}
php artisan bluesky:new-private-key
```

```
Please set this private key in .env

BLUESKY_OAUTH_PRIVATE_KEY="...url-safe base64 encoded key..."
```

Copia il valore emesso in `.env`.

```dotenv theme={null}
BLUESKY_OAUTH_PRIVATE_KEY="..."
```

<Info>
  Con Bluesky non è necessario registrare `client_id` o `client_secret`. Puoi usare l'autenticazione OAuth con la sola configurazione della chiave privata.
</Info>

### Scope OAuth predefiniti

Il pacchetto è configurato con scope OAuth predefiniti che coprono tre casi d'uso principali.

1. **Login con Socialite** — con `atproto`, `account:email` e `include:app.bsky.authViewAll` abilita l'autenticazione utente e l'accesso all'email
2. **Post** — con `include:app.bsky.authCreatePosts` e `blob:*/*` consente la creazione di post e l'upload di immagini/video
3. **Notifiche DM** — con `rpc:chat.bsky.convo.sendMessage` e `rpc:chat.bsky.convo.getConvoForMembers` abilita l'invio di messaggi diretti per le notifiche

Puoi personalizzare gli scope impostando la variabile d'ambiente `BLUESKY_OAUTH_SCOPE`.

```dotenv theme={null}
BLUESKY_OAUTH_SCOPE="atproto account:email include:app.bsky.authViewAll"
```

Per i dettagli degli scope disponibili consulta la [documentazione AT Protocol Permission Requests](https://atproto.com/guides/permission-requests).

### Sviluppo locale

Per impostazione predefinita sono configurati `http://localhost` e `http://127.0.0.1:8000/`, quindi in sviluppo locale non serve nessuna configurazione aggiuntiva.

```dotenv theme={null}
# Imposta solo se hai cambiato la porta
BLUESKY_CLIENT_ID=
BLUESKY_REDIRECT=http://127.0.0.1:8080/
```

### Ambiente di produzione

Se esiste una rotta chiamata `bluesky.oauth.redirect`, non serve configurare `.env`. Configuralo solo se hai cambiato il nome della rotta predefinito.

```dotenv theme={null}
BLUESKY_REDIRECT=/bluesky/callback
```

## Configurazione delle rotte

Come nome della rotta di callback è consigliato `bluesky.oauth.redirect`. Il pacchetto usa questo nome internamente.

```php theme={null}
// routes/web.php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\SocialiteController;

Route::get('login', [SocialiteController::class, 'login'])->name('login');
Route::match(['get', 'post'], 'redirect', [SocialiteController::class, 'redirect']);
Route::get('bluesky/callback', [SocialiteController::class, 'callback'])
     ->name('bluesky.oauth.redirect');
```

### Gestione del callback in sviluppo locale

Durante lo sviluppo locale, l'URL di callback da Bluesky è fisso su `http://127.0.0.1:8000/`. È comodo smistarlo a livello di rotta.

```php theme={null}
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

Route::get('/', function (Request $request) {
    if (app()->isLocal() && $request->has('iss')) {
        return to_route('bluesky.oauth.redirect', $request->query());
    }

    // ...
});
```

## Implementazione del controller

```php theme={null}
<?php

namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\Http\Request;
use Laravel\Socialite\Facades\Socialite;
use Revolution\Bluesky\Session\OAuthSession;

class SocialiteController extends Controller
{
    public function login(Request $request)
    {
        // Pagina con il form per inserire il login hint
        return view('login');
    }

    public function redirect(Request $request)
    {
        // Puoi passare handle, DID o URL del PDS come login_hint (opzionale)
        $hint = $request->input('login_hint');
        $request->session()->put('hint', $hint);

        return Socialite::driver('bluesky')
                        ->hint($hint)
                        ->redirect();
    }

    public function callback(Request $request)
    {
        if ($request->missing('code')) {
            // Per debug in sviluppo. In produzione sostituisci con una gestione errori adeguata
            dd($request);
        }

        $hint = $request->session()->pull('hint');

        /** @var \Laravel\Socialite\Two\User $user */
        $user = Socialite::driver('bluesky')
                         ->hint($hint)
                         ->user();

        /** @var OAuthSession $session */
        $session = $user->session;

        // Salva l'OAuthSession nella sessione Laravel
        $request->session()->put('bluesky_session', $session->toArray());

        $loginUser = User::updateOrCreate([
            'did' => $session->did(),
        ], [
            'iss'           => $session->issuer(),
            'handle'        => $session->handle(),
            'name'          => $session->displayName(),
            'avatar'        => $session->avatar(),
            'access_token'  => $session->token(),
            'refresh_token' => $session->refresh(),
        ]);

        auth()->login($loginUser, true);

        return to_route('bluesky.dashboard');
    }
}
```

## Informazioni utente (OAuthSession)

I metodi principali dell'`OAuthSession` ottenibile da `$user->session` sono i seguenti.

| Metodo          | Descrizione       | Esempio               |
| --------------- | ----------------- | --------------------- |
| `did()`         | DID Bluesky       | `did:plc:xxxxxx`      |
| `handle()`      | Handle Bluesky    | `alice.bsky.social`   |
| `displayName()` | Nome visualizzato | `Alice`               |
| `avatar()`      | URL dell'avatar   | `https://...`         |
| `issuer()`      | URL del PDS       | `https://bsky.social` |
| `token()`       | Access token      |                       |
| `refresh()`     | Refresh token     |                       |

Per vedere tutte le proprietà usa `toArray()`.

```php theme={null}
/** @var OAuthSession $session */
dump($session->toArray());
```

## Configurazione del database

Aggiungi alla tabella `users` le colonne specifiche di Bluesky. Il DID è l'identificatore univoco dell'utente Bluesky.

```php theme={null}
// database/migrations/xxxx_add_bluesky_columns_to_users_table.php

Schema::table('users', function (Blueprint $table) {
    $table->string('did')->nullable()->unique();
    $table->string('iss')->nullable();
    $table->string('handle')->nullable();
    $table->string('avatar')->nullable();
    $table->text('access_token')->nullable();
    $table->text('refresh_token')->nullable();
});
```

## Riutilizzo di OAuthSession

Puoi invocare le API usando l'OAuthSession salvato in sessione.

```php theme={null}
use Revolution\Bluesky\Facades\Bluesky;
use Revolution\Bluesky\Session\OAuthSession;

$session = OAuthSession::create(session('bluesky_session'));

$timeline = Bluesky::withToken($session)->getTimeline();
```

Nei job o nella Console, dove la sessione Laravel non è disponibile, ricostruisci l'OAuthSession dai dati del DB.

```php theme={null}
use Revolution\Bluesky\Facades\Bluesky;
use Revolution\Bluesky\Session\OAuthSession;

$session = OAuthSession::create([
    'did'           => $user->did,
    'refresh_token' => $user->refresh_token,
    // Per gli account non su bsky.social specifica anche iss
    // 'iss'        => $user->iss,
]);

$timeline = Bluesky::withToken($session)
                   ->refreshSession()
                   ->getTimeline();
```

## Aggiornamento automatico del token

Il refresh token può essere usato una sola volta, quindi dopo l'aggiornamento devi risalvarlo obbligatoriamente in DB. Usa l'evento `OAuthSessionUpdated`.

```bash theme={null}
php artisan make:listener OAuthSessionUpdatedListener
```

```php theme={null}
namespace App\Listeners;

use App\Models\User;
use Revolution\Bluesky\Events\OAuthSessionUpdated;

class OAuthSessionUpdatedListener
{
    public function handle(OAuthSessionUpdated $event): void
    {
        if (empty($event->session->did())) {
            return;
        }

        session()->put('bluesky_session', $event->session->toArray());

        User::firstWhere('did', $event->session->did())
            ->fill([
                'iss'           => $event->session->issuer(),
                'handle'        => $event->session->handle(),
                'name'          => $event->session->displayName(),
                'avatar'        => $event->session->avatar(),
                'access_token'  => $event->session->token(),
                'refresh_token' => $event->session->refresh(),
            ])->save();
    }
}
```

All'inizio del refresh viene emesso anche l'evento `OAuthSessionRefreshing`. A quel punto il `refresh_token` diventa invalido, quindi conviene rimuoverlo dal DB per sicurezza.

```php theme={null}
use Revolution\Bluesky\Events\OAuthSessionRefreshing;

public function handle(OAuthSessionRefreshing $event): void
{
    if (empty($event->session->did())) {
        return;
    }

    User::firstWhere('did', $event->session->did())
        ->fill(['refresh_token' => ''])
        ->save();
}
```

## Trait WithBluesky

Aggiungendo il trait `WithBluesky` al model User e implementando `tokenForBluesky()`, puoi ottenere un client autenticato tramite `$user->bluesky()`.

```php theme={null}
namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;
use Revolution\Bluesky\Session\OAuthSession;
use Revolution\Bluesky\Traits\WithBluesky;

class User extends Authenticatable
{
    use WithBluesky;

    protected function tokenForBluesky(): OAuthSession
    {
        return OAuthSession::create([
            'did'           => $this->did,
            'refresh_token' => $this->refresh_token,
            'iss'           => $this->iss,
        ]);
    }
}
```

```php theme={null}
$profile = auth()->user()
                 ->bluesky()
                 ->refreshSession()
                 ->getProfile();
```

## Personalizzazione del client-metadata

Il pacchetto definisce automaticamente le rotte `bluesky.oauth.client-metadata` e `bluesky.oauth.jwks`. Di norma non serve modificare nulla, ma puoi personalizzarle tramite `OAuthConfig`.

```php theme={null}
// AppServiceProvider

use Revolution\Bluesky\Socialite\OAuthConfig;

public function boot(): void
{
    OAuthConfig::clientMetadataUsing(function () {
        return collect(config('bluesky.oauth.metadata'))
            ->merge([
                'client_id'    => route('bluesky.oauth.client-metadata'),
                'jwks_uri'     => route('bluesky.oauth.jwks'),
                'redirect_uris' => [url('bluesky/callback')],
            ])
            ->reject(fn ($item) => is_null($item))
            ->toArray();
    });
}
```

## Comportamento in assenza di autenticazione

Se `OAuthSession` è null o manca il refresh token, viene lanciata un'eccezione `Unauthenticated` e vieni reindirizzato alla rotta `login`.

```php theme={null}
// bootstrap/app.php
->withMiddleware(function (Middleware $middleware) {
    $middleware->redirectGuestsTo('/bluesky/login');
})
```

<Info>
  Source: [docs/socialite.md](https://github.com/invokable/laravel-bluesky/blob/main/docs/socialite.md)
</Info>


## Related topics

- [Laravel Bluesky](/it/packages/laravel-bluesky/index.md)
- [Laravel Socialite (autenticazione social)](/it/socialite.md)
- [Basic client - Laravel Bluesky](/it/packages/laravel-bluesky/basic-client.md)
- [Tutorial per bot - Laravel Bluesky](/it/packages/laravel-bluesky/bot-tutorial.md)
- [Confronto tra metodi di autenticazione - Laravel Bluesky](/it/packages/laravel-bluesky/authentication.md)
