Authentication
Unfold CMS uses Laravel Fortify for authentication, providing a secure login system with two-factor authentication, social login, email verification, and rate limiting.
Login
Standard Login
Users log in with their email and password at /login. The login form includes:
- Email field
- Password field
- "Remember Me" checkbox
- Link to password reset
Rate Limiting
Login attempts are rate-limited to prevent brute force attacks:
| Limit | Rate |
|---|---|
| Login attempts | 5 per minute |
| Password reset | 3 per minute |
After exceeding the limit, users must wait before trying again. The lockout duration is communicated through error messages.
Two-Factor Authentication (2FA)
Unfold CMS supports TOTP-based two-factor authentication powered by Laravel Fortify.
Enabling 2FA
- Navigate to Settings > Security in the user's profile
- Click Enable Two-Factor Authentication
- Scan the QR code with an authenticator app (Google Authenticator, Authy, etc.)
- Enter the verification code to confirm
- Save the recovery codes in a safe place
Login with 2FA
When 2FA is enabled:
- User enters email and password
- After successful credential check, a 2FA challenge is presented
- User enters the 6-digit code from their authenticator app
- Access is granted
Recovery Codes
During 2FA setup, recovery codes are generated. These can be used if the authenticator app is unavailable. Each recovery code can only be used once.
Users can regenerate recovery codes from their security settings.
Email Verification
New user accounts can require email verification before full access is granted.
Email verification can be toggled in Settings > Authentication in the admin panel.
Verification Flow
- User registers or is created with an unverified email
- A verification email is sent with a signed link
- User clicks the link to verify
- The
email_verified_attimestamp is set
Unverified users can log in but may have restricted access depending on your middleware configuration.
Resending Verification
Users can request a new verification email from their profile or the verification notice page.
Password Reset
Reset Flow
- User clicks "Forgot Password?" on the login page
- Enters their email address
- Receives a password reset email with a signed, time-limited link
- Clicks the link and enters a new password
- Password is updated and user can log in
Password reset links expire after 60 minutes.
Password Change
Authenticated users can change their password from Settings > Password. They must provide their current password to confirm the change.
When a password is changed, a password_changed notification is sent (if notifications are enabled).
Social Login
Pro Feature — Social login is available in the Pro and Agency tiers.
Unfold CMS supports OAuth-based social login through Laravel Socialite. Visitors can sign in with one click instead of creating an email/password account.
Supported Providers
Google, GitHub, and Facebook are supported. Enable providers and enter their credentials in Settings > Authentication > Social Login in the admin panel.
Callback URL Pattern
Every provider needs the same callback URL pattern:
https://yourdomain.com/auth/{provider}/callback
So Google's callback is /auth/google/callback, GitHub's is /auth/github/callback, Facebook's is /auth/facebook/callback.
Setup — Google
- Go to Google Cloud Console → pick or create a project
- Open APIs & Services → OAuth consent screen and fill it in (External user type, app name, support email, authorized domain). You don't need to publish it for testing.
- Open APIs & Services → Credentials → + Create Credentials → OAuth Client ID
- Application type: Web application
- Authorized JavaScript origins:
https://yourdomain.com - Authorized redirect URIs:
https://yourdomain.com/auth/google/callback - Click Create — copy the Client ID (
xxxxx.apps.googleusercontent.com) and Client Secret (GOCSPX-xxxxx) - In the CMS admin:
/admin/settings→ Authentication tab → toggle Enable Social Login ON → toggle Enable Google Login ON → paste Client ID + Secret → Save
Setup — GitHub
GitHub is the easiest of the three.
- Go to GitHub Developer Settings → OAuth Apps → New OAuth App
- Application name: your site name
- Homepage URL:
https://yourdomain.com - Authorization callback URL:
https://yourdomain.com/auth/github/callback - Click Register application. Copy the Client ID, then click Generate a new client secret and copy that (you only see it once).
- In the CMS admin:
/admin/settings→ Authentication tab → make sure Enable Social Login is ON → toggle Enable GitHub Login ON → paste Client ID + Secret → Save
Setup — Facebook
Facebook is the most involved, but basic email + profile login works without app review.
- Go to Facebook for Developers → Create App
- Use case: Authenticate and request data from users → type: Consumer
- Fill in app name + contact email → Create
- On the app dashboard, find Facebook Login → Set up → pick Web platform
- Site URL:
https://yourdomain.com. Skip the quickstart. - Go to Facebook Login → Settings in the left sidebar
- Valid OAuth Redirect URIs:
https://yourdomain.com/auth/facebook/callback - Save Changes
- Go to Settings → Basic → copy the App ID (this is your Client ID) → click Show next to App Secret and copy it
- While you're on Settings → Basic, also fill in App Domains:
yourdomain.comand your Privacy Policy URL (Facebook requires one) → Save Changes - Flip the App Mode switch from Development to Live (top of the dashboard)
- In the CMS admin:
/admin/settings→ Authentication tab → make sure Enable Social Login is ON → toggle Enable Facebook Login ON → paste App ID + App Secret → Save
Social Login Flow
- User clicks "Continue with Google" (or other provider)
- Redirected to the provider's OAuth consent screen
- User grants permission
- Redirected back to the CMS
- If the email matches an existing account, the provider is linked to that account and they're logged in
- If no match, a new account is created with the User role,
email_verified_atset (the provider already verified the email), andprovider/provider_id/provider_avatarpopulated
Verifying It Works
After saving credentials:
- Open an incognito window (you may still be logged in on your normal browser)
- Go to
https://yourdomain.com/login - The "Continue with Google" (or GitHub / Facebook) button should appear below the email/password form
- Click it → approve on the provider's screen → you should land back on your site signed in
- Check
/admin/users— the new row should have your email and theprovidercolumn should saygoogle/github/facebook
Common Issues
Button doesn't appear on /login — the master toggle Enable Social Login is off, or the provider-specific toggle is off.
"Invalid redirect URI" on the provider's screen — the URL in their console doesn't match https://yourdomain.com/auth/{provider}/callback exactly, including https:// vs http:// and trailing slashes.
Google shows "This app isn't verified" — normal for new apps. Click Advanced → Go to (your app). To remove the warning permanently, submit your app for Google's verification process (free, takes a few days, only needed at production scale).
Facebook says app is in development mode — flip the App Mode toggle to Live (Step 11 above).
GitHub returns "Bad credentials" — the Client Secret was regenerated after you saved it. Generate a fresh secret on GitHub, paste it into UnfoldCMS, save.
User already exists with that email — the provider is linked to the existing account; no duplicate is created. They can use either method going forward.
Invalid Provider Handling
If a user tries to authenticate with an unconfigured provider, they're redirected back with an error message.
Registration
Registration can be enabled or disabled in Settings > Authentication in the admin panel.
Registration Flow
- User visits
/register - Fills in name, email, and password
- Account is created with the User role
- Verification email is sent (if enabled)
- Welcome email is sent (if enabled)
Session Security
| Feature | Description |
|---|---|
| CSRF Protection | All forms include CSRF tokens |
| Session Encryption | Sessions are encrypted by default |
| Secure Cookies | Cookies use HttpOnly and Secure flags in production |
| Session Timeout | Sessions expire after the configured lifetime |
Rate Limits
All authentication endpoints are rate-limited:
| Endpoint | Limit |
|---|---|
| Login | 5 per minute |
| Password Reset | 3 per minute |
| Registration | 5 per minute |
| Email Verification | 5 per minute |
Auth Page Appearance
Templates can customize the look of authentication pages (login, register, forgot password, etc.) through settings in the admin panel at Settings > Template > Auth Pages.
Layouts
Three layout options are available:
| Layout | Description |
|---|---|
simple |
Centered form on a clean background (default) |
card |
Form inside a card component |
split |
Two-column layout with branding on one side and form on the other |
Branding Settings
These settings apply to all auth page layouts, but are most visible in the split layout:
| Setting | Description | Default |
|---|---|---|
| Branding Title | Large heading on the branding panel | "Unfold your story" |
| Branding Description | Subtext below the title | "The modern content management system..." |
| Background Image | Image for the branding panel (URL or media path) | None |
| Background Color | Gradient start color (used when no image) | #2563EB |
| Background Color End | Gradient end color | #1D4ED8 |
How It Works
Auth page appearance is controlled per-template. Settings are stored with the key pattern:
template.{template_name}.auth.layout
template.{template_name}.auth.branding_title
template.{template_name}.auth.branding_description
template.{template_name}.auth.background_image
template.{template_name}.auth.background_color
template.{template_name}.auth.background_color_end
The active template's auth settings are automatically passed to all auth pages via the authAppearance shared prop in the Inertia middleware.
For Template Developers
Auth pages use the AuthLayout component (layouts/auth-layout.tsx) which automatically selects the correct layout based on the template's settings. Your auth pages should wrap their content with this component:
import AuthLayout from '@/layouts/auth-layout';
export default function Login() {
return (
<AuthLayout title="Sign in" description="Enter your credentials">
{/* Form fields */}
</AuthLayout>
);
}
The AuthLayout component reads authAppearance from page props and renders the appropriate layout (AuthSimpleLayout, AuthCardLayout, or AuthSplitLayout).
Related
- Security Hardening — Additional security features
- User Management — Managing user accounts
- Roles & Permissions — Access control
- Templates Development — Building custom templates