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

Laravel Socialite Mediawiki Laravel Package

taavi/laravel-socialite-mediawiki

A Laravel Socialite provider that lets users sign in to your application with their MediaWiki account, adding MediaWiki OAuth support to Socialite's familiar redirect and callback flow.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Install, then read the source before wiring anything. The package is small and single-maintainer, so spend five minutes in vendor/taavi/laravel-socialite-mediawiki/src first. You are looking for three facts the README may not settle:

  • The provider's fully-qualified class name (you'll need it for buildProvider).
  • Which OAuth version it targets — OAuth 2.0 (/w/rest.php/oauth2/...) or legacy OAuth 1.0a (Special:OAuth). Consumer registration, scopes, and token behaviour differ between the two.
  • The exact config/services.php key names (often url, base_url, or wiki_url — they are not standardised).
composer require taavi/laravel-socialite-mediawiki

Check composer.json in the vendor directory while you're there: confirm the declared PHP constraint and the laravel/socialite range against your pinned Laravel release.

2. Register an OAuth consumer on the wiki. This is where the ticket time actually goes. On Wikimedia it's https://meta.wikimedia.org/wiki/Special:OAuthConsumerRegistration/propose; on a self-hosted wiki it's the same special page once Extension:OAuth is installed and $wgOAuth2PublicKey / $wgOAuth2PrivateKey are configured. Request the basic scope, and record the client ID, secret, and the exact callback URL — scheme, host, port, and trailing slash all matter.

3. Configure the service.

// config/services.php
'mediawiki' => [
    'client_id'     => env('MEDIAWIKI_CLIENT_ID'),
    'client_secret' => env('MEDIAWIKI_CLIENT_SECRET'),
    'redirect'      => env('MEDIAWIKI_REDIRECT_URI'),
    'url'           => env('MEDIAWIKI_URL', 'https://meta.wikimedia.org'),
],

4. Register the Socialite driver if the package's service provider doesn't do it for you:

use Laravel\Socialite\Contracts\Factory as SocialiteFactory;

public function boot(): void
{
    $socialite = $this->app->make(SocialiteFactory::class);

    $socialite->extend('mediawiki', fn ($app) => $socialite->buildProvider(
        \Taavi\SocialiteMediaWiki\MediaWikiProvider::class, // verify in vendor/
        $app['config']['services.mediawiki']
    ));
}

5. Routes and controller. Two routes and one callback handler:

Route::get('/auth/mediawiki', fn () => Socialite::driver('mediawiki')->redirect());
Route::get('/auth/mediawiki/callback', [MediaWikiLoginController::class, 'callback']);
public function callback()
{
    $mw = Socialite::driver('mediawiki')->user();

    $user = User::firstOrCreate(
        ['name' => $mw->getNickname()],
        ['mediawiki_sub' => $mw->getId()]
    );

    Auth::login($user, remember: true);

    return redirect()->intended('/dashboard');
}

The first real use case is almost always "Log in with Wikimedia" on a Toolforge tool, an edit-a-thon sign-up page, or a campaign/event dashboard — a Laravel app whose entire audience already has a wiki account and no appetite for a new password.

Implementation Patterns

Persist external identity in its own table, not on users. This is the one structural decision that matters:

Schema::create('social_identities', function (Blueprint $table) {
    $table->id();
    $table->foreignId('user_id')->constrained()->cascadeOnDelete();
    $table->string('provider');           // 'mediawiki'
    $table->string('provider_user_id');
    $table->json('raw_payload')->nullable();
    $table->timestamps();

    $table->unique(['provider', 'provider_user_id']);
});

This decouples your user model from the package: an IdP swap, a fork, or a second provider becomes a data-only change. It also gives you somewhere to stash raw_payload, which is the single most useful debugging artifact when the profile shape surprises you.

Wrap the driver behind your own abstraction. Given ~0 stars and 0 dependents, plan to fork into an internal namespace right after your spike and treat upstream as a reference implementation, not a dependency. A thin MediaWikiAuthenticator interface with one authenticate(): ExternalIdentity method makes that cheap and keeps Socialite::driver('mediawiki') out of your domain code.

Build the account-linking flow explicitly. Two paths, not one:

  • First login — no existing account matched. Because the profile carries no email, you must decide whether wiki identity alone is sufficient (fine for wiki-centric tools) or whether to prompt for an email before completing registration.
  • Link login — an already-authenticated user clicks "link my wiki account." On collision (provider_user_id already bound to a different user), fail loudly rather than silently merging.

Use updateOrCreate on the identity, not the user. Resolve in this order: look up social_identities → if found, load that user; if not, and the session has a logged-in user, link; otherwise create. Keying User::updateOrCreate on getNickname() will produce duplicate accounts the first time someone is renamed on-wiki.

Spike first, log the payload. A 1–2 day branch that registers a dev consumer, completes the handshake, and Log::info()s the raw profile answers most of the open questions at once: what claims exist, whether the base URL is configurable, and whether the token does anything beyond authentication. Do this before designing the schema around assumptions.

Test without the network. Mock the Socialite user rather than hitting the wiki:

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

Then assert on your own linking logic. The OAuth handshake itself you validate manually once per environment.

Register per-environment consumers. Staging and production need either separate consumers or separate registered callback URLs. This is the most common go-live failure and it's invisible until someone tries to log in.

Config-driven base URL. If the package exposes a base URL key, set it from env — that's what makes self-hosted MediaWiki (enterprise wikis, chapter wikis) work versus being locked to Wikimedia. Verify this in the spike rather than assuming.

Gotchas and Tips

There is no email in the profile. MediaWiki OAuth profiles return a local sub, a username, and possibly groups — not an email address. Do not overload users.email with a placeholder; make it nullable and handle the no-email path deliberately in your onboarding flow. Anything downstream that assumes a verified email (password reset, notifications, receipts) needs an explicit answer.

Per-wiki IDs are not global IDs. On Wikimedia, the local sub is scoped to one wiki. The stable cross-wiki identifier is the CentralAuth ID. Key users on the wrong field and you get duplicate accounts across en.wikipedia.org, meta.wikimedia.org, etc. — painful to merge later, and the merges are manual. Confirm which one the driver's getId() actually returns before you ship.

Callback URL mismatch is a hard login outage. Exact match on scheme, host, port, and trailing slash. A canonical-host change, a proxy that rewrites http/https, or a domain migration silently breaks login for everyone. Alert on callback failure rate — users rarely report "the login button did nothing."

The token is probably identity-only. MediaWiki's OAuth 2.0 has historically been authentication-oriented. Do not assume the returned access token authorises Action API writes. If the roadmap includes editing or uploading on behalf of users, that likely requires OAuth 1.0a with grants — a different implementation entirely. Defer that work until capability is confirmed.

Socialite major upgrades will break it first. Socialite has changed AbstractProvider signatures across 5.x → 6.x. With zero dependents, nobody else is exercising this package against a new major — you will be the first to discover breakage, and it fails at container resolution, not at a nice error boundary. Pin the version, and budget a review on each Socialite major.

Assume you own maintenance. Single maintainer, sourcehut-hosted, no GitHub issue tracker, so no Dependabot and no GitHub Advisory coverage. composer audit only sees this package if it's on Packagist and an advisory is filed through Packagist's feed. "Wait for a fix" is not a strategy; the fork is.

Packagist vs. VCS repository. Confirm whether it's published to Packagist. If you're consuming it via a VCS repository pointed at git.sr.ht, then sourcehut availability is now a composer install dependency in CI — a real supply-chain consideration for the sake of a few hundred lines.

Secret rotation is a manual, on-wiki operation with no API. Assign an owner and document it. The failure mode is 401s at token exchange after someone rotates the consumer secret without a redeploy.

Handle Socialite's exceptions explicitly. Catch Laravel\Socialite\Two\InvalidStateException separately from generic provider Throwables — the first usually means a stale session or a cookie/domain mismatch in the callback, the second usually means a misconfigured client ID or secret. Logging them as one bucket makes both harder to diagnose.

Extension points. The provider is an AbstractProvider subclass, so the practical escape hatches are overriding getAuthUrl(), getTokenUrl(), getUserByToken(), or mapUserToObject() in your own subclass. That's a lighter-touch fix than forking for a single endpoint change.

Instrument the login path. Every failure mode here — callback mismatch, rotated secret, Socialite upgrade, upstream extension change or OAuth 2.0 deprecation, identity drift after an on-wiki rename — manifests as an outage on the one flow users can't work around.

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.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core
nodisrupt/laravel-agent
sierratecnologia/media-manager