Search by

internetguru / laravel-user

internetguru

Interent Guru Laravel User

Package info

github.com/internetguru/laravel-user

pkg:composer/internetguru/laravel-user

Statistics

Installs: 3 548

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v8.2.2 2026-09-09 05:04 UTC

This package is auto-updated.

Last update: 2026-09-09 05:05:33 UTC


README

Internet Guru Laravel User is a library that provides seamless integration with various social authentication providers. It stores the user's social identity in the database and allows the user to link multiple social identities to a single account. It also provides PIN-based login via email and a full user management UI.

Branch Status Code Coverage
Main tests coverage
Staging tests coverage
Dev tests coverage

Table of Contents

Features and Terminology

  • Account – application user account.
  • Identity – provider & provider_user_id.
  • Register – create a new account with a new identity linked to it.
  • Connect – link a new identity to the current account.
  • Disconnect – unlink an existing identity from the current account.
  • Transfer – unlink an existing identity from one account and link it to the current one.
  • Automatic account – an account created programmatically, e.g. during data import, that has never been logged into; created_by equals its own id and logged_at is null.

Installation

  1. Install the package via Composer:

    composer require internetguru/laravel-user
  2. Publish and run the migration files:

    php artisan vendor:publish --provider="InternetGuru\LaravelUser\LaravelUserServiceProvider" --tag="ig-user:migrations"
    php artisan migrate
  3. Set required session configuration in config/session.php:

    'expire_on_close' => true,
    'lifetime' => 120,

    The service provider throws an exception in debug mode or logs a warning in production if these values are not set correctly.

Configuration

Environment Variables

Variable Default Description
GOOGLE_CLIENT_ID Google OAuth client ID
GOOGLE_CLIENT_SECRET Google OAuth client secret
GOOGLE_REDIRECT_URI Google OAuth redirect URI
SEZNAM_CLIENT_ID Seznam OAuth client ID
SEZNAM_CLIENT_SECRET Seznam OAuth client secret
SEZNAM_REDIRECT_URI Seznam OAuth redirect URI
AUTH_LOGIN_ENABLED true Set to false to disable login — all login entry points return 404
AUTH_DEMO false Enable demo login — no password, user selected from list
AUTH_MERGE_ENABLED false Set to true to allow merging user accounts
AUTH_MERGE_INLINE_LIMIT 100 Up to this many merge candidates are embedded in the user detail and filtered in the browser; above it the picker searches the server
AUTH_SYSTEM_NOTICE_ROLE operator Lowest role that sees the system notice install hint; unknown value hides it for everyone
LANG_DOMAINS "" Comma-separated lang:domain pairs, e.g. cs:example.cz,da:example.dk

Disabling a Provider

Set enabled to false in config/services.php for any provider to hide it from all login/connect buttons:

'google' => [
    'enabled' => false,
    ...
],

Roles

The package ships with a five-level Role enum. Applications can override User::roles() to return a custom enum.

Role Level Icon
CUSTOMER 10 fa-user
OPERATOR 20 fa-user-nurse
AUDITOR 30 fa-user-shield
MANAGER 40 fa-user-tie
ADMIN 50 fa-user-gear

Each case exposes level(), icon(), and translation() methods.

Dynamic Role Checks

The User model provides magic is{Role}() and is{Role}Plus() methods based on the configured roles enum:

$user->isAdmin();         // true if role === ADMIN
$user->isManagerPlus();   // true if role level >= MANAGER level
$user->isOperatorPlus();  // true if role level >= OPERATOR level

Role Helpers

User::roles()::cases();        // all Role cases
User::publicRolesArray();      // all cases except ADMIN
User::roleOptions();           // [['id' => 'manager', 'name' => 'Manager'], ...]

Socialite Providers

Built-in providers: Google and Seznam. Any other Socialite-compatible provider can be added by the application.

The socialite.action route accepts a provider and an action: login, loginAndConnect, register, connect, or disconnect. Use socialite.callback for the OAuth return URL.

Routes

Route name URI Description
login GET /login Unified login page
logout GET /logout Log out
pin-login.form POST /pin-login/send Send PIN email
pin-login.verify GET /pin-login/verify Show PIN entry form
pin-login.verify.submit POST /pin-login/verify Verify PIN — throttled: 5 per 10 minutes
socialite.action GET /socialite/{provider}/{action} Redirect to provider
socialite.callback GET /socialite/{provider}/{action}/callback Handle provider callback
users.index GET /users User list — managers and above
users.show GET /users/{user} User detail
users.update POST /users/{user} Update name, email, phone, or role
users.merge-candidates GET /users/{user}/merge-candidates JSON search over the accounts this user may be merged with, ?q= terms, one row over what the picker lists

Usage Examples

{{-- Socialite login/register/connect buttons --}}
<x-ig-user::buttons action="login" :showRemember="true" />
<x-ig-user::buttons action="login" :showRemember="false" />
<x-ig-user::buttons action="register" />
<x-ig-user::buttons action="connect" />

{{-- Direct disconnect link --}}
<a href="{{ route('socialite.action', [
    'provider' => InternetGuru\LaravelUser\Enums\Provider::GOOGLE,
    'action' => InternetGuru\LaravelUser\Enums\ProviderAction::DISCONNECT,
]) }}">Disconnect Google</a>

Disabling Login

Set AUTH_LOGIN_ENABLED=false to close all ways in — /login, /register, /pin-login, the PIN endpoints and socialite login/register all return 404. Routes stay registered, so route('login') keeps resolving in host apps and views.

AUTH_LOGIN_ENABLED=false

Logout, the users routes and socialite connect/disconnect remain available, so already authenticated sessions keep working. The x-ig-user::user-menu component hides its login link.

Demo Mode

Set AUTH_DEMO=true to enable demo login. The login page switches to login-demo view, which lists all non-automatic users sorted by role from highest to lowest for one-click login — no password required. Useful for staging environments.

AUTH_DEMO=true

The list is provided by User::getDemoUsers(), which returns all non-automatic users sorted by role level descending.

PIN Login

PIN login allows users to authenticate with a 6-digit PIN sent to their email address. The PIN is prefixed with IG- in the UI, for example IG-123456.

Flow

  1. User submits their email on the unified /login page.
  2. The server sends a PIN email and redirects to /pin-login/verify.
  3. User enters the PIN in the 6-box input; a hidden field assembles the full value.
  4. On success, the user is logged in and redirected.

Settings

Setting Value
PIN lifetime 10 minutes
Resend throttle 1 minute
Verify throttle 5 attempts per 10 minutes
PIN format IG- prefix + 6-digit numeric code

Send Form Options

  • Remember me – persists the session beyond the browser close.
  • Create account if not found – when checked, a new account is created for unknown emails.

reCAPTCHA

The PIN send form is protected by reCAPTCHA v3. Ensure laravel-common reCAPTCHA is configured.

Language and Locale

The SetAppLocale middleware, registered automatically in the web group, handles language detection and persistence.

Priority Order

  1. Explicit ?lang= query parameter.
  2. Authenticated user's lang column.
  3. Session-stored locale.
  4. Browser Accept-Language header — Slovak falls back to Czech if Czech is configured.
  5. app.locale config default.

Lang Domains

Map languages to dedicated domains via LANG_DOMAINS:

LANG_DOMAINS=cs:example.cz,da:example.dk
  • Requests on a lang domain always enforce that domain's language.
  • When a user switches to a language that has a dedicated domain, they are redirected there.
  • When a user switches to a language without a dedicated domain while on a lang domain, they are redirected to app.www.

Language is saved to the authenticated user's lang column on every explicit change.

User Management

The package provides a user list at /users and a user detail page at /users/{user}, built on laravel-model-browser. Access is controlled by UserPolicy.

Users can update their own name, email, phone, and role via POST to /users/{user}. Role changes are subject to the setRole policy.

Merging Accounts

Merging joins several accounts of the same person into one group, so any of them owns the group's records. It is disabled by default; set AUTH_MERGE_ENABLED=true to expose the merge and unmerge controls on the user detail page. While disabled, the merge gate denies everyone, which hides the section and makes the users.merge, users.unmerge and users.merge-candidates endpoints return 403.

AUTH_MERGE_ENABLED=true

A candidate list of at most User::MERGE_CANDIDATES_SHOWN accounts is listed straight away, each row with an Add button and no search box at all. A longer one is searched by typing: the search matches name and e-mail, accents ignored on both sides (it uses whereLikeUnaccented from laravel-model-browser), nothing is listed until something is typed, and a query still matching more accounts than the list holds asks for a narrower one instead of paginating. The candidate list never loads the whole table: while an installation holds at most AUTH_MERGE_INLINE_LIMIT candidates they travel with the page and are filtered in the browser, and above that every keystroke searches users.merge-candidates instead. An account already in the group keeps its row, greyed out and with its button disabled, so adding one does not pull the rows below it up. The merge ability authorizes the picked account again on the way in.

Automatic Accounts

An account is considered automatic when created_by === id and logged_at IS NULL. These accounts are hidden from getDemoUsers(). When a user registers via PIN login with the "create account" option, reusing an existing automatic account converts it to a regular account.

User::summary() makes a wider cut than that: it lists only accounts somebody has signed in to at least once (scopeLoggedIn), whoever created them, since an account opened on a customer's behalf is a placeholder for a person until that person uses it. The list's never_logged_in checkbox brings the rest back.

Blade Components

<x-ig-user::buttons>

Renders socialite provider buttons for a given action.

Prop Default Description
providers User::providers()::enabledCases() List of enabled providers
action ProviderAction::LOGIN Action: login, register, connect
prev_url User::getPreviousUrl() URL to redirect to after auth
showRemember false Show "Remember me" checkbox
disabled false Disable all buttons

<x-ig-user::user-menu>

Dropdown menu for authenticated users showing name, role icon, link to user detail, and logout. Shows a login link for guests.

<x-ig-user::pin-input>

Alpine.js-powered 6-box PIN input with paste, backspace, and arrow key support.

Prop Default Description
name 'pin' Hidden input field name
length 6 Number of digit boxes
prefix 'IG-' Visual prefix label

Add to Home Screen

The use-app system notice links part of its message to a guide that walks the user through installing the site on their home screen, built on philfung/add-to-homescreen. Any element carrying data-add-to-homescreen opens the guide.

The guide is opt-in, because it needs an npm dependency the application owns. Without the wiring below the notice degrades to plain text. To enable it:

  1. Install the library:

    npm install pwa-add-to-homescreen
  2. Alias the package's JavaScript and register the asset plugin in vite.config.js. The plugin mirrors the library's illustrations into public/vendor/add-to-homescreen/img, which the library loads at runtime and Vite therefore cannot bundle:

    import addToHomescreenAssets from './vendor/internetguru/laravel-user/resources/js/vite-add-to-homescreen-assets.js';
    
    export default defineConfig({
        plugins: [laravel({ /* ... */ }), addToHomescreenAssets()],
        resolve: {
            alias: {
                'ig::user-js': path.resolve(__dirname, 'vendor/internetguru/laravel-user/resources/js'),
            },
        },
    });

    Add /public/vendor to .gitignore.

  3. Import it from resources/js/app.js:

    import 'ig::user-js';

The library also expects the site to be a valid PWA: a web manifest linked from the layout and a square /apple-touch-icon.png of at least 40x40 pixels.

User Preferences

The user_preferences table provides a simple key-value store per user.

$user->setPreference('theme', 'dark');
$theme = $user->getPreference('theme', 'light'); // 'dark'

Association History

The User model uses the AssociationHistory trait from laravel-common. Changes to the following fields are tracked automatically:

name, email, phone, role, lang, socialite

The history is displayed on the user detail page, visible to managers and above.

User Policy

Gate Description
crud User can edit themselves; admins can edit all; managers can edit users with lower/equal roles
viewAny Managers and above can view the user list
administrate Managers and above
setRole Admins can set any role; managers can set roles up to their own level
merge Managers and above, both subjects editable; requires AUTH_MERGE_ENABLED=true
viewRoleList Everyone signed in; override to restrict the roles and permissions page

Publish the default policy to customise it:

php artisan vendor:publish --provider="InternetGuru\LaravelUser\LaravelUserServiceProvider" --tag="ig-user:policies"

Roles and Permissions Page

GET /role-list (route name role-list) documents the permission model of the whole application: for every role, what it gains — or loses — compared to the role below it.

Nothing is maintained by hand. The page discovers the policies, invokes each ability once per role with sample arguments, and diffs the results, so a policy change shows up on the page by itself. ADMIN is left out, as User::publicRolesArray() does everywhere else.

Discovery

Policies come from two places: the directories listed in ig-user.role_list.policy_paths (app/Policies by default) and the policies registered with the gate. When an application policy extends a package one, only the application class is listed, so its overrides drive the summary.

Sample Arguments

An ability is invoked with arguments built by PolicyArgumentResolver, which knows accounts, the roles enum and scalars. Both the acting and the target account get the evaluated role, so an ability comparing the two reads as "what a role may do to its own peer". The accounts are never stored, and their null key keeps every relation query empty.

An ability taking a type the resolver does not know is left out of the page rather than reported as denied. Teach the resolver about your own models to list those abilities too:

namespace App\Support;

use App\Models\Machine;
use BackedEnum;
use InternetGuru\LaravelUser\Support\PolicyArgumentResolver;
use ReflectionParameter;

class AppPolicyArgumentResolver extends PolicyArgumentResolver
{
    public function resolve(ReflectionParameter $parameter, BackedEnum $role): mixed
    {
        return match ($parameter->getType()?->getName()) {
            Machine::class => new Machine(['name' => 'Sample']),
            default => parent::resolve($parameter, $role),
        };
    }
}
// AppServiceProvider::register()
$this->app->bind(PolicyArgumentResolver::class, AppPolicyArgumentResolver::class);

Naming Abilities

Each ability is labelled by the role-list.{Policy}@{ability} translation line, taken from the application first and from ig-user::role-list second — so an application can rename a package ability in the words of its own domain. An unnamed ability falls back to its key, which keeps a newly added policy visible instead of blank.

// lang/en/role-list.php
return [
    'MachinePolicy@manage' => 'Manage machines',
];

Import the stylesheet in your app.scss next to the other package partials:

@import 'ig::user/role-list';

IgUserSeeder

The package ships with IgUserSeeder for seeding the Internet Guru team accounts with Google/Seznam socialites and ADMIN role. Use it in your DatabaseSeeder:

$this->call(\InternetGuru\LaravelUser\Database\Seeders\IgUserSeeder::class);

Publishing

Tag Destination Description
ig-user:migrations database/migrations/ Database migrations
ig-user:translations lang/vendor/ig-user/ Language files — cs, en, da
ig-user:views resources/views/vendor/ig-user/ Blade views
ig-user:policies app/Policies/ UserPolicy
php artisan vendor:publish --provider="InternetGuru\LaravelUser\LaravelUserServiceProvider" --tag="ig-user:translations"
php artisan vendor:publish --provider="InternetGuru\LaravelUser\LaravelUserServiceProvider" --tag="ig-user:views"

E2E Tests

The package includes Playwright E2E tests via laravel-common test helpers. Register them in your Playwright config:

import { registerUserTests } from 'path/to/laravel-user/e2e';

registerUserTests(test, { languages: ['en', 'cs'], demo: true });

Options

Option Type Description
languages string[] Languages to test — login/logout per language
demo boolean Include demo login flow tests

License & Commercial Terms

License

Copyright © 2026 Internet Guru

This software is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0) license.

Disclaimer: This software is provided "as is", without warranty of any kind, express or implied. In no event shall the authors or copyright holders be liable for any claim, damages or other liability.

Commercial Use

The standard CC BY-NC-SA license prohibits commercial use. If you wish to use this software in a commercial environment or product, we offer flexible commercial licenses tailored to:

  • Your company size.
  • The nature of your project.
  • Your specific integration needs.

Note: In many instances, especially for startups or small-scale tools, this may result in no fees being charged at all. Please contact us to obtain written permission or a commercial agreement.

Contact for Licensing: info@internetguru.io

Professional Services

Are you looking to get the most out of this project? We are available for:

  • Custom Development: Tailoring the software to your specific requirements.
  • Integration & Support: Helping your team implement and maintain the solution.
  • Training & Workshops: Seminars and hands-on workshops for your developers.

Reach out to us at info@internetguru.io — we are more than happy to assist you!