Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Discogs Laravel Package

socialiteproviders/discogs

View on GitHub
Deep Wiki
Context7

Getting Started

What it is: an OAuth 1.0a Socialite driver for Discogs. Discogs does not do OAuth 2.0 or OpenID Connect, so this package is not a drop-in Socialite::driver() swap — it plugs into socialiteproviders/manager and gives you a two-step token exchange. That single fact drives almost every design decision below.

Install and wire up (5 steps):

composer require socialiteproviders/discogs
  1. Create an app at https://www.discogs.com/settings/developers and set the callback URL. Discogs gives you one callback URL per app, so plan dev/staging/prod as separate Discogs apps.
  2. Add credentials to config/services.php:
'discogs' => [
    'client_id'     => env('DISCOGS_CLIENT_ID'),
    'client_secret' => env('DISCOGS_CLIENT_SECRET'),
    'redirect'      => env('DISCOGS_REDIRECT_URI'),
],
DISCOGS_CLIENT_ID=
DISCOGS_CLIENT_SECRET=
DISCOGS_REDIRECT_URI="${APP_URL}/auth/discogs/callback"
  1. Register the SocialiteWasCalled listener. On Laravel ≤10 add it to App\Providers\EventServiceProvider::$listen; on Laravel 11/12 there is no EventServiceProvider by default, so listen explicitly in AppServiceProvider::boot():
use Illuminate\Support\Facades\Event;
use SocialiteProviders\Manager\SocialiteWasCalled;

public function boot(): void
{
    Event::listen(SocialiteWasCalled::class, \SocialiteProviders\Discogs\DiscogsExtendSocialite::class.'@handle');
}

(If your socialiteproviders/manager version auto-registers listeners, this is redundant but harmless — skip it only after you've confirmed the driver resolves.)

  1. Routes — note the two-step flow:
Route::get('/auth/discogs/redirect', fn () => Socialite::driver('discogs')->redirect());

Route::get('/auth/discogs/callback', function (Request $request) {
    $token = Socialite::driver('discogs')->getToken(); // oauth_token + oauth_token_secret

    $user = Socialite::driver('discogs')->userFromTokenAndSecret(
        $token['oauth_token'],
        $token['oauth_token_secret'],
    );

    // $user->getId(), $user->getNickname()  (Discogs username)
});
  1. First use case to build: "Sign in with Discogs" on a collection/wantlist viewer. It is the highest-value, lowest-risk integration — you get a stable numeric user id plus a long-lived token you can reuse for /users/{username}/collection/folders/0/releases.

Where to look first: vendor/socialiteproviders/discogs/ — read the provider class (getAuthUrl, getTokenUrl, getUserByToken) before touching your controller. It is a few dozen lines and answers every question about what fields you actually receive. Then read the 4.1.0...4.2.0 compare diff in composer.json; the release notes for 4.2.0 are literally just that link, so the diff is the release note.

Implementation Patterns

Persist the token pair, not a session. OAuth 1.0a tokens from Discogs do not expire; they live until the user revokes the app. Store them on your users/social_accounts table:

$table->string('provider')->default('discogs');
$table->string('provider_user_id');      // Discogs numeric id — stable
$table->string('nickname');              // Discogs username — mutable
$table->text('oauth_token');
$table->text('oauth_token_secret');      // encrypt: cast 'encrypted'
$table->unique(['provider', 'provider_user_id']);

Use 'oauth_token_secret' => 'encrypted' in $casts. The secret is a bearer credential in all but name.

Account linking must key on the numeric id, not the username. Discogs usernames can change. getId() is the only durable identifier, and getNickname() is what humans recognise.

Separate the "login" concern from the "API access" concern. The Socialite callback should do exactly one thing: obtain and persist tokens. Everything else (collection sync, wantlist diffing, marketplace lookups) belongs in a DiscogsClient service that signs requests with the stored pair:

final class DiscogsClient
{
    public function __construct(private readonly string $token, private readonly string $secret) {}

    public function get(string $path, array $query = []): array
    {
        return Http::withOptions(['auth' => 'oauth'])
            ->withToken(/* ... */)
            ->withHeaders(['User-Agent' => config('app.name').'/1.0 +'.config('app.url')])
            ->baseUrl('https://api.discogs.com')
            ->get($path, $query)
            ->throw()
            ->json();
    }
}

For the OAuth 1.0a signature itself, guzzlehttp/oauth-subscriber is the least-surprising option — wrap it as a middleware on a dedicated Guzzle client and keep Http:: for everything else so you don't mix signing strategies.

Queue anything that fans out. A collection sync is N paginated calls at 60 req/min authenticated. Dispatch a job per page and accept that a 3,000-release collection takes minutes, not seconds. Discogs returns pagination.pages in every list response — drive your job chain off that.

Re-auth handling: wrap API calls in a try/catch on 401 and flip a discogs_revoked_at flag, then prompt re-link. Users revoke apps in Discogs settings and you will only find out on the next call.

Testing: Socialite is static-facade-heavy, so fake the driver rather than the HTTP layer:

Socialite::shouldReceive('driver->userFromTokenAndSecret')
    ->andReturn((new \Laravel\Socialite\Two\User)->map([
        'id' => 12345, 'nickname' => 'collector',
    ]));

For the callback test, assert your token-persistence path with a fake getToken() returning ['oauth_token' => 'x', 'oauth_token_secret' => 'y']. Keep a small recorded Discogs JSON fixture for userFromTokenAndSecret shape so a package bump that changes field mapping fails loudly.

Multi-guard / SPA note: OAuth 1.0a requires a top-level browser redirect at both steps. You cannot do this from fetch()/Inertia XHR — the first leg must be window.location = '/auth/discogs/redirect'.

Gotchas and Tips

Driver [discogs] is not supported is almost always the listener registration, not the package. Check it before you debug config.

No email — this is the big one. Discogs' /oauth/identity endpoint returns id, username, resource_url, and consumer_name; there is no email field and there are no scopes to request one. Any registration flow that assumes $user->getEmail() will break. Design for a username-based account or ask for email separately post-link.

No scopes. OAuth 1.0a + Discogs derives permissions from the consumer app itself, so ->scopes([...]) is a no-op. Don't copy OAuth 2.0 boilerplate into this driver.

stateless() does nothing here. That's OAuth 2.0-only; OAuth 1.0a needs the request token round-trip. If you're behind a load balancer with sticky sessions off, you'll get intermittent verification failures — the userFromTokenAndSecret pattern above sidesteps session dependence entirely, which is why it's the safer default.

Redirect URI must match byte-for-byte, including scheme and trailing slash. http://localhost:8000/auth/discogs/callback vs https:// is the classic local-vs-Heroku failure. One callback URL per Discogs app means you genuinely need separate apps per environment.

Send a User-Agent on every API call. Discogs blocks requests without one (403), and the error message does not say so. Bake it into your client's default headers.

Respect 60 req/min authenticated. Discogs returns X-Discogs-Ratelimit-Remaining; back off on 429 rather than retrying immediately. Unauthenticated calls are capped much lower (25/min), so always sign.

config:cache in production will happily serve stale services.discogs values. Any credential rotation needs php artisan config:cache in the deploy.

Store the secret encrypted and keep it out of logs. oauth_token and oauth_verifier land in the callback query string, so your access-log redaction rules matter — Laravel's default log middleware won't scrub them for you.

On 4.2.0 specifically: the release notes contain nothing but a compare/4.1.0...4.2.0 link — no breaking changes, features, or deprecations are documented. Treat it as a patch/minor alignment release (most likely constraint bumps against socialiteproviders/manager / laravel/socialite). Before deploying, read the actual diff, run composer why-not socialiteproviders/discogs 4.2.0 in your app to confirm nothing is holding you back, and re-run your callback test with a recorded identity fixture. If you pin the package, pin to ^4.2 and let the manager resolve — mixing a 4.2 driver with an old manager is the most likely source of a silent SocialiteWasCalled mismatch.

Extension point: if you need Discogs-specific behaviour (custom identity parsing, extra headers), extend SocialiteProviders\Discogs\Provider and register your own SocialiteWasCalled handler that maps 'discogs' to your subclass, rather than forking the package.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment