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

Fortify Laravel Package

orchid/fortify

Orchid Fortify integrates Laravel Fortify authentication into Orchid admin panels, providing login, registration, password reset, email verification, and two-factor authentication screens with configurable routes for secure Orchid applications.

View on GitHub
Deep Wiki
Context7

Getting Started

orchid/fortify is a template repository, not a package you install. Do not composer require it — there is no tracked dependency to pin, and the value is entirely in the wiring you copy out of it. Clone it once into a scratch directory, read it, and port the pieces you need into your app:

git clone https://github.com/orchidsoftware/fortify /tmp/orchid-fortify

Assumed starting point: a working Orchid panel (orchid/platform installed, /admin reachable) and stock Fortify already installed:

composer require laravel/fortify
php artisan vendor:publish --provider="Laravel\Fortify\FortifyServiceProvider"

Where to look first, in this order:

  1. FortifyServiceProvider — this is the integration in one file. It shows how Fortify::loginView(), registerView(), resetPasswordView(), twoFactorChallengeView(), and friends are rebound from stock Blade views to Orchid Screens. That mapping is the template's contribution; everything else is presentation.
  2. The routes file — confirms what the template registers itself vs. what it lets Fortify own. Compare against php artisan route:list on your install and note the collisions with /admin/*.
  3. The Screen directory — one Screen subclass per auth flow. Read LoginScreen first; the rest (RegisterScreen, ForgotPasswordScreen, ResetPasswordScreen, TwoFactorChallengeScreen, ConfirmPasswordScreen, VerifyEmailScreen) are variations on the same pattern.
  4. configureRateLimiting() and the Fortify action bindings — where login throttling and AttemptToAuthenticate / CreateNewUser / ResetUserPassword live.

Your first use case, verbatim: get /admin/login rendering an Orchid-styled form that posts to Fortify's login route, and confirm a valid user lands inside the panel. Nothing else. That single flow validates the guard wiring, the view binding, and the layout assumption all at once. If it works, the remaining flows are mechanical; if it doesn't, you've spent an hour instead of a sprint.

Before you wire anything, grep the template's Screen base class against your pinned Orchid version. Orchid's Screen/Layout API has moved across majors and templates characteristically lag behind.

Implementation Patterns

Bind Fortify views to Orchid Screens, and centralise every binding in one provider. Fortify resolves its entry points through Fortify::*View() callbacks; the template's trick is returning an Orchid Screen (or a minimal Blade shell that renders the Screen inside the panel layout) rather than a stock Blade view. Keep them all in one FortifyServiceProvider so the wiring is greppable:

// pattern: every Fortify view name resolves to an Orchid Screen
Fortify::loginView(fn () => (new LoginScreen())->build());
Fortify::resetPasswordView(fn ($request) => (new ResetPasswordScreen($request))->build());

One Screen per flow, small and flow-named. Build fields with Orchid's form builder (Input::make('email'), Password::make('password')) inside layouts() so styling and validation-error rendering come free through Orchid's TD/Layout machinery. Hand-written Blade inside a Screen is a smell — you've lost the reason you chose Orchid.

Keep Fortify's headless backend; replace only the surface. Customise through Fortify actions, not by editing the template's controllers:

// app/Providers/FortifyServiceProvider.php
Fortify::authenticateUsing(fn ($request) => /* custom lookup */);
Fortify::createUsersUsing(CreateNewUser::class);
Fortify::updateUserProfileInformationUsing(UpdateUserProfileInformation::class);
Fortify::updateUserPasswordsUsing(UpdateUserPassword::class);
Fortify::resetUserPasswordsUsing(ResetUserPassword::class);

The template shows the shape; your app-specific rules belong in app/Actions/Fortify/ where they're reviewable independent of the UI.

Align the guard explicitly. config/fortify.php → guard must match config/platform.php → guard (typically web, session driver). This is the single most common silent failure: valid credentials, no error, bounced back to the login screen.

Sequence the flows — do not port all of them at once. The dependency order that works:

  1. Login + logout.
  2. Password reset (needs password_reset_tokens migrated and a broker configured in config/auth.php).
  3. Email verification (the signed URL must point at an Orchid route — not Fortify's default /email/verify).
  4. 2FA last — it touches session state (login.id), recovery codes, and the challenge redirect, and it's the flow most likely to need custom work.

Enable features deliberately, and confirm a screen exists for each. In config/fortify.php:

'features' => [
    Features::registration(),
    Features::resetPasswords(),
    Features::emailVerification(),
    Features::updateProfileInformation(),
    Features::updatePasswords(),
    Features::twoFactorAuthentication([
        'confirm' => true,
        'confirmPassword' => true,
    ]),
],

Every enabled flag needs a reachable Screen in the template. A flag with no corresponding screen is a 500 the first time a real user hits that path — usually in production, because nobody exercises "the other" auth flows in staging.

Fold 2FA enrolment into your existing Orchid Profile screen rather than replacing it. Read the template's profile Screen diff, then port only the enable/disable/QR/recovery-code fields into a subclass of your profile Screen. You keep Orchid's profile behaviour and gain 2FA without forking an entire screen you'll now have to maintain across Orchid upgrades.

Throttling belongs in Fortify's provider, and is part of your auth review surface:

RateLimiter::for('login', fn (Request $request) => Limit::perMinute(5)
    ->by($request->input('email').'|'.$request->ip()));

Auditors ask about this. Treat the limiter definition as first-class code, not configuration.

Testing pattern: because view names are rebound to Screens, tests asserting view() names will break. Assert routes and Screen classes instead, and author feature tests for exactly the flows you would otherwise be trusting upstream on: throttled login, successful 2FA challenge, recovery-code consumption, password-reset token expiry. There is no CI in the upstream repo — that coverage is yours to write.

Gotchas and Tips

  • Route collision is the day-one failure. Fortify registers /login, /logout, /two-factor-challenge; Orchid owns /admin/*. Decide explicitly: either set fortify.prefix to admin (everything lives under the panel) or keep Fortify's routes and have the panel redirect to them. Running both unmodified yields redirect loops or a login 404, and the symptom is indistinguishable from a guard mismatch until you route:list.
  • View bindings must resolve at request time. A Fortify::loginView() pointing at a Blade view that doesn't exist — or at a Screen that throws inside build() — surfaces as a hard error only on /login, which is easy to miss until you log out in a hurry.
  • verified middleware needs the verification routes wired too. If you gate the panel on verified, confirm the signed verification URL lands on an Orchid route. Otherwise users are permanently locked out with no UI path to complete verification.
  • 2FA lockout is a support incident, not a bug ticket. Confirm recovery codes are actually surfaced to the user (not just generated) and that you have an admin-side reset — a column wipe or a console command. If you're migrating from Orchid's native 2FA, migrate secrets and recovery codes as a named work item with a rollback plan; skipping this logs users out permanently.
  • Don't run two auth stacks. Fortify + Orchid's native auth + anything Breeze-shaped produces divergent password-reset behaviour and intermittent guard bugs that are miserable to reproduce. Pick one authoritative guard before you start and remove the others.
  • Pin Orchid, Fortify, and Laravel explicitly. Classify any Orchid major upgrade as a planned migration — Screen/Layout APIs have broken across majors, and the failure mode is rendering, which often only appears in staging.
  • Assets. Fortify ships none, but Orchid's need publishing (php artisan vendor:publish --tag=orchid-assets, or orchid:install). Auth screens that look correct locally and broken in production are almost always an unpublished-asset problem.
  • Debugging order: php artisan route:list | grep -E 'login|two-factor' → confirm config/fortify.php guard matches config/platform.php → dd() inside the Fortify::loginView callback to confirm which Screen is actually resolving → check session driver parity between local and staging.
  • Extension points worth knowing: Fortify actions (CreateNewUser, AttemptToAuthenticate, ResetUserPassword, UpdateUserProfileInformation, UpdateUserPassword) for behaviour; Fortify::redirects() for post-login destinations inside the panel; RateLimiter::for('login', ...) for throttling. Everything else in the template is presentation — edit it freely.
  • Upstream has no tests, no CI, and no triage. Budget your own review time on the auth boundary before shipping, and make sure at least two engineers can trace the login → 2FA → session flow end to end. The template saves roughly two to three days of screen-building and creates a permanent, small ownership obligation. That trade is favorable on Orchid, and not worth considering anywhere else.
  • SSO is out of scope. Fortify does not do SAML, OIDC, or social login. If enterprise SSO is on the roadmap, it belongs to Socialite, an OIDC client, or a hosted IdP and must be sized separately — do not let this template appear to solve it.
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.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle