The Northwestern Laravel Starter provides a multi-method authentication system that handles Northwestern users (via NetID/SSO) and external collaborators (via passwordless verification codes), and integrations through OAuth credentials issued by Laravel Passport.
Sign-in belongs to the App panel. Guests who open a page in any panel are sent to /app/login, which lists the sign-in methods this application has configured:
Sign In with NetID appears when single sign-on is configured. It uses Online Passport (WebSSO) when that is configured, otherwise Entra ID.
Sign In with Email appears when LOCAL_AUTH_ENABLED=true.
Sign In As appears in the local environment only. See Signing In Locally.
If none is available, the page explains that no sign-in methods are available.
When NetID is the only method, /app/login skips the page and goes straight to single sign-on. The exceptions are the ci environment, where the page always renders so end-to-end tests can sign in with email codes, and local, where it offers Sign In As.
Email sign-in is a single page, /app/login/email, with two steps: request a code, then enter it. Progress is kept in the session, so reloading the page returns to the current step.
Which methods are offered, and the step every method ends with, are decided in App\Domains\Auth\SignIn. Every sign-in finishes with SignIn::complete(): a new session and CSRF token, a login record, and the page the person asked for, or /. An email code sign-in is remembered past the session (Laravel’s remember cookie), since a new code costs a trip to the inbox; single sign-on and Sign In As aren’t, since signing in again is usually silent or one click. SignInMethod::remembers() is where to change that.
A local environment needs no SSO, email or credentials to sign in. In the local environment, the sign-in page has a Development section with a Sign In As… menu of the seeded demo users, each shown with its role:
User
Signs in as
nuit.admin
NUIT Administrator, a Super Administrator
generic.user
A Northwestern user with no extra roles
partner.user
A local (email) account, when LOCAL_AUTH_ENABLED=true
Each menu item is a link to /app/login/as/{username}, which signs in as that user, records the sign-in like any other, and redirects to / (or the page you asked for). Agents and scripts can use the URL directly, at any host or port the application is served on.
The users come from DemoUserSeeder, which db:rebuild runs. Add the users you seed for specific roles to its SIGN_IN_AS list and they appear here too. API users can’t be signed in as.
To test real NetID sign-in locally, configure Entra ID or Online Passport (see Installation). The Azure redirect URI must match the host you use.
Every sign-in path ends at /. HomeController shows guests the landing page and sends signed-in users to destinationFor(), which returns the App panel by default. A user who started from a specific page, for example a link in an email, is returned to that page instead.
To change where signed-in users go, override destinationFor():
Northwestern users authenticate via single sign-on using their NetID credentials. The starter supports two SSO providers, Microsoft Entra ID (OAuth2) and Online Passport (agentless WebSSO via ForgeRock), and auto-detects which one to use based on your configured credentials. See the WebSSO / Entra ID documentation for provider-specific details.
User attempts to access a route requiring authentication
Redirect to SSO
The application sends the guest to /app/login. When email sign-in is off, that page redirects straight to Northwestern’s authentication service; otherwise the user picks Sign In with NetID there
User authenticates
User enters NetID and password (+ MFA if enabled)
Callback with token
SSO provider redirects back with authentication token
User provisioning
Application creates/updates user record from Directory Search data
Administrator creates a local user account in the Filament panel
Verification code sent
The user requests a code on /app/login/email, or a super administrator (ManageAll) sends one with Send Verification Code on the user’s page (or the Send a verification code now option when creating the user)
User receives email
The email contains the code (valid for 10 minutes by default, LOCAL_AUTH_CODE_EXPIRES_MINUTES) and an Enter your code button. The button opens /app/login/email at the code step for that challenge, so a code sent by an administrator or read on another device can be entered. The link alone does not sign anyone in, and an expired, used, locked or altered link is ignored.
User enters code
On /app/login/email, the code authenticates the user and SignIn::complete() establishes a session
Enter the email address, first and last name, job title and organization (all required), and an optional description
Select Send a verification code now if the user is ready to sign in. Otherwise they can request one themselves.
Create the user
CreateLocalUser generates a unique username and creates the local user as an affiliate. With sendLoginLink: true (the default), it also emails a verification code.
Every login creates a UserLoginRecord with a segment, a classification of the user at that moment. Segments feed the login analytics dashboard (charts, stats, filters) and are captured at login time so historical metrics remain accurate even if user roles change later.
Sign-in records are deleted after LOGIN_RECORD_RETENTION_DAYS (default 365). See Data Retention.
Authentication uses layered rate limits to prevent brute force attacks and abuse. Limits are applied per IP and per identifier (email or challenge ID) to balance security with usability. App\Domains\Auth\LoginCodes enforces the passwordless limits itself, along with the resend cooldown, because the email sign-in page’s Livewire actions don’t pass through route middleware.
Users with the ManageImpersonation permission can sign in as another user to see the application as they do. The Impersonate button on a user’s page in the administration panel posts to /impersonate/take/{id}. API users can’t be impersonated, and only a user with ManageAll can impersonate another user who has it.
While impersonating, both panels show the theme’s impersonation banner, whose leave button posts to /impersonate/leave and returns to the original account. Both routes are in routes/auth.php and share the impersonation rate limit.
Each start is recorded in the impersonation log, which is kept forever unless IMPERSONATION_LOG_RETENTION_DAYS is set. See Audit Logging.
For information on roles, permissions, and policies after a user is authenticated, see the Authorization documentation.