# Frappe / ERPNext

Use any Frappe-based site, including ERPNext, Frappe HR, LMS, Helpdesk, Books, and custom apps, as an OAuth2 / OpenID Connect provider.

composer require socialiteproviders/frappe

# Installation & Basic Usage

Please see the Base Installation Guide (opens new window), then follow the provider-specific instructions below.

# Create an OAuth Client in Frappe / ERPNext

See Frappe's official How to setup OAuth Client (opens new window) guide, then set the redirect URI to your callback (e.g. https://your-app.test/auth/frappe/callback) and the scopes to openid. Use openid all when requesting additional User fields or REST API access. After saving, use the generated App Client ID and App Client Secret below.

# Add configuration to config/services.php

'frappe' => [
  'client_id' => env('FRAPPE_CLIENT_ID'),
  'client_secret' => env('FRAPPE_CLIENT_SECRET'),
  'redirect' => env('FRAPPE_REDIRECT_URI'),
  'base_url' => env('FRAPPE_BASE_URL'), // Also used for logout and token revocation
],

Set the site root, without an API path, in .env:

FRAPPE_CLIENT_ID=your-client-id
FRAPPE_CLIENT_SECRET=your-client-secret
FRAPPE_REDIRECT_URI=https://your-app.test/auth/frappe/callback
FRAPPE_BASE_URL=https://erp.example.com

# Add provider event listener

# Laravel 11+

Add the listener in your AppServiceProvider boot method:

use Illuminate\Support\Facades\Event;

Event::listen(function (\SocialiteProviders\Manager\SocialiteWasCalled $event) {
    $event->extendSocialite('frappe', \SocialiteProviders\Frappe\Provider::class);
});
Laravel 10 or below

Add the listener to the listen array in app/Providers/EventServiceProvider.php:

protected $listen = [
    \SocialiteProviders\Manager\SocialiteWasCalled::class => [
        \SocialiteProviders\Frappe\FrappeExtendSocialite::class.'@handle',
    ],
];

# Usage

return Socialite::driver('frappe')->redirect();

The default openid scope is enough for login and profile data. To access the Frappe REST API too:

return Socialite::driver('frappe')
    ->scopes(['openid', 'all'])
    ->redirect();

# Additional User fields

Add the field names to the provider's configuration:

'frappe' => [
  // ...
  'fields' => [
    'custom_department',
    'custom_employee_number',
  ],
],

When configured, the provider automatically requests the all scope and fetches those fields from the authenticated Frappe User record. The OAuth Client in Frappe must allow the all scope.

Additional fields are available in Socialite's raw user array:

$user = Socialite::driver('frappe')->user();

$department = $user->getRaw()['custom_department'] ?? null;

# Logout

No extra configuration is required; the logout and revocation endpoints are derived from base_url.

Revoke the OAuth access token and log out of your Laravel app:

Socialite::driver('frappe')->revokeToken($user->token);
Auth::logout();

To also end the user's Frappe browser session, send the user to Socialite::driver('frappe')->getLogoutUrl().

Frappe v15 and earlier accept a GET, so a redirect works. Frappe v16 restricted this endpoint to POST (opens new window), and POST is CSRF-checked; submit a form carrying the session's csrf_token, or list your app's origin in the site's allowed_referrers.

See Frappe's official logout (opens new window) and token revocation (opens new window) documentation.

# Returned User fields

  • id (OpenID sub, falling back to email)
  • name
  • email
  • avatar
  • given_name
  • family_name
  • roles
  • Any configured fields, via getRaw()