Vai al contenuto principale

Panoramica

L’OAuth di Bluesky è basato su AT Protocol e differisce sostanzialmente dai provider Socialite tipici come GitHub o Google.
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.

Differenze rispetto al normale OAuth

ElementoSocialite tradizionaleBluesky Socialite
Metodo di autenticazioneOAuth 2.0AT Protocol OAuth (DPoP)
Identificazione del clientclient_id + client_secretChiave privata + URL di client-metadata.json
client_idStringa fissa registrataURL di client-metadata.json
Identificatore dell’utenteEmail, ecc.DID (did:plc:...) o handle
Login hintNon necessarioPuoi passare handle / DID / URL del PDS

Flusso di autenticazione

Installazione e configurazione

Creazione della chiave privata

Prima genera la chiave privata. Puoi farlo senza registrarti su Bluesky.
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.
BLUESKY_OAUTH_PRIVATE_KEY="..."
Con Bluesky non è necessario registrare client_id o client_secret. Puoi usare l’autenticazione OAuth con la sola configurazione della chiave privata.

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.
BLUESKY_OAUTH_SCOPE="atproto account:email include:app.bsky.authViewAll"
Per i dettagli degli scope disponibili consulta la documentazione AT Protocol 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.
# 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.
BLUESKY_REDIRECT=/bluesky/callback

Configurazione delle rotte

Come nome della rotta di callback è consigliato bluesky.oauth.redirect. Il pacchetto usa questo nome internamente.
// 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.
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

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.
MetodoDescrizioneEsempio
did()DID Blueskydid:plc:xxxxxx
handle()Handle Blueskyalice.bsky.social
displayName()Nome visualizzatoAlice
avatar()URL dell’avatarhttps://...
issuer()URL del PDShttps://bsky.social
token()Access token
refresh()Refresh token
Per vedere tutte le proprietà usa toArray().
/** @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.
// 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.
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.
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.
php artisan make:listener OAuthSessionUpdatedListener
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.
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().
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,
        ]);
    }
}
$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.
// 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.
// bootstrap/app.php
->withMiddleware(function (Middleware $middleware) {
    $middleware->redirectGuestsTo('/bluesky/login');
})
Ultima modifica il 13 luglio 2026