Leecore logo

Information

3 MIN READ

Building Login/Logout Functions Yourself: Security Aspects Often Overlooked

Think building Login/Logout is simple? This article provides a detailed A-Z security checklist (CSRF, 2FA,...) you must know before building your own secure authentication system.

Hi everyone,

Many of you have probably had this experience as a student. When working on a large group project, if we felt our coding skills weren't quite on par with the rest of the team, we'd often "volunteer" for the tasks that seemed "easy," like coding the Login/Logout feature. The thinking was simple: it's just a form, check the username/password, let them in if it's correct, and show an error if it's not.

However, reality isn't quite that simple.

Even though we were warned about various security attacks in our classes, it probably didn't fully sink in. It wasn't until I started working and was exposed to a dedicated team whose sole responsibility was managing authentication (Login/Logout) for the company's entire product ecosystem that I truly understood its complexity. Those seemingly basic functions hide a myriad of detailed use cases that need to be handled.

Of course, today we have more convenient options. Third-party providers like Auth0, Firebase Authentication, or AWS Cognito have already handled these problems. We just need to integrate them, which is both fast and secure.

But...

If you're curious, or if your company or project requires you to "do it yourself" and build the entire system from scratch, then I invite you to join me. Let's explore what it really takes to build a "login" function properly.

Hold on! Please read these important notes first!

Before we dive into the detailed list below, I need to clarify a few important points:

  1. NOT every project needs to implement the "full" list of features I'm listing. This is meant to be a comprehensive reference for you to consider.
  2. CONSIDER CAREFULLY. The decision to apply these should be based on the scale of your project. Most importantly, please consult with experienced Senior developers or a Solutions Architect (SA) before implementation.
  3. THIS IS BASED ON PERSONAL EXPERIENCE compiled from what I know and have learned. It will certainly have omissions, so please feel free to provide feedback and additions.

1. The Login Function

This is the most basic function, but it's also the most important gateway to your system.

  • Input validation: Validate input data (email/username format, password) on both the client-side (browser) and server-side.
  • Password security:
    • Require users to use strong passwords (e.g., minimum 8 characters, including uppercase, lowercase, numbers, and special characters).
    • NEVER store passwords in plain text. You must hash them (using bcrypt, Argon2, scrypt, etc.) before saving them to the database.
  • Session creation (if using sessions): After successful authentication, create a server-side session and send the session ID back to the client via a cookie.
  • JWT issuance (if using tokens): If using JWT, generate an access token (and possibly a refresh token) after authentication and send it to the client for storage.
  • Set secure cookies: If storing tokens/session IDs in cookies, you must set the HttpOnly, Secure, and SameSite flags to enhance security.
  • CSRF protection: If you use cookies, you must have a mechanism to prevent CSRF (Cross-Site Request Forgery) attacks, typically by using a CSRF token.
  • Two-Factor Authentication (2FA): Support 2FA. Send an OTP via email/SMS or use an authenticator app (like Google Authenticator).
  • "Remember me" support: If you have this feature, use a refresh token or a long-lived token to maintain the login session.
  • Account lockout policy: Temporarily lock an account after a certain number of failed login attempts (e.g., 5) to prevent brute-force attacks. Banking apps are very strict about this.
  • Rate limiting: Limit the number of login attempts allowed from a single IP address within a specific time frame.
  • CAPTCHA: Require the user to complete a "I'm not a robot" challenge after several consecutive failed logins.
  • Device Fingerprinting:
    • Collect device characteristics (user-agent, canvas, WebGL, timezone, etc.).
    • Hash this information to create a device ID.
    • If a login from an unrecognized device is detected => Send a warning email, require OTP verification.
  • GeoIP Tracking: Detect logins from an IP in an unusual country (e.g., a user from Vietnam logging in from a different location) => Send a warning email, require OTP verification.
  • Session management:
    • Sessions must have a finite Time-To-Live (TTL).
    • You must "regenerate" the session ID immediately after a successful login to prevent session fixation attacks.
  • Frontend error feedback: Display clear, specific error messages to the user (e.g., wrong password, account not found, account is locked).
  • Logging: Log ALL login behaviors (both successful and failed), saving information like: user ID, email, timestamp, IP address, device info, etc., for auditing purposes.

2. The Logout Function

You might think it's just about deleting the token on the client, but that's not enough.

  • Session invalidation: If using sessions, you must ensure the session is destroyed on the server-side.
  • CSRF protection: The logout API endpoint must also be protected against CSRF.
  • Token revocation (if using tokens):
    • This is a complex part of using JWT (as JWTs are stateless).
    • You need a mechanism to "revoke" a token, e.g., using a blacklist (stored in Redis) or by setting a very short lifespan for access tokens (5-15 minutes) and using refresh tokens to issue new ones.
  • Clear cookies: Clear all authentication-related cookies (access token, refresh token, session ID) from the client-side.
  • Redirect: After a successful logout, redirect the user to the Login page or the homepage.
  • Frontend state cleanup: Clear all user data from the client-side state (Redux/Context/etc.) to prevent sensitive information from leaking.
  • Invalidate refresh token: If your system uses refresh tokens, ensure they are also invalidated or deleted from the database.
  • Advanced "logout" options: Consider providing users with:
    • Logout from the current device.
    • Logout from a specific device (in a "manage devices" list).
    • Logout from all devices (Logout all sessions).
  • Frontend error feedback: Always use try...catch blocks. Even if a logout fails, you need to inform the user.
  • Logging: Log logout behaviors (successful/failed) with details: user ID, email, timestamp, IP, device, etc.

3. The "Forgot Password" Function

This is an extremely sensitive feature. If not handled carefully, it can be exploited by attackers to take over accounts.

  • Email/username verification: You must check if the email/username exists in the system before sending a reset link.
  • Rate limiting and abuse protection: Limit the number of "forgot password" requests from a single IP or for a single email to prevent spam and abuse.
  • Generate secure token: Create a reset token that is random, sufficiently long, hard to guess (32-64 characters), and, importantly, for one-time use only.
  • Token expiration: This token must have a short expiration time (e.g., 10-15 minutes) to minimize risk.
  • Send email with reset link: Send the user an email containing the link with the embedded token.
  • Server-side token storage: Store the (hashed) token in your database, associating it with the user and its expiration time.
  • Reset password form: The page where the user enters a new password must ensure:
    • The token submitted is still valid (not expired, belongs to the user).
    • The new password meets strength requirements (and includes a "confirm password" field).
    • It has CSRF protection if necessary.
  • Token one-time usage: Ensure the token can only be used once. Immediately invalidate it after a successful reset.
  • Password security: The new password must also be hashed (bcrypt, Argon2, etc.) carefully before saving.
  • Notify user after reset: Send the user an email notification saying, "Your password has just been changed" so they are aware of any unusual activity.
  • Option to logout all sessions: You should provide an option (e.g., a checkbox) to "Log out from all other devices" after a successful password reset.
  • Frontend error feedback: Provide specific, user-friendly error messages (e.g., "This token has expired," "Password is too weak").
  • Logging: Log the request-to-reset and the reset status (success/fail) with details (user ID, email, timestamp, IP).

4. The Register Function

This initial user creation function also has many potential risks if not handled correctly.

  • Input validation: Validate (on both client and server) the email format, password strength, username validity, and ensure the "password" and "confirm password" fields match.
  • Duplicate check: Check if the email or username already exists in the system.
  • Password security: Enforce strong passwords and hash them before saving (same as the Login function).
  • Email verification: Send a verification email (with a link or OTP code) to ensure the user's provided email is real.
  • Rate limiting & bot protection: Limit the number of registrations from a single IP. It's almost mandatory to integrate CAPTCHA/reCAPTCHA to prevent bots from creating spam accounts.
  • Username/email normalization: Standardize the input data. For example, convert emails to lowercase, trim whitespace, etc., to ensure duplicate checks work correctly.
  • Set default user role/status: Assign a default role (e.g., user) and status (e.g., unverified) to the new account.
  • Create related entities: Depending on your application's logic, you might need to create a user profile, an empty shopping cart, a favorites list, etc., for the new user.
  • Send welcome email: Send a welcome email, possibly with usage instructions or helpful information.
  • Secure session/token issuance: After a successful registration, you might want to automatically log the user in by issuing a session or token.
  • CSRF protection: The registration form also needs to be protected against CSRF attacks.
  • Email/phone confirmation reminder UI: If the user hasn't verified their account, you should display a reminder in the UI with a "Resend verification email" button.
  • Terms of Service & Privacy Policy agreement: It's mandatory to require users to check a box agreeing to your "Terms of Service" and "Privacy Policy" before completing registration.
  • Frontend error feedback: Provide clear error messages (e.g., "Password is too weak," "Email already exists").
  • Logging: Log the registration event (user ID, email, timestamp, IP).

Real-World Scenarios to Consider

The list above is technical, but in a real-world implementation, you might encounter these situations:

  • User switching networks: A user is on Wi-Fi, loses connection, and switches to 4G/5G. Their IP address will change. Should the system send an alert or force re-authentication? (This needs careful discussion with your SA/Senior).
  • User using a VPN: When using a VPN, the IP address can change frequently. You might consider allowing users to "trust" a device (Whitelist) to avoid constant alerts.
  • Private mode: Browsers in private/incognito mode can interfere with Device Fingerprinting mechanisms. In this case, the system may need to "fallback" to checking IP and user-agent.
  • Privacy: If your system collects IPs and fingerprints, you must clearly and transparently disclose this in your "Privacy Policy."
  • User Experience (UX): Security is paramount, but don't let security measures negatively impact the user experience. Forcing users to verify themselves too often can be frustrating. We always need to find a balance between Security and Usability.

Conclusion

Well, we've gone through a pretty long list.

After reading all this, you can probably see that there are many details to handle correctly, right? This is precisely why "Auth-as-a-Service" platforms (like Auth0, Firebase, Cognito) are so useful. They have done all this complicated work for us.

Therefore, my advice is, if your project doesn't have any special or mandatory requirements, you should prioritize using these available services.

But in cases where you must build it yourself, I hope this checklist has given you a broader, more comprehensive view of the issues to be aware of, helping you avoid basic security pitfalls.

If you have experience or know of any other points to consider, please share them in the comments so we can all learn!