Skip to main content

Reflections

After this lesson, you should have a deeper understanding of the purpose and importance of authentication in web applications. How does authentication impact user experience and security?

Authentication is a critical component of web application security. It helps ensure that only authorized users can access sensitive information and perform actions within the application. By implementing robust authentication mechanisms, developers can protect user data, prevent unauthorized access, and maintain the integrity of the application.

Purpose of Authentication

What is Authentication?

Authentication is the process of verifying the identity of a user, system, or application. In web applications, authentication answers the fundamental question: "Who are you?"

It's important to distinguish authentication from authorization, which answers: "What are you allowed to do?" Authentication must happen before authorization can take place.

Why Do We Need Authentication?

Authentication serves several critical purposes in web applications:

  • Security: Protect sensitive data and resources from unauthorized access
  • Personalization: Provide customized experiences based on user identity
  • Accountability: Track and audit user actions for compliance and debugging
  • Access Control: Enable fine-grained permissions and role-based access
  • Trust: Build confidence that users are who they claim to be

Types of Authentication

Some of the common types of authentication we usually implement in OGP (that you may also have already tried implementing in the exercises) include:

1. Email OTP Authentication

Users receive a one-time password (OTP) via email to authenticate their identity.

This method is often used to allow for passwordless login or (less frequently) as part of multi-factor authentication (MFA), as it provides a convenient and secure way for users to verify their identity without needing to remember complex passwords.

However, it is important to consider implications that may arise from this, such as the potential for email interception or delays in email delivery, or the reliance on the security of the user's email account itself. Products that implement email OTP authentication also have additional security measures in place, such as rate limiting and token expiration, to mitigate these risks.

We will look into how to implement email OTP authentication and mitigate its risks in the exercises later in this curriculum.

2. OAuth 2.0 / OpenID Connect (OIDC)

Delegation protocols that allow users to authenticate using third-party providers (Google, GitHub, etc.)

  • OAuth 2.0: Authorization framework
  • OpenID Connect: Authentication layer built on OAuth 2.0

As our applications frequently serve both internal users (e.g., government officers) and external users (e.g., citizens), integrating with trusted identity providers via OAuth 2.0 / OpenID Connect helps streamline the authentication process while maintaining security.

Why was iron-session used in this lab?

iron-session is a stateless session library that encrypts session data directly into cookies, eliminating the need for a separate session store or database. This approach offers several advantages:

  • Simplified Infrastructure: No need to manage a separate session database or Redis instance
  • Horizontal Scaling: Stateless sessions work seamlessly across multiple server instances without session synchronization
  • Reduced Latency: No database queries needed to retrieve session data on each request (unless you need to fetch additional user data and want user info to be fresh)
  • Lower Operational Costs: Eliminates the need to maintain and scale a session storage system

However, it's important to consider the trade-offs. Cookie-based sessions have size limitations (typically 4KB), cannot be invalidated server-side without additional mechanisms, and require careful handling of cookie expiration and renewal. For applications with simple session data and where immediate session invalidation is not critical, iron-session provides an elegant and performant solution.

Examples of Authentication Methods

Below are some of the examples of authentication methods used in various OGP applications.

ApplicationAuthentication TypeUser TypePurposeGitHub Repository
ActiveSGSingpass (OIDC)CitizensFor authentication to log in to book programmes and facilitiesactivesg
ActiveSGSGID (OIDC)Public OfficersFor authentication to log in to perform admin duties. Has additional checks to verify whether one is a public officeractivesg
PairWOG AAD (Whole-of-Government Azure AD)Public OfficersGovt-only AI chatbotspairgovsg
Ask.gov.sgEmail OTPPublic OfficersAdmin officer login to CRUD questions and answersaskgov-next

Common Authentication Flows

Email OTP (Session based)

  1. User requests OTP
  2. Server generates OTP, sends to email
  3. User enters OTP
  4. Server verifies OTP
  5. Server creates a session with user information, and stores the session in the database
  6. Session ID stored in cookie
  7. Cookie sent with subsequent requests

Email OTP (Stateless - iron-session)

  1. User requests OTP
  2. Server generates OTP, sends to email
  3. User enters OTP
  4. Server verifies OTP
  5. Server encrypts user information and stores in cookie
  6. Cookie sent with subsequent requests

Singpass OIDC (Stateless)

  1. User initiates login via Singpass
  2. Redirected to Singpass login page
  3. User enters credentials
  4. Singpass verifies credentials
  5. Singpass redirects back with authorization code
  6. Server exchanges code for access token
  7. Server retrieves user information using access token
  8. Server encrypts user information and stores in cookie
  9. Cookie sent with subsequent requests

Security Best Practices

  • Validate all inputs serverside: Prevent injection attacks. Use zod to validate ALL inputs.
  • Generate OTPs cryptographically: Do not use Math.random(), use node:crypto or nanoid. Math.random() is actually very predictable, you only need 8-10 observations to predict the next output.
  • Sufficient OTP complexity: Use sufficient complexity. If you're not sure, 8 alphanumeric chars is a good starting point.
  • Store hashed OTPs: OTPs are passwords, so never store OTPs in plain text, use node:crypto or bcrypt
  • Compare in constant time: Use crypto.timingSafeEqual.
  • Bind OTP to current login attempt: Prevent shoulder surfing attack.
  • Implement IP rate limiting: Otherwise you will get rate limited or blacklisted by your email sending provider. Look out for big groups sharing one IP though (e.g. company VPN, CGNAT)
  • Implement user rate limiting: Same as above, enforce a >30s timeout between OTP resending.
  • Delete OTP after use: Force generation of a new OTP for next login attempt.
  • Use HTTPS: Encrypt all authentication and authenticated traffic
  • Secure cookie management: Use httpOnly secure cookies
  • Secure session management: Implement timeouts, secure cookies, encrypted payloads

Further Reading

RFC examples

Here are some RFCs written by fellow engineers on authentication for their various products that you may find helpful: