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 Linkedin Laravel Package

artesaos/laravel-linkedin

Artesaos Laravel LinkedIn offers a simple LinkedIn API integration for Laravel applications, with OAuth authentication and convenient helpers for sharing updates, retrieving profile data, and managing LinkedIn connections in your PHP projects.

View on GitHub
Deep Wiki
Context7

Getting Started

Before writing a line of code, check the package constraints: composer show artesaos/laravel-linkedin on a modern app will usually fail resolution because it targets PHP 5.5–7.x and illuminate/support 5.x. This is a Laravel/Lumen 5-era package (last release September 2016), so plan for a legacy app, a fork, or an adapter layer rather than a drop-in on Laravel 10/11.

Minimal path:

  1. Install:
    composer require artesaos/laravel-linkedin
    
  2. Register the provider and facade. Laravel 5.5+ auto-discovers Artesaos\LinkedIn\Providers\LinkedInServiceProvider; older versions need it in config/app.php along with the LinkedIn facade alias from Artesaos\LinkedIn\Facades\LinkedIn.
  3. Publish the config:
    php artisan vendor:publish --provider="Artesaos\LinkedIn\Providers\LinkedInServiceProvider"
    
    You get config/linkedin.php with client_id, client_secret, redirect, and scopes. Put the credentials in .env and reference them from the config.
  4. Create the app at the LinkedIn Developer Portal, add the exact redirect URL (scheme, host, path, trailing slash), and request the scopes you actually need.

Where to look first, in this order:

  • config/linkedin.php — the only knobs most consumers touch.
  • The facade, then the class it resolves (usually Artesaos\LinkedIn\LinkedIn). Read its public methods; that is your real API surface, not the README.
  • vendor/happyr/linkedin-api-client — the package is a thin wrapper over this client, so get(), post(), getLoginUrl(), and getAccessToken() behave as HappyR documents them.
  • The service provider, to see the container binding name you can app() or override.

First use case — "Sign in with LinkedIn" plus a profile read:

Route::get('auth/linkedin', fn () => redirect()->away(LinkedIn::getLoginUrl()));
Route::get('auth/linkedin/callback', 'LinkedInController@callback');
public function callback(Request $request)
{
    $client = LinkedIn::getClient();
    $client->getAccessToken($request->get('code'));

    $profile = $client->get('people/~');   // v1 endpoint — see Gotchas

    $user = User::updateOrCreate(
        ['linkedin_id' => $profile['id']],
        ['name' => $profile['firstName'].' '.$profile['lastName']]
    );

    Auth::login($user);
    return redirect('/dashboard');
}

That flow is the honest ceiling of the package as shipped. Treat anything beyond profile fetch and basic sharing as something you'll be writing yourself against the v2 API.

Implementation Patterns

Wrap the facade, don't spray it. Bind your own LinkedInGateway interface in a service provider and have the implementation call LinkedIn::getClient(). When you inevitably replace the v1 calls with v2, you change one class instead of forty controllers. This is the single highest-leverage move with a package this old.

Persist tokens yourself. The package's default flow keeps the access token in the session, which is useless for queued jobs, scheduled company-page posts, and multi-account setups. Add linkedin_token, linkedin_refresh_token, linkedin_expires_at, and linkedin_id to users (or a social_accounts table), and always inject the token into a fresh client instance for background work.

One client per account. The facade resolves a singleton; do not use it for a multi-tenant app where different organizations have different tokens. Instantiate new \Happyr\LinkedIn\LinkedIn([...]) per account, or resolve a scoped binding keyed by account id.

Queue the write operations. Posts and shares are network-bound and rate-limited. Dispatch a job, add backoff and a retry limit, and record the returned post URN so you can reconcile or delete later.

Keep your own service layer for organization pages. Company-page reads and shares require an admin token and an organization URN. Model that explicitly (Organization with linkedin_urn, token, expires_at) rather than fishing it out of API responses each time.

Testing: always fake the network. Either mock the facade (LinkedIn::shouldReceive('getClient')->andReturn($fake)) or bind a fake gateway in the container. Never let a test hit LinkedIn — the failure modes are unhelpful 4xx bodies. Cover the callback controller with a canned token/profile fixture.

Migrating calls to v2 in place. You can usually keep the OAuth plumbing and swap only the request layer. Add the required headers via the underlying Guzzle client or a custom middleware:

$client->setRequestOption('headers', [
    'X-Restli-Protocol-Version' => '2.0.0',
    'LinkedIn-Version' => '202401',
    'Authorization' => 'Bearer '.$token,
]);

Then replace people/~ with /v2/userinfo (OIDC), and shares with POST /v2/ugcPosts or /v2/shares using the author URN. If the wrapper won't let you set headers cleanly, bypass it: keep the package for getLoginUrl()/getAccessToken(), and issue v2 calls through your own Guzzle client. That hybrid is the pragmatic day-to-day pattern for a maintained app.

Events and logging. Fire an application event after successful link/unlink and log the raw response body on every failure. You will be reading those logs when LinkedIn changes a scope or returns a 403 with a serviceErrorCode.

Gotchas and Tips

The v1 API is gone. This is the headline issue: the package speaks people/~, companies, and people/~/shares, all of which LinkedIn retired. Expect 403/404/410 regardless of correct credentials. The package is best treated as OAuth scaffolding and a reference implementation, not a working integration.

Scopes have changed completely. r_basicprofile, r_emailaddress, and w_share are dead. Current scopes are openid, profile, email, and w_member_social. Requesting legacy scopes yields an opaque authorization error at the consent screen, which is easy to misdiagnose as a config problem.

Redirect URI mismatches are the #1 setup failure. http://localhost and http://127.0.0.1 are different URIs to LinkedIn. Trailing slashes matter. So does https behind a proxy — if APP_URL says http but you serve https, the generated callback won't match. Use URL::forceScheme('https') in the provider.

State/CSRF may not be enforced. The 2016-era flow may not round-trip a state parameter. Add your own nonce in the session before redirecting and verify it in the callback; don't assume the package does it.

No refresh tokens. LinkedIn issues long-lived tokens (~60 days) and does not give you a refresh token in the classic flow. Build a "reconnect" path and alert users before expiry rather than discovering it in a failed cron job.

Queue workers have no session. Any code path relying on the session-stored token silently fails in a job. This is the most common "it works in the browser" bug with this package.

Error bodies are the only useful signal. LinkedIn returns structured errors (serviceErrorCode, message) in the body, not the status line. Wrap calls in try/catch for Guzzle exceptions and log $e->getResponse()->getBody(). Without that you'll see "403 Forbidden" and nothing else.

Don't --ignore-platform-reqs your way into production. If Composer refuses to install on Laravel 9+, that's the package telling you it can't work there. Fork it, or vendor a small adapter and keep the dependency out of the critical path.

Extension points worth knowing: the container binding can be overridden with your own implementation; the config is merged and publishable so you can namespace extra keys (e.g. linkedin.api_version); and the service provider is small enough to subclass if you need a different client construction. Prefer these over editing vendor/.

Version-pin and vendor-patch deliberately. If you must keep it, pin the exact version and use a path or VCS repository for your fork so the patch survives composer update. Add a calendar reminder: this dependency will not get security or API fixes upstream.

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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin