Skip to content
thedevdojoPublic

About

The Profiles package for the DevDojo Platform

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

16 Commits

Folders and files

Repository files navigation

DevDojo Accounts

Drop-in account management for the TALL stack. A complete, beautifully designed account area — profile details, avatar uploads, verified email changes, connected accounts, password management, two-factor authentication, active devices, and billing — that you can add to any Laravel + Livewire application in minutes.

Think Clerk's <UserProfile />, rebuilt natively for Tailwind, Alpine, Laravel, and Livewire.

<x-accounts::user-profile />

That one line gives your users a full account management UI.


Features

  • Profile details — name and avatar management with instant previews, drag-free uploads, and automatic fallback to locally generated initials avatars (no external avatar services).
  • Verified email changes — changing an email sends a six digit code to the new address; the swap only happens after verification. Codes are hashed, expire, throttle resends, and lock out after five failed attempts.
  • Username support — optional, validated, unique.
  • Connected accounts — list, link, and unlink social providers (Google, GitHub, X, and a dozen more) via devdojo/auth. Linking is done safely against the authenticated user, and users can never remove their only way of signing in.
  • Password management — set or update with current-password verification and a "sign out of all other devices" option. Password rules automatically follow your devdojo/auth password policy.
  • Two-factor authentication — full TOTP setup wizard: QR code, can't-scan manual key entry, six digit confirmation, and backup codes with copy / download / print / regenerate. Compatible with devdojo/auth's 2FA login challenge.
  • Active devices — see every signed-in session with browser, platform, IP, and last-active time. Revoke any of them. ("This device" is always protected.)
  • Delete account — Clerk-style type-to-confirm plus password confirmation. Fires an event so you can clean up related data.
  • Billing tab — appears automatically when devdojo/billing is installed: current plan, status, renewal date, customer portal, plans, and invoices.
  • User button — an avatar dropdown with identity header, "Manage account", your own menu items, and sign out.
  • Three mounting modes — a ready-made full page (/user/profile), an embeddable component, or a modal.
  • Custom pages — add your own tabs to the sidebar from config, powered by a Blade view or a Livewire component.
  • Dark mode, i18n, events — everything is translatable, dark-mode ready, and instrumented with events.

Every section degrades gracefully: features that depend on companion packages hide themselves when the package isn't installed, and every section can be toggled in config.


Requirements

  • PHP 8.2+
  • Laravel 10, 11, 12, or 13
  • Livewire 3+ (Alpine is bundled with Livewire — no extra install)
  • Tailwind CSS in your build (see Styling)

Installation

composer require devdojo/accounts

Add the trait to your User model:

use Devdojo\Accounts\Traits\HasProfile;

class User extends Authenticatable
{
    use HasProfile;
}

Run the migrations (adds a nullable avatar column if you don't have one, plus a small table for pending email changes):

php artisan migrate

Tell Tailwind about the package views so its classes get compiled.

Tailwind v4 — add to your CSS entry:

@source "../../vendor/devdojo/accounts/resources/views/**/*.blade.php";

Tailwind v3 — add to tailwind.config.js:

content: [
    // ...
    './vendor/devdojo/accounts/resources/views/**/*.blade.php',
],

If you upload avatars to the default public disk, make sure storage is linked:

php artisan storage:link

That's it. Visit /user/profile — the full account area is already there.

The fastest path to a complete auth + account experience: install devdojo/auth for registration/login/social/2FA, then add devdojo/accounts. Users can sign up, sign in, and manage everything about their account — without you writing a single screen.


Usage

1. The ready-made page (zero work)

The package registers /user/profile with deep-linkable tabs:

/user/profile            → Profile details
/user/profile/security   → Security
/user/profile/billing    → Billing (when devdojo/billing is installed)

Change the path, middleware, or wrap it in your own layout:

// config/devdojo/accounts.php (after vendor:publish)
'routes' => [
    'enabled' => true,
    'path' => 'account',            // now lives at /account
    'middleware' => ['web', 'auth'],
    'layout' => 'layouts.app',      // a Blade *component* layout with a $slot
],

2. Embedded anywhere

Drop the component into any existing page:

<x-accounts::user-profile />

{{-- or open on a specific tab --}}
<x-accounts::user-profile tab="security" />

3. As a modal

The same component can render as a modal on top of everything else — just flip the mode prop. Include it once (e.g. in your layout), then open it from anywhere:

<x-accounts::user-profile mode="modal" />

<button x-data @click="$dispatch('accounts-open-modal')">
    Account settings
</button>

Want it open as soon as the page loads (e.g. on a dedicated settings route)?

<x-accounts::user-profile mode="modal" :open="true" />

{{-- and it can still start on a specific tab --}}
<x-accounts::user-profile mode="modal" tab="security" />

The modal centers itself, keeps a comfortable minimum height, closes on Esc or backdrop click, and is scrollable when a tab is taller than the viewport. <x-accounts::user-profile-modal /> still works and accepts the same tab / open props.

The user button

The classic avatar dropdown — identity header, "Manage account", custom items, sign out:

<x-accounts::user-button />

{{-- show the user's name next to the avatar --}}
<x-accounts::user-button :show-name="true" />

{{-- open the account area in the modal instead of navigating --}}
<x-accounts::user-button mode="modal" />

Add your own menu items from config:

'user_button' => [
    'menu' => [
        ['label' => 'Dashboard', 'url' => '/dashboard', 'icon' => '<svg ...></svg>'],
    ],
],

Avatars everywhere

The HasProfile trait gives every user a smart avatar URL — their upload, else their social account's avatar, else a generated initials avatar:

<img src="{{ $user->profileAvatarUrl() }}" class="size-8 rounded-full" />
$user->profileDisplayName();   // "Tony Stark" (name → username → email prefix)
$user->profileInitials();      // "TS"
$user->hasUploadedAvatar();    // bool
$user->updateProfileAvatar($uploadedFile);
$user->removeProfileAvatar();

Configuration

Publish the config to config/devdojo/accounts.php:

php artisan vendor:publish --tag=accounts:config
return [
    'routes' => [
        'enabled' => true,
        'path' => 'user/profile',
        'middleware' => ['web', 'auth'],
        'layout' => null,                  // null = the package's own clean layout
    ],

    'features' => [
        'avatar' => true,
        'name' => true,
        'username' => false,               // requires a `username` column on users
        'email' => true,
        'connected_accounts' => true,      // requires devdojo/auth
        'password' => true,
        'two_factor' => true,              // requires devdojo/auth
        'sessions' => true,                // requires SESSION_DRIVER=database
        'delete_account' => true,
        'billing' => true,                 // requires devdojo/billing
    ],

    'avatar' => [
        'disk' => 'public',
        'directory' => 'avatars',
        'max_kilobytes' => 10240,          // 10 MB
    ],

    'email_change' => [
        'code_expires_in' => 10,           // minutes
        'resend_throttle_seconds' => 60,
    ],

    'delete_account' => [
        'redirect' => '/',
    ],

    'user_button' => [
        'menu' => [],
    ],

    'custom_pages' => [],
];

Custom pages

Add your own tabs to the account sidebar:

'custom_pages' => [
    [
        'slug' => 'api-keys',                  // /user/profile/api-keys
        'label' => 'API Keys',
        'icon' => '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4"/></svg>',
        'view' => 'pages.account.api-keys',    // a Blade view…
        'component' => null,                   // …or a Livewire component alias
    ],
],

Custom pages are deep-linkable, appear in the mobile nav, and work in the modal too.

For per-user visibility (or when you prefer code over config), register pages at runtime from a service provider — the when closure is evaluated on every render:

use Devdojo\Accounts\Accounts;

Accounts::registerPage([
    'slug' => 'import',
    'label' => 'Import',
    'icon' => '<svg …>',
    'view' => 'pages.account.import',
    'when' => fn () => auth()->user()?->can('import-posts'),
]);

Companion packages

devdojo/auth — connected accounts + two-factor

With devdojo/auth installed:

  • Connected accounts appear on the Profile tab. Providers come from your config/devdojo/auth/providers.php (only active ones are offered). Linking uses the package's own callback — add it to your OAuth app alongside the auth one:

    https://your-app.com/user/profile/connect/{provider}/callback
    
  • Two-step verification appears on the Security tab, using the same columns, encryption format, and recovery codes as devdojo/auth — so the 2FA challenge at login Just Works. It also respects devdojo.auth.settings.enable_2fa.

  • Password rules follow your devdojo.auth.settings.password_* policy automatically.

devdojo/billing — the Billing tab

With devdojo/billing installed, a Billing tab appears showing the user's current plan, status, and renewal date, with buttons for the customer portal (Stripe) or update URL (Paddle), the plans page, and invoices. No configuration needed — it reads everything from the billing package.

Active devices

Session listing needs sessions stored in the database:

SESSION_DRIVER=database
php artisan session:table && php artisan migrate

The section hides itself for other session drivers.


Events

Every meaningful action fires an event with the $user (and a $payload array where useful):

Event Fired when
Devdojo\Accounts\Events\ProfileUpdated Name/username/profile saved
Devdojo\Accounts\Events\AvatarUpdated New avatar stored
Devdojo\Accounts\Events\AvatarRemoved Avatar removed
Devdojo\Accounts\Events\EmailChangeRequested Verification code sent (payload: email)
Devdojo\Accounts\Events\EmailChanged Email swapped (payload: previous)
Devdojo\Accounts\Events\PasswordUpdated Password set/updated
Devdojo\Accounts\Events\TwoFactorEnabled TOTP confirmed
Devdojo\Accounts\Events\RecoveryCodesRegenerated Backup codes regenerated
Devdojo\Accounts\Events\SessionRevoked A device signed out (payload: session_id)
Devdojo\Accounts\Events\ConnectedAccountDisconnected Provider unlinked (payload: provider)
Devdojo\Accounts\Events\AccountDeleted Fired before the user row is deleted
Event::listen(AccountDeleted::class, function ($event) {
    // archive posts, cancel subscriptions, say goodbye…
});

Customization

Language

Every string lives in a translation file. Publish and edit:

php artisan vendor:publish --tag=accounts:lang
lang/vendor/accounts/en/ui.php

Add other locales by creating lang/vendor/accounts/{locale}/ui.php.

Views

All Blade views (including every UI element component) can be published and customized:

php artisan vendor:publish --tag=accounts:views

Theming hooks

Every significant element carries a stable ac-* class (ac-card, ac-sidebar, ac-nav-item, ac-section, ac-button, ac-badge, ac-menu, ac-otp-digit, …) so you can restyle anything with plain CSS without touching the views:

.ac-card { border-radius: 0; }
.ac-nav-item[aria-current="page"] { color: var(--brand); }

Dark mode works out of the box with Tailwind's dark: variant.

Styling

The components are styled with Tailwind utility classes and compile through your build, so they automatically pick up your Tailwind theme (fonts, radii, palette tweaks). The only requirement is the @source / content entry from the install step.


Security model

  • Email changes are verified at the new address before the swap; codes are stored hashed, expire after 10 minutes, throttle resends, and lock after 5 failed attempts. A successful change marks the email verified.
  • Password updates require the current password (when one exists) and can revoke every other session.
  • Account deletion requires typing the confirmation phrase exactly, plus the password when one exists.
  • Disconnecting a social account is blocked when it's the user's only sign-in method.
  • Social linking refuses provider identities already connected to another user.
  • The current session can never be revoked from the devices list.
  • Unconfirmed 2FA secrets are discarded when setup is abandoned.

Testing

The package ships with a full Pest suite (Livewire component tests + unit tests):

composer install
composer test

Legacy goodies

Earlier versions of this package shipped dynamic key/value profile fields, social links, and privacy settings. Those traits, models, configs, and migrations are still here and unchanged: HasProfileKeyValues, HasDynamicFields, config/profile.php, config/privacy.php.

License

MIT © DevDojo

About

The Profiles package for the DevDojo Platform

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages