Skip to content

Authentication

Flowstate uses passwordless authentication for all user logins and OpenID Connect for admin access. This approach eliminates password-related vulnerabilities while providing a seamless user experience.

Users authenticate by receiving a magic link via email. The flow is simple:

  1. User enters their email address on the login page
  2. System generates a single-use token and sends it via email
  3. User clicks the link in their email
  4. System validates the token and creates an authenticated session
  5. User is redirected to the canvas

Magic links expire in 15 minutes. After expiration, users must request a new link. This short window limits the risk of intercepted or forwarded links being used maliciously.

Traditional password authentication introduces several risks:

  • Password reuse: Users often reuse passwords across services, so a breach elsewhere compromises your platform
  • Weak passwords: Users choose memorable passwords that are easy to guess or crack
  • Credential stuffing: Attackers use leaked credentials from other breaches to try logging in
  • Phishing: Users can be tricked into entering passwords on fake login pages
  • Storage vulnerabilities: Even hashed passwords can be cracked if the hashing algorithm is weak or the database is compromised

Magic links eliminate all of these risks. There’s no password to reuse, no password to guess, and no password to store. The only attack vector is email account compromise, which is outside the platform’s control and typically protected by the email provider’s security measures.

Each magic link contains a cryptographically random token that’s:

  • Single-use: Once validated, the token is immediately invalidated
  • Time-limited: Tokens expire in 15 minutes
  • Unpredictable: Generated using a secure random number generator
  • Hashed before storage: The database stores a hash of the token, not the token itself

If an attacker intercepts a magic link, they have a 15-minute window to use it. Once used or expired, the link becomes worthless. The short expiration window and single-use nature make magic links significantly more secure than long-lived passwords.

Magic link requests are rate-limited to 10 per minute per email address. This prevents:

  • Email bombing: Attackers can’t flood a user’s inbox with login emails
  • Enumeration attacks: Attackers can’t rapidly test which email addresses have accounts
  • Resource exhaustion: The email sending service isn’t overwhelmed by malicious requests

The rate limit applies to the email address, not the IP address. This prevents shared-IP false positives (like users on corporate networks or VPNs) while still protecting against abuse.

Once authenticated via magic link, users receive a JWT (JSON Web Token) that represents their session. The token contains:

  • User ID: Identifies the authenticated user
  • Email: User’s email address
  • Issued at: When the token was created
  • Expiry: When the token becomes invalid (7 days from issuance)

Tokens are signed using a secret key known only to the backend. This signature prevents tampering. If an attacker modifies the token (for example, changing the user ID), the signature becomes invalid and the backend rejects the token.

Tokens expire after 7 days. This limits the window of opportunity if a token is stolen. However, requiring users to re-authenticate every 7 days would be disruptive.

To balance security and usability, tokens automatically renew when they have less than 2 days remaining. The renewal process:

  1. User makes an authenticated request with a token that expires in less than 2 days
  2. Backend validates the token and processes the request
  3. Backend generates a new token with a fresh 7-day expiry
  4. Backend includes the new token in the response (via Set-Cookie header for web, or response body for desktop)
  5. Client stores the new token and uses it for subsequent requests

This approach provides long-lived sessions without compromising security. If a token is stolen, it’s only valid for a maximum of 7 days. If the legitimate user is actively using the platform, their token renews regularly and the stolen token becomes outdated.

Tokens are stored differently depending on the client:

Web Application:

  • Stored in an httpOnly cookie
  • Cookie is secure (HTTPS-only in production)
  • Cookie has sameSite=lax to prevent CSRF attacks
  • JavaScript cannot access the cookie (prevents XSS token theft)

Desktop Application:

  • Stored in Electron’s secure storage (encrypted at rest)
  • Sent via Authorization header: Authorization: Bearer <token>
  • Never exposed to the renderer process (main process handles it)

RPC Gateway:

  • Desktop app sends token via Authorization header
  • Gateway validates the token before processing RPC calls
  • Token is never logged or exposed in error messages

Each login creates a tracked session in the database. Sessions record:

  • User ID: Who the session belongs to
  • Device info: User agent string (browser/OS/device type)
  • IP address: Where the session was created
  • Created at: When the session started
  • Last active: When the session was last used
  • Token hash: Hashed version of the JWT token

Sessions enable several security features:

  • Active session listing: Users can see all their active sessions
  • Remote logout: Users can revoke sessions from other devices
  • Suspicious activity detection: Unusual login locations or devices can trigger alerts
  • Session expiry: Sessions can be forcibly expired if compromised

Session tokens are hashed before storage using a one-way hash function. If the database is compromised, attackers can’t use the stored hashes to impersonate users. They would need the original token, which only exists in the user’s browser or desktop app.

The desktop app supports a “request on one device, approve on another” flow:

  1. User opens the desktop app on a new device
  2. Desktop app generates a login request ID and displays a QR code
  3. User scans the QR code with their phone (where they’re already logged in)
  4. Phone app approves the login request
  5. Desktop app polls the backend for approval status
  6. Once approved, desktop app receives a token and authenticates

This flow enables:

  • Seamless multi-device setup: No need to enter email or click magic links on devices without easy email access
  • Secure approval: The approving device must already be authenticated
  • Time-limited requests: Login requests expire in 5 minutes

Login requests are stored in Redis with a short TTL (time to live). Once approved or expired, they’re automatically deleted.

Admin users authenticate through Amazon Cognito using OpenID Connect (OIDC). This provides:

  • Enterprise identity management: Integrates with corporate SSO and identity providers
  • Multi-factor authentication: Cognito supports MFA for admin accounts
  • Centralized user management: Admins are managed in Cognito, not the application database
  • Audit trails: Cognito logs all authentication events

The OIDC flow:

  1. Admin clicks “Admin Login” on the login page
  2. Browser redirects to Cognito’s hosted UI
  3. Admin authenticates with Cognito (username/password + MFA)
  4. Cognito redirects back to the application with an authorization code
  5. Backend exchanges the code for an ID token and access token
  6. Backend validates the ID token and creates an admin session
  7. Admin is redirected to the admin dashboard

ID tokens contain the admin’s email and Cognito user ID. The backend uses the email to look up the admin’s role and permissions in the application database.

Super admin privileges are granted automatically to whitelisted email addresses. The whitelist is stored in an environment variable:

When an admin authenticates via Cognito, the backend checks if their email is in the whitelist. If so, they’re automatically promoted to super_admin role. This ensures the right people have full platform access from day one, without manual role assignment.

Super admins can promote other users to admin roles through the admin dashboard. All role changes are logged in the admin audit trail.

Users can delete their accounts, but the process requires email confirmation to prevent accidental deletions:

  1. User clicks “Delete Account” in settings
  2. System sends a confirmation email with a single-use token
  3. Token expires in 30 minutes
  4. User clicks the link in the email
  5. System validates the token and permanently deletes the account

The confirmation token is:

  • Single-use: Once validated, the token is invalidated
  • Time-limited: Expires in 30 minutes
  • Hashed before storage: Database stores a hash, not the token itself
  • Tied to the user: Only valid for the user who requested deletion

If the user doesn’t click the confirmation link within 30 minutes, the deletion request is cancelled and the account remains active.

Account deletion is permanent and irreversible. All user data (canvas positions, sessions, uploaded files, etc.) is deleted. The user’s email address is also removed, so they can create a new account with the same email if desired.

Authentication errors are handled carefully to avoid leaking information:

Magic Link Requests:

  • Always return success, even if the email doesn’t exist
  • This prevents enumeration attacks (testing which emails have accounts)
  • Users receive an email if the account exists, nothing if it doesn’t

Token Validation:

  • Invalid tokens return 401 Unauthorized
  • Error message is generic: “Invalid or expired token”
  • No details about why the token is invalid (prevents information leakage)

Rate Limiting:

  • Exceeded rate limits return 429 Too Many Requests
  • Response includes Retry-After header with seconds until reset
  • Error message explains the rate limit and when to retry

Session Errors:

  • Expired sessions return 401 Unauthorized
  • Client is redirected to the login page
  • No details about why the session expired (could be timeout, logout, or revocation)

Generic error messages prevent attackers from learning about the system’s internal state. For example, if an error message said “Token signature is invalid,” an attacker would know the token format is correct but the signature is wrong. A generic “Invalid token” message reveals nothing.

Flowstate’s authentication system provides:

  • No password storage (magic links eliminate password vulnerabilities)
  • Short-lived magic links (15-minute expiry limits attack window)
  • Single-use tokens (magic links and deletion tokens can’t be reused)
  • Automatic token renewal (seamless long-term sessions without compromising security)
  • httpOnly cookies (prevents XSS token theft on web)
  • Secure token storage (encrypted at rest on desktop)
  • Session tracking (users can see and revoke active sessions)
  • Rate limiting (prevents brute force and enumeration attacks)
  • Admin MFA (Cognito supports multi-factor authentication)
  • Audit logging (all admin authentication events are logged)
  • Confirmation flows (account deletion requires email confirmation)
  • Generic error messages (prevents information leakage)

Planned improvements to the authentication system:

  • WebAuthn support: Passwordless authentication using biometrics or security keys
  • Social login: OAuth integration with Google, GitHub, and Microsoft
  • Session anomaly detection: Alert users when sessions are created from unusual locations or devices
  • Forced re-authentication: Require re-authentication for sensitive actions (like changing email or deleting account)
  • Session expiry policies: Configurable session timeouts for different user types
  • Login notifications: Email users when new sessions are created

Authentication security is an ongoing focus. The platform evolves to incorporate new standards and best practices as they emerge.