skip to content

Accounts & Access Control

Laravel resolves the caller through guards, signs users in and resets passwords, then decides what they may do with gates and policies. Interviewers probe it because the two halves get conflated.

on this pageshow

explore

questions

page 1 of 2

In Laravel, what is the difference between a gate defined with Gate::define and a policy class, and when would you choose each?

level: juniorimportance: must knowfreq 72%

answer

  1. closure versus class
  2. defined in AppServiceProvider::boot()
  3. one-off, not tied to a model
  4. policy methods named after abilities
  5. resolved by the model you pass

basics

~20 s

A gate is a named closure registered with Gate::define for one-off checks not tied to a model. A policy is a class grouping one model's abilities (view, update, delete), found from the model you pass; most model rules belong there.

solid answer

~40 s

Both answer "may this user do X?" through the same `Gate` service, so every caller — `Gate::allows`, `Gate::authorize`, the `can` middleware, `@can`, `$user->can()` — works with either. A **gate** is a closure (or `[Class, 'method']`) registered by name, usually in `AppServiceProvider::boot()`: `Gate::define('publish-issue', fn (User $user) => $user->is_admin)`. A **policy** is a class such as `ArticlePolicy` whose method names are the abilities (`view`, `update`, `delete`); Laravel picks it from the **model or class passed** as the first argument. The docs call gates a way to learn the basics and recommend policies for robust apps. Rule of thumb: an action on a model — editing an article — goes in that model's policy; a global action with no model, like opening the admin dashboard, fits a gate.

code

php · 16 lines
php
<?php

namespace App\Providers;

use App\Models\User;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        // Global action, no model involved: a gate
        Gate::define('view-billing', fn (User $user) => $user->role === 'admin');
    }
}

go deeper

for a junior

Define each: a gate is a named closure, a policy is a class of abilities for one model, and both are checked through the Gate.

for a middle

Explain how the Gate picks a policy from the argument before falling back to a gate, and why an unmatched ability denies.

for a senior

Organise rules so model actions live in policies and global actions in a few gates, keeping the provider short and testable.

for a principal

Set the team convention for where authorization rules live, so reviewers can find every rule for a model in one place.

## One authorization service, two ways to register rules Laravel's authorization layer is the `Illuminate\Auth\Access\Gate` service behind the `Gate` facade. Every check — `Gate::allows()`, `Gate::authorize()`, `$user->can()`, the `can` middleware, `#[Authorize]`, `@can` — goes through it. What differs is **where the rule lives**. ## Gates A **gate** is a named callback: ```php Gate::define('open-editorial-dashboard', fn (User $user) => $user->role === 'admin'); Gate::define('publish-article', [ArticlePolicy::class, 'publish']); ``` - Registered in `AppServiceProvider::boot()`. - The first parameter is always the current user; Laravel supplies it, and callers pass only the extra arguments. - Good for **global** actions not tied to a model: viewing a dashboard, exporting a report, toggling site maintenance. - Every gate lives in one provider, which becomes a long list as an app grows. ## Policies A **policy** is a class that groups a model's abilities: ```php class ArticlePolicy { public function update(User $user, Article $article): bool { return $user->id === $article->author_id || $user->role === 'editor'; } } ``` - Generated with `php artisan make:policy ArticlePolicy --model=Article`. - Method names **are** the ability names; `Gate::authorize('update', $article)` calls `update()`. - Laravel finds the policy from the argument: an `Article` instance or the class name `Article::class`. - Resolved through the service container, so constructor injection works. - Can hold a `before()` filter for the whole class. ## How the Gate chooses When you call `Gate::allows('update', $article)`: 1. global `Gate::before` callbacks run first; 2. if the first argument maps to a policy **and** that policy has a method for the ability, the policy's `before()` and then the method run; 3. otherwise a gate defined under that ability name runs; 4. if nothing matches, the result is `null`, which is treated as **deny**. So a policy method wins over a gate with the same name when a model is passed. ## Choosing, on a publishing platform | Rule | Where | |---|---| | An editor may update any article; a writer only their own | `ArticlePolicy::update` | | A reviewer may comment on articles assigned to them | `ArticlePolicy::comment` | | Only admins may open the billing screen | `Gate::define('view-billing', ...)` | | Admins may do everything | `Gate::before` or a policy `before()` | ## Common confusions - Gates and policies are not two security systems; they are two registration styles for one. - A policy does not need registering by hand when names follow the conventions (`App\Models\Article` → `App\Policies\ArticlePolicy`). - Defining a gate called `update` does not make it apply to articles when an `ArticlePolicy::update` exists — the policy is consulted first. - Neither runs by itself: something (a call, middleware, attribute or directive) must invoke the check. ## Growing from gates to policies Many apps start with a handful of gates and move to policies as models multiply. A clean migration: 1. Generate the policy: `php artisan make:policy ArticlePolicy --model=Article`. 2. Move each model-specific gate body into the matching method (`update-article` → `update`). 3. Change callers from `Gate::allows('update-article', $article)` to `Gate::allows('update', $article)`; the ability names become the method names. 4. Delete the old gates so there is one source of truth. 5. Keep genuinely global abilities (`view-billing`, `open-editorial-dashboard`) as gates. A halfway step is `Gate::define('update-article', [ArticlePolicy::class, 'update'])`, which points an old gate name at a policy method. ## What interviewers listen for - That both styles share the `Gate` and its `before`/`after` hooks. - That policies are found from the argument, so the model (or `Article::class`) must be passed. - That the documentation itself steers robust applications towards policies. - That an undefined ability denies rather than allows.

  • If both Gate::define('update', ...) and ArticlePolicy::update exist, which runs for Gate::allows('update', $article)?
    The policy. When the first argument maps to a policy that has an `update` method, the Gate resolves the policy callback before looking at gates registered under that name. Without a model argument, or if the policy lacks the method, the gate runs.
  • How does a gate or policy method get the current user?
    The Gate resolves the currently authenticated user through its user resolver and passes it as the first argument. Callers never pass the user; to check another user, use `Gate::forUser($user)` or `$otherUser->can(...)`.

A gate is a single rule on a sign by the door; a policy is the rulebook kept for one kind of room. The same doorman reads both, and when you name a room he opens its rulebook before looking at the signs.

saying these in an interview costs you the question

  • Gates and policies are two separate authorization systems
  • You must pass the current user to Gate::allows
  • Every policy has to be registered manually in a provider
  • A gate with the same name overrides the model's policy method
  • Policies run automatically on every model query
open as a page

In Laravel's config/auth.php, what is the difference between a guard and a user provider, and what ships by default?

level: juniorimportance: must knowfreq 55%

basics

~20 s

A guard decides how a request is authenticated and remembered; a user provider decides where user records are loaded from. The Laravel 13 skeleton ships one web guard (session driver) pointing at one users provider (eloquent, App\Models\User).

open as a page

In Laravel, how does a hand-built login use Auth::attempt, and why does the documented example call $request->session()->regenerate() afterwards?

level: juniorimportance: must knowfreq 70%

basics

~20 s

Auth::attempt($credentials) finds the user by the non-password keys, checks the password hash and, on success, signs them into the session, returning true or false. Regenerating the session ID defeats session fixation; Laravel 13's guard already does it in login().

open as a page

In Laravel, what must be in place for the verified middleware to keep users with unconfirmed email addresses out of a route?

level: juniorimportance: must knowfreq 50%

basics

~10 s

App\Models\User must implement Illuminate\Contracts\Auth\MustVerifyEmail, users needs an email_verified_at column, sign-up must dispatch Registered, the verification.notice, verification.verify and verification.send routes must exist, and routes use ['auth', 'verified'].

open as a page

What two authentication mechanisms does Laravel Sanctum provide, and which one suits a first-party SPA versus a mobile app?

level: juniorimportance: must knowfreq 68%

basics

~20 s

Sanctum offers cookie-based session authentication for a first-party SPA on a stateful domain, and database-backed personal access tokens sent as a Bearer header for mobile apps and scripts. Your own SPA uses the session; the mobile app uses a token.

open as a page

In Laravel, how do you add GitHub sign-in with Socialite, from the config/services.php entry to the callback route?

level: juniorimportance: must knowfreq 55%

basics

~10 s

Add a github entry with client_id, client_secret and redirect to config/services.php, then define two routes: one returning Socialite::driver('github')->redirect(), and a callback calling ->user() to get the provider user you map to a local account.

open as a page

In Laravel, what is Fortify, what does the features array in config/fortify.php control, and what does 'views' => false change?

level: juniorimportance: must knowfreq 45%

basics

~20 s

Fortify is Laravel's headless authentication backend: it registers login, registration, reset, verification, two-factor and passkey routes and controllers but ships no UI. The features array decides which route groups exist; 'views' => false removes the GET routes that return screens.

open as a page

In Laravel 13, how do Gate::authorize, #[Authorize], the can middleware, @can and $user->can differ in what they do when a check fails?

level: middleimportance: must knowfreq 60%

basics

~10 s

Gate::authorize, the can middleware and #[Authorize] throw AuthorizationException, which becomes a 403. $user->can and Gate::allows return a bool you must act on. @can only decides what Blade renders and protects nothing on the server.

open as a page

In Laravel, what should a logout action do besides calling Auth::logout(), and what do invalidate() and regenerateToken() each add?

level: middleimportance: must knowfreq 60%

basics

~20 s

Auth::logout() removes the guard's user from the session, forgets the remember-me cookie and cycles remember_token. Then invalidate() wipes all session data under a new ID, and regenerateToken() issues a fresh CSRF token, so nothing from the old session survives.

open as a page

In Laravel Passport 13, how do you declare scopes with Passport::tokensCan and enforce them with CheckToken versus CheckTokenForAnyScope?

level: middleimportance: must knowfreq 46%

basics

~10 s

Passport::tokensCan(['shipments:read' => 'View shipments', ...]) declares the scopes clients may request. CheckToken::using(...) requires every listed scope on the access token, CheckTokenForAnyScope::using(...) requires one; a missing scope throws MissingScopeException, rendered as 403.

open as a page

In Laravel, how does the Password broker carry a reset from Password::sendResetLink to Password::reset, and what does your own code still have to do?

level: middleimportance: must knowfreq 58%

basics

~20 s

Password::sendResetLink finds the user by email, stores a hashed token and mails a link. Password::reset re-checks email and token, runs your closure to save the new password, deletes the token, and both return a status string.

open as a page

In a Laravel 13 car-dealership app where sales staff and customers sign in separately, how would you configure a second admin guard, and what traps come with it?

level: seniorimportance: must knowfreq 50%

basics

~20 s

Add a staff provider (eloquent, App\Models\Staff) and an admin guard (session driver, provider staff) to config/auth.php, protect back-office routes with auth:admin, and name the guard explicitly in shared code. Both guards share one session under separate keys.

open as a page

For a Laravel API serving your own SPA and mobile app, when is Sanctum the right choice, and what requirement would push you to Passport?

level: seniorimportance: must knowfreq 60%

basics

~20 s

Sanctum fits first-party clients: session cookies for your SPA and database-backed personal access tokens for your mobile app. Choose Passport only when you must be an OAuth2 authorization server, for example so third-party apps can act for your users.

open as a page

When adding Fortify two-factor authentication to a Laravel tax-filing app, what do the confirm and confirmPassword options change, and how does login become two steps?

level: seniorimportance: must knowfreq 40%

basics

~20 s

With confirm => true, enabling stores a secret but login is not challenged until the user submits a valid code; confirmPassword => true puts password.confirm on the management routes. Login then stops at /two-factor-challenge before signing in.

open as a page

In a Laravel policy, why do viewAny and create receive only the user while view and update also receive a model, and how do you call each?

level: juniorimportance: should knowfreq 45%

basics

~20 s

viewAny and create decide about a whole kind of model, before any instance exists, so they take only the user; you call them with the class name, Gate::authorize('create', Article::class). view and update judge one record and receive it: Gate::authorize('update', $article).

open as a page

What steps set up Laravel Passport 13 in a fresh Laravel 13 app, and what does each step add?

level: juniorimportance: should knowfreq 35%

basics

~20 s

Run php artisan install:api --passport, which requires Passport and runs passport:install (keys, config, migrations, an optional personal access client). Then add HasApiTokens and OAuthenticatable to User, define an api guard with driver passport, and protect routes with auth:api.

open as a page

In Laravel, how does Gate::before let an admin bypass every authorization check, and why must it return null for everyone else?

level: middleimportance: should knowfreq 52%

basics

~20 s

Gate::before callbacks run before every gate and policy; any non-null return becomes the final answer. Return true for admins and null otherwise, because returning false, or a bool like $user->isAdmin(), denies every check for non-admins.

open as a page

How does Laravel find the policy for an Eloquent model, and which wins among Gate::policy, the #[UsePolicy] attribute and naming-convention discovery?

level: middleimportance: should knowfreq 40%

basics

~10 s

Laravel checks policies registered with Gate::policy first, then a #[UsePolicy] attribute on the model, then convention: ModelNamePolicy in a Policies directory at or above the model's, e.g. App\Policies\ArticlePolicy. Gate::guessPolicyNamesUsing() replaces the convention.

open as a page

In Laravel, how do Auth::guard() and Auth::shouldUse() decide which guard Auth::user() and $request->user() consult?

level: middleimportance: should knowfreq 40%

basics

~20 s

Without a name, Auth::user() and $request->user() use the guard in auth.defaults.guard, normally web. Auth::guard('admin') asks a named guard explicitly; Auth::shouldUse('admin') changes the default for the rest of the request, which the auth:admin middleware does itself.

open as a page

In Laravel's config/auth.php, what changes when a user provider uses the database driver instead of the eloquent driver?

level: middleimportance: should knowfreq 30%

basics

~20 s

An eloquent provider loads users through a configured model and returns that model; a database provider queries a configured table with the query builder and returns an Illuminate\Auth\GenericUser, which has no relationships, casts, accessors or can() helper.

open as a page

In Laravel, when would you sign a user in with Auth::login, loginUsingId, once or attemptWhen instead of Auth::attempt?

level: middleimportance: should knowfreq 40%

basics

~10 s

Use Auth::login($user) or loginUsingId($id) when identity is already proven, once($credentials) to authenticate one request without a session, and attemptWhen() when sign-in must also pass checks on the loaded user after the password matches.

open as a page

In Laravel, how does the password.confirm middleware work, and which routes must a hand-built app define for it?

level: middleimportance: should knowfreq 35%

basics

~20 s

The password.confirm middleware (RequirePassword) lets a request through only if the session's auth.password_confirmed_at is within auth.password_timeout, three hours by default. Otherwise it redirects to the route named password.confirm, whose POST handler checks the password and calls $request->session()->passwordConfirmed().

open as a page

In Laravel, how does remember me work with Auth::attempt, the remember_token column and the recaller cookie, and what does Auth::viaRemember() report?

level: middleimportance: should knowfreq 45%

basics

~20 s

Passing true as attempt()'s second argument stores a random remember_token and queues a long-lived encrypted cookie with the user ID, that token and a password-hash HMAC. When the session is gone the guard restores the user from it, and Auth::viaRemember() returns true.

open as a page

In a Laravel API behind a separate SPA, how do you make reset and verification emails link to the front end instead of Laravel routes?

level: middleimportance: should knowfreq 30%

basics

~10 s

Register URL builders in AppServiceProvider::boot(): ResetPassword::createUrlUsing(fn ($user, $token) => ...) returns a front-end URL with the token and email, and VerifyEmail::createUrlUsing(fn ($notifiable) => ...) must build and embed the signed verification URL itself.

open as a page

In Laravel's config/auth.php, what do a password broker's expire and throttle settings do, and how do the database and cache token drivers differ?

level: middleimportance: should knowfreq 38%

basics

~20 s

expire is how many minutes a reset token stays valid; throttle is how many seconds before the same account gets another link (both 60 in the skeleton). Database-driver rows need auth:clear-resets; cache-driver entries expire by TTL.

open as a page

With Laravel Sanctum, what does $user->createToken() store, what does it return, and how are token abilities attached and checked?

level: middleimportance: should knowfreq 45%

basics

~20 s

createToken($name, $abilities = ['*'], $expiresAt = null) saves a personal_access_tokens row with a SHA-256 hash of the secret and JSON abilities, and returns a NewAccessToken whose plainTextToken is shown once. tokenCan() checks the current token's abilities.

open as a page

With Laravel Sanctum SPA authentication, what do statefulApi(), SANCTUM_STATEFUL_DOMAINS and the /sanctum/csrf-cookie route each contribute to a logged-in API request?

level: middleimportance: should knowfreq 55%

basics

~10 s

statefulApi() adds EnsureFrontendRequestsAreStateful to the api group; SANCTUM_STATEFUL_DOMAINS lists the hosts whose Referer or Origin make a request stateful, gaining cookies, session and CSRF; /sanctum/csrf-cookie returns 204 and sets XSRF-TOKEN before login.

open as a page

In Laravel Socialite, how do scopes(), setScopes() and with() differ, and why can replacing GitHub's default scope leave getEmail() returning null?

level: middleimportance: should knowfreq 30%

basics

~20 s

scopes() adds to the driver's default scopes, setScopes() replaces them, and with() sets extra query parameters. The GitHub driver fetches the primary verified email only when user:email is requested, so setScopes() without it can leave getEmail() null.

open as a page

What does Laravel Socialite keep in the session between redirect() and user(), why does the callback throw InvalidStateException, and when is stateless() appropriate?

level: middleimportance: should knowfreq 42%

basics

~20 s

redirect() stores a random state (and, with enablePKCE(), a code_verifier) in the session; user() pulls the state and throws InvalidStateException when it is missing or differs. stateless() skips that check for sessionless APIs and gives up its protection.

open as a page

After php artisan fortify:install, which classes in app/Actions/Fortify does a Laravel app own, and how does Fortify call them?

level: middleimportance: should knowfreq 35%

basics

~10 s

fortify:install publishes CreateNewUser, ResetUserPassword, UpdateUserPassword, UpdateUserProfileInformation and a PasswordValidationRules trait into app/Actions/Fortify. They are your code; FortifyServiceProvider registers them with Fortify::createUsersUsing() and friends, and Fortify's controllers call them.

open as a page

showing 1–30 of 47