Authentication Modes
Porta supports multiple authentication methods that can be configured per organization and per client. This page covers all login methods, password-login two-factor authentication options, and how they are configured and enforced.
Overview
Porta provides a layered authentication system:
| Layer | Methods | Description |
|---|---|---|
| Primary authentication | Password, Magic Link | How users prove their identity |
| Password-login 2FA | Email OTP, TOTP, Recovery Codes | Additional verification after a password |
The 2FA policy applies only when a user signs in with a password. A validated magic link is already a complete passwordless proof, so Porta does not add an email OTP or TOTP prompt after it.
Login Method Matrix
| Configuration | Login Page Shows | Use Case |
|---|---|---|
[password, magic_link] | Password form + magic link button + forgot password | Default — maximum flexibility |
[password] | Password form + forgot password only | Traditional enterprise apps |
[magic_link] | Email input + magic link button only | Passwordless-first experience |
Password Authentication
Traditional email + password authentication with enterprise-grade security.
How It Works
Password Security
| Feature | Implementation |
|---|---|
| Hashing algorithm | Argon2id (winner of the Password Hashing Competition) |
| Password validation | NIST SP 800-63B compliant |
| Minimum length | 8 characters (configurable) |
| Breach detection | Checks against known breached passwords |
| Fixed verification work | One account or dummy Argon2id verification per valid-shaped attempt |
| Rate limiting | Redis-backed per-email rate limiting on login attempts |
Password Reset Flow
When password login is enabled, users can reset forgotten passwords:
Password-reset requests do not perform account lookup, token creation, or SMTP delivery in the public request. A bounded worker performs that work after the generic response has been returned. Retries reuse the same job-owned artifact; an ambiguous SMTP outcome can deliver the same link again, but cannot create a second active reset token for that job.
Magic Link Authentication
Passwordless authentication via secure one-time email links. Users click a link in their email to log in — no password needed.
How It Works
Magic Link Security
| Feature | Implementation |
|---|---|
| Token generation | Cryptographically secure random tokens |
| Token expiry | Configurable (default: 15 minutes) |
| Single use | Tokens are invalidated after first use |
| Rate limiting | Prevents email flooding attacks |
| User enumeration protection | Same response regardless of whether email exists |
| Tenant and session binding | Link authority is fixed to one tenant and optional OIDC interaction |
The callback route treats its organization and interaction values as untrusted transport input. They must match the authority stored when the link was issued. A mismatch returns the same generic failure as an invalid or expired link and does not consume the artifact. Once the database transaction succeeds, the link stays consumed even if the short-lived Redis continuation cannot be created; the user must request a new link rather than replaying the committed one.
When to Use Magic Link
Magic link is ideal for:
- Consumer applications where users dislike remembering passwords
- Internal tools where email access implies authorization
- Mobile-first experiences where typing passwords is cumbersome
- Low-friction onboarding where you want to minimize signup steps
Password-Login Two-Factor Authentication (2FA)
After successful password authentication, Porta can require a second factor. This policy does not run after magic-link authentication.
Email OTP
A 6-digit one-time password sent to the user's email.
| Feature | Detail |
|---|---|
| Code format | 6 numeric digits |
| Delivery | Email via configured SMTP |
| Expiry | Configurable (default: 10 minutes) |
| Max active codes | 3 per user (prevents flooding) |
| Template | Customizable via emails/otp-code.hbs |
User flow:
- User completes password login
- Porta generates a 6-digit code and stores it in the database
- Code is emailed to the user
- User enters the code on the verification page
- Porta validates the code (not expired, not used)
- Authentication is complete
TOTP (Authenticator Apps)
Time-based One-Time Password using authenticator apps like Google Authenticator, Authy, Microsoft Authenticator, or any TOTP-compatible app.
| Feature | Detail |
|---|---|
| Algorithm | TOTP (RFC 6238) |
| Code format | 6 numeric digits, 30-second window |
| Setup | QR code scanning or manual secret entry |
| Secret storage | AES-256-GCM encrypted in PostgreSQL |
| Verification | Time-window tolerance (±1 step) |
Setup flow:
- User navigates to 2FA setup
- Porta generates a TOTP secret and encrypts it with AES-256-GCM
- QR code is displayed for scanning with an authenticator app
- User enters a verification code from their app to confirm setup
- Recovery codes are generated and displayed (one-time view)
Login flow:
- User completes password login
- Porta detects TOTP is configured for this user
- User enters the current 6-digit code from their authenticator app
- Porta verifies the code against the encrypted secret
- Authentication is complete
Recovery Codes
One-time backup codes for account recovery when the primary 2FA method is unavailable (lost phone, no email access).
| Feature | Detail |
|---|---|
| Format | 8-character alphanumeric codes with dash (e.g., A1B2-C3D4) |
| Count | 10 codes generated per setup |
| Storage | Argon2id hashed (not stored in plain text) |
| Usage | Each code can be used exactly once |
| Case-insensitive | a1b2-c3d4 matches A1B2-C3D4 |
| Dash-insensitive | A1B2C3D4 matches A1B2-C3D4 |
Important
Recovery codes are shown only once during 2FA setup. Users should save them in a secure location. If all recovery codes are used and the primary 2FA method is lost, an admin must manually disable 2FA for the user.
Per-Organization Configuration
Each organization has a defaultLoginMethods setting. Clients configured to inherit login methods use this non-empty selection.
Setting Organization Login Methods
Via CLI:
# Enable both password and magic link (default)
porta org update <id-or-slug> --login-methods password,magic_link
# Password only
porta org update <id-or-slug> --login-methods password
# Magic link only
porta org update <id-or-slug> --login-methods magic_linkVia Admin API:
curl -X PUT https://porta.local:3443/api/admin/organizations/<org-id> \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "defaultLoginMethods": ["password", "magic_link"] }'2FA Organization Policy
Password-login 2FA enforcement is configured per organization:
| Policy | Password-login behavior |
|---|---|
optional | Use a user's enrolled method when enabled; otherwise continue |
required_email | Require an email one-time password |
required_totp | Require authenticator/TOTP enrollment and verification |
required_any | Require an available email OTP or TOTP method |
The embedded Admin UI edits this policy on the selected organization's Authentication tab. The SDK exposes the same resource through porta.twoFactor.getPolicy(organizationId) and porta.twoFactor.setPolicy(organizationId, policy). The policy endpoint uses admin:org:update; the Admin UI reloads the displayed policy after a failed partial save and does not retry automatically.
Per-Client Overrides
Individual clients can override the organization's default login methods. This allows different applications within the same organization to offer different login experiences.
Setting Client Login Methods
Via CLI:
# Override to password only for this client
porta client update <client-id> --login-methods password
# Override to magic link only
porta client update <client-id> --login-methods magic_link
# Clear override (inherit from organization)
porta client update <client-id> --clear-login-methodsVia Admin API:
# Set override
curl -X PUT https://porta.local:3443/api/admin/clients/<client-id>/login-methods \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "loginMethods": ["password"] }'
# Clear override (inherit from org)
curl -X DELETE https://porta.local:3443/api/admin/clients/<client-id>/login-methods \
-H "Authorization: Bearer $TOKEN"Resolution Logic
When a user initiates a login, Porta determines the available login methods using this resolution logic:
Enforcement Points
Login methods are enforced at five endpoints, before any user lookup or CSRF validation:
GET /interaction/:uid— Login page renderingPOST /interaction/:uid/magic-link— Magic link requestGET /interaction/:uid/forgot-password— Password reset formPOST /interaction/:uid/forgot-password— Password reset submissionPOST /interaction/:uid/reset-password— New password submission
If a user attempts to use a disabled login method, Porta responds with:
- HTTP 403 status code
- Audit event:
security.login_method_disabled - Error message explaining the method is not available
Security Considerations
Rate Limiting
All authentication endpoints are rate-limited to prevent brute-force attacks:
| Endpoint | Limit | Window |
|---|---|---|
| Password login | Configurable | Per email + org |
| Magic link request | Configurable | Per email + org |
| 2FA code verification | Configurable | Per user |
| Password reset request | Configurable | Per email |
Audit Logging
Every authentication event is logged for security monitoring:
| Event | Logged Data |
|---|---|
auth.login.success | User ID, method, client, org, IP |
auth.login.failure | Email attempted, failure reason, org, IP |
auth.magic_link.sent | Email, org, IP |
auth.magic_link.verified | User ID, org |
auth.2fa.verified | User ID, method (otp/totp/recovery) |
auth.2fa.failed | User ID, method, failure reason |
auth.password_reset.requested | Email, org |
auth.password_reset.completed | User ID, org |
security.login_method_disabled | Attempted method, client, org |
Enumeration-Resistant Operations
Porta does not use wall-clock timing thresholds as a security guarantee. Instead, every admitted password attempt performs one Argon2id verification: an eligible account hash is used when one exists, otherwise Porta verifies a process-cached Argon2id dummy hash. Failed attempts also use the same persistence-operation shape and return the same generic public response.
Magic-link and password-reset requests enqueue tenant-bound recovery work before returning the generic response. Account lookup, token creation, and email delivery happen later in a bounded worker. Requests for absent or ineligible accounts complete as private no-ops, so the public request path does not reveal account state through account-dependent token or SMTP work.
Next Steps
- Capabilities Overview — Full feature list
- Custom UI Tutorial — Customize login pages and emails
- OIDC & Authentication — OIDC protocol details
- Two-Factor Authentication — 2FA concept details
- Login Methods — Login method configuration details