Skip to main content

Overview

Bluesky’s OAuth is based on the AT Protocol and differs significantly from standard Socialite providers such as GitHub or Google.
Bluesky OAuth is fundamentally different from other Socialite providers. It uses DPoP (Demonstrated Proof of Possession) and PAR (Pushed Authorization Requests) endpoints. No client_secret is needed — you use a private key instead.

Differences from standard OAuth

Authentication flow

Installation and configuration

Create a private key

Generate a private key. No Bluesky account registration is required.
Copy the output into your .env file.
For Bluesky, you don’t need to register a client_id or client_secret. Setting the private key is the only required step.

Default OAuth scopes

The package is configured with default OAuth scopes to support three main use cases:
  1. Socialite Loginatproto, account:email, and include:app.bsky.authViewAll enable user authentication and email access
  2. Postinginclude:app.bsky.authCreatePosts and blob:*/* allow creating posts and uploading images/videos
  3. DM Notificationsrpc:chat.bsky.convo.sendMessage and rpc:chat.bsky.convo.getConvoForMembers enable sending direct messages for notifications
You can customize the scopes by setting the BLUESKY_OAUTH_SCOPE environment variable:
For more information about available scopes, see the AT Protocol Permission Requests documentation.

Local development

By default, http://localhost and http://127.0.0.1:8000/ are preconfigured, so no additional .env settings are needed for local development.

Production

If the route named bluesky.oauth.redirect exists, no .env configuration is needed. Set it only if you have changed the default route name.

Route setup

Use bluesky.oauth.redirect as the callback route name — the package uses this name internally.

Handling callbacks in local development

During local development, the callback URL returned by Bluesky is fixed to http://127.0.0.1:8000/. Redirect it at the route level.

Controller implementation

User information (OAuthSession)

Key methods available on the OAuthSession retrieved from $user->session. Use toArray() to inspect all properties.

Database setup

Add Bluesky-specific columns to the users table. The DID is the unique identifier for a Bluesky user.

Reusing the OAuthSession

Use the OAuthSession stored in the session to call APIs.
In Jobs or Console commands where the Laravel session is unavailable, build an OAuthSession from the database.

Automatic token refresh

Refresh tokens can only be used once, so you must save the updated token after each refresh. Use the OAuthSessionUpdated event.
The OAuthSessionRefreshing event fires when a refresh starts. Clear the refresh token from the database here, since it becomes invalid at this point.

WithBluesky trait

Add the WithBluesky trait to your User model and implement tokenForBluesky() to get an authenticated Bluesky client via $user->bluesky().

Customizing client-metadata

The package automatically registers bluesky.oauth.client-metadata and bluesky.oauth.jwks routes. Changes are rarely needed, but you can customize them with OAuthConfig.

Unauthenticated behavior

If OAuthSession is null or has no refresh token, an Unauthenticated exception is thrown and the user is redirected to the login route.
Last modified on April 29, 2026