Finally Understanding Authentication?!

Ryan MalawaRyan Malawa
19 min read

Authentication is the process that verifies the identity of users, ensuring that only legitimate individuals gain access to sensitive data and functionalities. Navigating the array of available authentication methods can often feel like traversing a complex maze. This guide aims to simplify these intricate concepts, providing a clear and efficient explanation of several fundamental authentication methods.

JSON Web Tokens (JWT)

It is a widely adopted open standard that defines a compact and URL-safe way to represent claims securely as a JSON object. This JSON object is then digitally signed, which ensures the integrity and authenticity of the data being transmitted.
JWTs are commonly used for both authentication, to verify the identity of users, and authorization, to determine what actions those users are allowed to perform.
Stateless in nature, meaning that the server does not need to maintain session information for each user. All the necessary information to verify the user's identity and permissions is contained within the token itself. This self-contained and efficient mechanism allows for secure transmission of identity and authorization information between parties.

Anatomy of a JWT:

  • Header: Contains metadata about the token, including the signing algorithm being used (such as HS256 or RS256) and the type of token, which is usually "JWT".

  • Payload: Contains the "claims," which are statements about an entity, often the user. These claims can include various pieces of information such as the user's ID, username, email address, assigned roles, permissions, and the token's expiration time. Claims within the payload can be categorized as registered claims (predefined claims like issuer, expiration time, subject, audience), public claims (custom claims that should be collision-resistant), or private claims (custom claims agreed upon by the parties exchanging the token).

  • Signature: Serves to verify that the token has not been tampered with during transmission (integrity) and that it was indeed issued by the party holding the secret or private key (authenticity).

The JWT Authentication Flow with Roles

  1. The process of JWT authentication with roles typically begins when a user provides their login credentials, such as a username and password, to the authentication server.

  2. The server then authenticates the user by verifying these credentials against a user database or another identity management system.

  3. Upon successful authentication, the server generates a JWT. The payload of this JWT includes information about the user, such as their unique identifier and their assigned roles within the application.

  4. The server then sends this generated JWT back to the client.

  5. The client, which could be a web browser or a mobile application, stores this JWT, often in local storage, session storage, or as a secure HTTP-only cookie.

  6. When the client needs to access a protected resource on the server, it includes the JWT in the Authorization header of the HTTP request. The standard practice is to use the "Bearer" schema, so the header would look something like Authorization: Bearer <token>.

  7. Upon receiving the request, the server extracts the JWT from the Authorization header and verifies its signature using the secret key that was used to sign it. This verification step ensures that the token has not been altered during transit.

  8. If the signature is valid, the server decodes the JWT and extracts the user's information from the payload, including their roles.

  9. Before processing the request for the protected resource, the server checks the user's roles (obtained from the JWT payload) to determine if they have the necessary permissions to access the resource or perform the requested action. This mechanism allows for efficient role-based access control, as the user's roles are readily available within the token itself, reducing the need for the server to query a database for this information on every request.

Best Use Cases for JWT

  • They are particularly well-suited for securing APIs due to their self-contained and stateless nature, making them ideal for RESTful architectures where each request should be independent.

  • JWTs also excel in Single Sign-On (SSO) implementations, where a single token can be passed between multiple applications and domains to authenticate the user seamlessly, eliminating the need for repeated logins.

  • Their lightweight and efficient data transmission make them a great fit for mobile applications and Single-Page Applications (SPAs) where performance is critical.

  • Furthermore, JWTs can be effectively used for cross-domain authentication scenarios, where traditional cookies might face limitations due to browser security policies.

  • In microservices architectures, JWTs facilitate secure and stateless communication between different services, as each service can verify the authenticity and claims within the token independently.

    Overall, JWTs offer a versatile and robust solution for a wide range of authentication and authorization needs in contemporary distributed systems.

OAuth 2.0

OAuth 2.0, which stands for "Open Authorization," is a widely adopted open standard designed to enable third-party applications to obtain limited access to a user's resources hosted by other web services without exposing the user's password.

It focuses primarily on granting access and managing permissions to these resources, rather than on verifying the user's identity itself; its core function is authorization, not authentication.

It's important to note that OpenID Connect (OIDC) is an authentication protocol that builds upon the foundation of OAuth 2.0 to provide user authentication and Single Sign-On (SSO) capabilities.11

Compared to its predecessor, OAuth 1.0, OAuth 2.0 simplifies the authorization process by using tokens, such as access tokens and refresh tokens, instead of relying on cryptographic signatures for every request.6 This makes OAuth 2.0 the de facto industry standard for secure delegated access to online resources.

Key Roles in OAuth 2.0

  1. The Resource Owner is the user or entity that owns the protected resources and has the authority to grant access to them.

  2. The Client is the third-party application (e.g., a mobile app, a web application) that seeks access to the Resource Owner's resources. Clients can be either confidential, meaning they can securely store a client secret (typically server-side applications), or public, where keeping a secret confidential is not possible (e.g., browser-based SPAs or native mobile apps).

  3. The Resource Server is the server that hosts the protected user accounts and resources and is responsible for accepting and responding to requests for these resources using access tokens.

  4. The Authorization Server is the server that authenticates the Resource Owner and, upon obtaining their consent, issues access tokens to the Client application. In many implementations, the Authorization Server and the Identity Provider are the same entity.

Understanding OAuth 2.0 Grant Types

OAuth 2.0 offers several "grant types," which are essentially different methods for a client to obtain an access token, each suited to different scenarios:

  • The Authorization Code Grant is the most commonly used grant type, especially for traditional web applications running on a server. It involves a two-step process where the client first obtains an authorization code from the Authorization Server and then exchanges that code for an access token.5 Often, this grant type is used with PKCE (Proof Key for Code Exchange) for public clients like mobile apps and SPAs to enhance security.

  • The Implicit Grant is a simplified flow primarily used for single-page applications (SPAs) and sometimes mobile apps where securely storing a client secret is challenging. In this flow, the access token is returned directly to the client via the redirect URI, without an intermediate authorization code. However, this grant type is generally discouraged now in favor of the Authorization Code Grant with PKCE due to security concerns.

  • The Client Credentials Grant is used when the client application itself needs to access resources, not on behalf of a user. It's common in server-to-server interactions where no user is directly involved. The client sends its client ID and client secret directly to the Authorization Server to obtain an access token.

  • The Resource Owner Password Credentials Grant is used when the Resource Owner (user) provides their service credentials (username and password) directly to the client application, which then exchanges them for an access token from the Authorization Server. This grant should only be used for highly trusted applications and is generally not recommended due to security implications.

  • The Refresh Token Grant involves using a refresh token (obtained during the initial authorization) to request a new access token from the Authorization Server when the original access token expires.

  • The Device Authorization Grant is designed for devices that lack a browser or have limited input capabilities (like smart TVs). It involves a multi-step process where the device obtains a code that the user can then use on another device with a browser to authorize access.

The OAuth 2.0 Authentication Flow

  1. The Client application(e.g., example.com) requesting authorization from the Resource Owner(John) to access their protected resources(e.g., John's files on Google Drive). This request is often initiated by redirecting the user to the Authorization Server(e.g., Google).

  2. The Authorization Server then presents a consent screen to the Resource Owner, asking them to approve or deny the Client's request for access to specific resources (defined by scopes). If the Resource Owner approves, the Authorization Server issues an authorization grant to the Client, which is usually an authorization code (in the case of the Authorization Code Grant).

  3. The Client then exchanges this authorization grant for an access token by making a request to the Authorization Server's token endpoint. This request includes the authorization code, the Client's credentials (client ID and secret, if applicable), and the redirect URI(https://example.com/callback).

  4. The Authorization Server authenticates the Client and validates the authorization grant. If everything is valid, it issues an access token (and often a refresh token) to the Client.

  5. The Client can now use this access token to make requests to the Resource Server on behalf of the Resource Owner. The access token is typically included in the Authorization header of the HTTP request (e.g., as a Bearer token).

  6. The Resource Server receives the request with the access token and validates it, often by communicating with the Authorization Server or by verifying its signature (if it's a JWT).

  7. If the access token is valid and has the necessary scopes (permissions), the Resource Server grants access to the requested resource.

    This flow highlights the multi-step process and the interactions between different entities to ensure secure delegated access.

Use Cases for OAuth 2.0

  • It is commonly used for delegating access to user data between different applications, for example, allowing a photo editing app to access photos stored on a cloud storage service without needing the user's cloud storage password.

  • It also powers the "Login with..." functionality, enabling users to sign up or log in to a new service using their existing credentials from a trusted provider like Google or Facebook.

  • Widely used for securing APIs, allowing controlled access to specific resources.

  • It's also employed in machine-to-machine (M2M) authorization, where applications need to access each other's resources without direct user involvement, such as a backend service accessing data from another internal service.

  • It is suitable for mobile and desktop applications that need to access protected resources on behalf of a user.

    This broad range of use cases underscores the flexibility and importance of OAuth 2.0 in modern web and application development.

HTTP Cookies

HTTP cookies are small pieces of data that web servers store in a user's web browser.
When a user visits a website, the web server can send one or more cookies to the browser, which then stores them. For any subsequent HTTP requests the browser makes to the same domain (or its subdomains, depending on how the cookie is configured), the browser automatically includes these cookies in the Cookie header of the request.

Cookies are fundamental for maintaining state in the stateless HTTP protocol, and one of their primary uses is for session management, which includes maintaining a user's login status.
When a user successfully logs in to a website, the server can create a session ID, store information about that session server-side, and send the session ID to the client as a cookie.
On subsequent requests, the server can read the session ID from the cookie and retrieve the associated session information, effectively authenticating the user without requiring them to re-enter their credentials on every page load.

The Anatomy of a Cookie

  • Name: serves as an identifier for the cookie

  • Value: contains the actual data being stored, such as a session identifier or a JWT.

  • Attributes: are the name-value pairs that control various aspects of the cookie's behavior, security, and scope. These attributes are crucial for configuring cookies effectively for different purposes and security contexts.

Essential Cookie Attributes Explained

  • HttpOnly: The HttpOnly attribute, when set, prevents client-side scripts, like JavaScript running in the browser, from accessing the cookie's value through the document.cookie API. This is a critical security measure that helps to mitigate Cross-Site Scripting (XSS) attacks by preventing malicious scripts from stealing sensitive information stored in cookies, such as session IDs or authentication tokens.

  • Secure:The Secure attribute instructs the browser to only transmit the cookie over HTTPS connections. This is essential for protecting the cookie's data from being intercepted by attackers using "man-in-the-middle" attacks over unencrypted HTTP connections.

  • SameSite: The SameSite attribute tells the browser when it's allowed to send the cookie along with requests. This helps to prevent Cross-Site Request Forgery (CSRF) attacks.
    It can have three values:

    1. Strict :The cookie is only sent for requests originating from the same site.
    2. Lax: The cookie is sent with same-site requests and also with cross-site requests that are "safe" navigations (like clicking a link with a simple GET request). It won't be sent for cross-site requests that might change data (like form submissions with POST).

    3. None :The cookie is sent with all requests, both same-site and cross-site. Important: If you use SameSite=None, you must also set Secure to true, meaning the cookie will only be sent over HTTPS. This is required for security.

  • Domain: The Domain attribute specifies the domain for which the cookie is valid. If a domain is set (e.g., .example.com), the cookie will be sent with requests to that specific domain and all its subdomains. If the Domain attribute is not explicitly set, the cookie typically defaults to the domain of the server that set it, but it will usually not be sent to subdomains in that case.56 A leading dot in the domain name is often used to include all subdomains.

  • Path: The Path attribute specifies the URL path within the domain for which the cookie is valid.
    For example, if Path=/accounts, the cookie will only be sent with requests to URLs that begin with /accounts. If not set, it defaults to the path of the resource that set the cookie.

  • Expires/Max-Age: Both of these attributes control the lifespan of the cookie.
    Expires sets a specific date and time for the cookie to expire.
    Max-Age defines the cookie's maximum age in seconds. If neither attribute is set, the cookie is considered a session cookie and will be deleted when the user closes their browser.

Cookies in Action: Localhost vs. Production Environments

When developing locally, the domain for cookies is often localhost . It's important to note that browsers might treat different ports (e.g., localhost:3000 and localhost:5000) as separate domains, which can prevent cookies set by one from being sent to the other.

A common solution to this is to use a reverse proxy, such as the one provided by webpack dev server, to make the frontend and backend appear to be on the same origin, thus enabling cookies to function correctly.

Additionally, for security-sensitive attributes like Secure and SameSite=None to work as intended, even in localhost environments, it might be necessary to use HTTPS, which can be achieved through the use of self-signed certificates.

In production environments, the Domain attribute should be explicitly set to the actual domain name of the application (e.g., .example.com to include all subdomains).

The Secure attribute should always be enabled to ensure cookies are only transmitted over HTTPS.
The SameSite attribute needs to be carefully configured based on the application's requirements for handling cross-site requests.
For instance, if the application needs to be embedded in iframes or interact with other domains, the SameSite attribute will need to be set accordingly, keeping in mind the security implications of each setting (Strict, Lax, None).

Best Use Cases for Cookie Authentication

  • It is particularly well-suited for traditional web applications where the server can effectively manage user sessions, often using server-side session stores.

  • Cookies are also naturally suited for maintaining user sessions, remembering login status, and providing personalized experiences on websites.
    The "Remember Me" functionality, which allows users to stay logged in even after closing their browser, is commonly implemented using persistent cookies that have an expiration date set.

  • In scenarios where there is a simple, direct client-server relationship and the frontend and backend are closely coupled, operating within the same domain, cookie-based authentication can be straightforward to implement and manage, with the browser handling much of the cookie transmission automatically.

Single Sign-On (SSO)

Is an authentication method that allows users to securely authenticate with multiple related applications and websites by using just one set of login credentials.
This approach significantly improves the user experience by eliminating the need to remember and manage multiple usernames and passwords for different applications.
Beyond convenience, SSO also enhances security by reducing password fatigue, which often leads to users choosing weak or easily guessable passwords, or reusing the same password across multiple accounts.
For organizations, SSO simplifies user and access management, providing a centralized point of control for provisioning and deprovisioning user accounts across various applications.

How SSO Works: The Authentication Flow

  1. A user attempts to access an application or website, which acts as the Service Provider.

  2. Instead of being prompted for credentials directly by the application, the user is redirected to a central SSO system or Identity Provider. Here, the user authenticates themselves with the IdP using their single set of credentials, such as a username and password, and potentially a second factor for multi-factor authentication.

  3. Once the IdP verifies the user's identity, it generates an SSO token or assertion that represents the user's authenticated session.

  4. This SSO token is then securely passed back to the originally requested application, often via the user's browser.

  5. The application then validates the received SSO token with the IdP to confirm the user's identity and, upon successful validation, grants the user access without requiring them to log in again.

    This process simplifies the authentication experience for users across multiple applications by centralizing the login process through a trusted Identity Provider.

Common SSO Protocols: SAML, OAuth 2.0, OpenID Connect

  • SAML (Security Assertion Markup Language): Is a widely adopted open standard, particularly in enterprise environments, for securely exchanging identity and access data between an Identity Provider and a Service Provider.

  • OAuth 2.0, while primarily an authorization framework, can also be leveraged in SSO scenarios, especially for enabling "Login with..." functionalities and federated authentication, where users can authenticate using their existing accounts from platforms like Google or Facebook.

  • OpenID Connect (OIDC) is another popular protocol that is built as an authentication layer on top of OAuth 2.0. It provides a standardized way to verify the identity of end-users and to obtain basic profile information about them, making it well-suited for implementing SSO in modern web and mobile applications.

Use Cases of SSO

  • Enterprise environments where employees need access to numerous internal and external applications

  • Cloud-based application suites

  • Online education platforms

  • E-commerce sites offering integrated services

  • Government services providing access to various citizen portals.

WebAuthn

Web Authentication (WebAuthn) is a web standard published by the World Wide Web Consortium (W3C) with the primary aim of building an authentication system for web-based applications that effectively solves or mitigates the security issues associated with traditional password-based authentication.
It enables secure, passwordless authentication on the web by leveraging the power of asymmetric cryptography, which relies on the use of public and private key pairs for secure communication.

Instead of relying on users to create, remember, and manage passwords, WebAuthn utilizes public key cryptography and authenticators to verify a user's identity.

How it Works

The WebAuthn authentication process involves two main stages: registration and authentication.

During the registration flow,

  1. a user visits a website or web application that supports WebAuthn and requests to create a new account or register a security key.1

  2. The website, acting as the Relying Party, initiates the process by generating a unique and cryptographically random challenge.

  3. It then communicates to the user's browser that it wants to register a new public-private key pair for the user.

  4. The user chooses their preferred authenticator, which can be a platform authenticator (built into their device, such as a fingerprint sensor or facial recognition) or an external authenticator (like a hardware security key).

  5. The chosen authenticator then generates a new public-private key pair specifically for this website.

  6. The private key is securely stored within the authenticator and never leaves the user's device, while the public key is what will be registered with the website.

  7. The authenticator sends the public key back to the website via the user's browser, along with a unique identifier for the credential (credential ID) and, optionally, attestation data that provides information about the authenticator itself.

  8. Finally, the website validates the registration data and stores the public key associated with the user's account.

In the authentication flow,

  1. When a user attempts to log in to a website that they have previously registered with using WebAuthn , the website sends an authentication challenge to the user agent (browser).

  2. The user agent then asks the authenticator to sign this challenge , and the user provides their consent to the authenticator, typically through a biometric scan or by entering a PIN.

  3. The authenticator uses the stored private key to sign the challenge and returns the signed assertion to the user agent, which then forwards the assertion to the website.

  4. Finally, the website validates the received assertion using the stored public key. If the validation is successful, the user is authenticated and granted access.

Key Roles: Relying Party and Authenticator

The Relying Party is the website or online service that the user wants to access. It initiates the WebAuthn authentication process and verifies the user's identity based on the assertion received from the user agent.

The Authenticator is the device or software that holds the user's private key and performs the actual authentication.
Authenticators can come in two main types: platform authenticators, which are built into the user's device (like a fingerprint reader or facial recognition on a laptop or smartphone), and roaming authenticators, which are external devices that can be used across multiple platforms (such as a USB security key).1
These two roles work together to establish a secure authentication mechanism that doesn't rely on traditional passwords.

Best Use Cases for WebAuthn

  • Replacing passwords as the primary authentication method,

  • Securing high-value transactions where strong authentication is critical

  • Scenarios that require robust resistance to phishing attacks, and for use in both mobile and desktop applications.

The Hybrid Approach: Combining Cookies and JWT

A common pattern in modern web development is to combine the use of HTTP cookies and JSON Web Tokens (JWTs) for authentication. This hybrid approach aims to leverage the benefits of both technologies.

How JWT can be Stored in HTTP Cookies

One way to implement this combination is by storing the JWT as the value of an HTTP cookie.
When the server authenticates a user, it generates a JWT and then sets this JWT as the value of a cookie in the HTTP response sent back to the client.
This cookie can then be configured with important security attributes like HttpOnly and Secure.
The HttpOnly flag prevents client-side JavaScript from accessing the cookie, thus enhancing security against Cross-Site Scripting (XSS) attacks.
The Secure flag ensures that the cookie is only transmitted over HTTPS connections, protecting against Man-in-the-Middle (MITM) attacks.28

Use Cases and Benefits of this Combination

  • This hybrid approach offers several advantages, particularly for Single-Page Applications (SPAs). By storing the JWT in an HttpOnly cookie, the token is automatically sent with every subsequent HTTP request to the server for the same domain, similar to traditional cookie-based authentication.
    This eliminates the need for the client-side JavaScript code to manually include the JWT in the Authorization header of each request.
    Meanwhile, the backend can still utilize the JWT for stateless authentication and authorization, enjoying the scalability and efficiency benefits that JWTs provide.

  • Another common use case is to store a short-lived JWT in an HttpOnly cookie as an access token, and a longer-lived refresh token (which could also be stored in a cookie or other storage mechanism) to obtain new access tokens when they expire.
    This helps to improve security by limiting the validity of the access token while still allowing for a seamless user experience by using the refresh token to silently renew the session.

  • Furthermore, when combined with the SameSite attribute for cookies and other Cross-Site Request Forgery (CSRF) prevention techniques, storing JWTs in cookies can help to mitigate CSRF risks, which are a common concern with cookie-based authentication.

    This hybrid approach can therefore provide a good balance between security, statelessness, and ease of use for certain types of web applications.

0
Subscribe to my newsletter

Read articles from Ryan Malawa directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Ryan Malawa
Ryan Malawa

EXPLORING GOLANG