AJAX - AJAX Request Credentials and Cookie-Based Sessions

AJAX requests can communicate with a server without reloading the entire webpage. In many web applications, the server needs to know which user is making a request. This is commonly handled through cookies and session-based authentication. When an AJAX request includes the appropriate credentials, the browser can send cookies associated with the server, allowing the server to identify the user's session.

What Are Request Credentials?

Request credentials refer to authentication-related information that the browser may include when making a request. This can include cookies, TLS client certificates, and other browser-managed credentials. In traditional applications, cookies are often the most important type because they can contain a session identifier.

For example, after a user logs into a website, the server may create a session and send a cookie to the browser:

Set-Cookie: sessionId=ABC123

The browser stores this cookie according to its rules. When the browser later makes another request to the same website, it may automatically attach the cookie:

Cookie: sessionId=ABC123

The server can then use the session ID to determine which logged-in user is making the request.

Cookies in AJAX Requests

An AJAX request made using XMLHttpRequest can participate in cookie-based authentication. For same-origin requests, browsers generally handle relevant cookies automatically.

Example:

const xhr = new XMLHttpRequest();

xhr.open("GET", "/profile", true);

xhr.onload = function () {
    if (xhr.status === 200) {
        console.log(xhr.responseText);
    }
};

xhr.send();

If the user has an active session for the website, the browser can send the appropriate session cookie with the request. The server can therefore return information associated with that user's session.

This makes AJAX useful for applications such as dashboards, shopping carts, account pages, and administrative systems where different users need to receive different information.

withCredentials in XMLHttpRequest

The withCredentials property becomes particularly important when an AJAX request is made across origins.

For example:

const xhr = new XMLHttpRequest();

xhr.open("GET", "https://api.example.com/profile", true);

xhr.withCredentials = true;

xhr.onload = function () {
    if (xhr.status === 200) {
        console.log(xhr.responseText);
    }
};

xhr.send();

Setting:

xhr.withCredentials = true;

indicates that the browser is allowed to include credentials such as cookies in a cross-origin request, subject to the browser's cookie and security policies.

However, setting withCredentials alone does not make a cross-origin request work. The server must also provide the appropriate CORS response headers, and the cookies themselves must satisfy applicable cookie restrictions.

Same-Origin and Cross-Origin Requests

A same-origin request occurs when the webpage and requested resource share the same scheme, host, and port.

For example:

https://example.com/page
https://example.com/api/data

These have the same origin.

A cross-origin request occurs when one or more of these components differs.

For example:

https://example.com
https://api.example.com

These are different origins because their hosts differ.

Cross-origin requests are subject to additional browser security rules. When credentials such as cookies are involved, the server must explicitly permit credentialed cross-origin requests.

A server may respond with headers such as:

Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Credentials: true

The server cannot use a wildcard origin together with credentialed CORS in the usual browser CORS model:

Access-Control-Allow-Origin: *

when credentials are requested.

Cookie-Based Sessions

A session-based authentication system normally follows a sequence like this:

  1. The user submits login credentials.

  2. The server verifies the credentials.

  3. The server creates a session.

  4. The server sends a session cookie to the browser.

  5. The browser stores the cookie.

  6. Subsequent AJAX requests include the cookie when applicable.

  7. The server reads the session identifier.

  8. The server identifies the logged-in user.

  9. The server returns the appropriate response.

For example, after authentication, the server might issue:

Set-Cookie: sessionId=XYZ789; Secure; HttpOnly

A later AJAX request might contain:

Cookie: sessionId=XYZ789

The application does not normally need to manually read the HttpOnly cookie using JavaScript.

Why HttpOnly Is Important

A cookie marked with the HttpOnly attribute cannot normally be accessed through JavaScript's document.cookie API.

For example:

Set-Cookie: sessionId=XYZ789; HttpOnly; Secure

This is useful for session cookies because application JavaScript does not need direct access to the session identifier.

It helps reduce the impact of certain cross-site scripting attacks, because injected JavaScript cannot simply read an HttpOnly session cookie. It does not, however, prevent an attacker from making requests using the victim's browser if other security protections are absent.

The Secure Attribute

The Secure attribute instructs the browser to send the cookie only over secure HTTPS connections.

Example:

Set-Cookie: sessionId=XYZ789; Secure

Using secure cookies is important for protecting authentication information from being transmitted over an unencrypted HTTP connection.

For production applications, authentication cookies should generally be protected with HTTPS and appropriate cookie attributes.

The SameSite Attribute

The SameSite attribute controls when cookies are sent in cross-site contexts.

Common values include:

SameSite=Strict
SameSite=Lax
SameSite=None

Strict provides stronger restrictions on cross-site cookie sending.

Lax permits cookies in some cross-site situations and is commonly used as a practical default.

None allows cross-site cookie use, but modern browsers generally require such cookies to also have:

Secure

For example:

Set-Cookie: sessionId=XYZ789; SameSite=None; Secure

This is particularly relevant when an AJAX application and authentication service operate across different sites.

Credentials and Security

Credentialed AJAX requests must be designed carefully because cookies automatically carry authentication information. An incorrectly configured application can unintentionally expose authenticated resources.

Important protections include:

  • Use HTTPS for authenticated communication.

  • Configure CORS with specific trusted origins.

  • Avoid unnecessarily allowing credentials for untrusted origins.

  • Use HttpOnly for session cookies when JavaScript does not need access.

  • Use Secure for authentication cookies.

  • Select an appropriate SameSite policy.

  • Protect state-changing operations against CSRF attacks.

  • Validate authentication and authorization on the server for every protected request.

Client-side code should never be treated as the authority for deciding whether a user is allowed to access a resource. The server must perform authentication and authorization checks.

AJAX Credentials and CSRF

Cookie-based authentication creates an important security consideration known as Cross-Site Request Forgery (CSRF).

Suppose a user is logged into:

https://bank.example

A malicious website could potentially cause the user's browser to make a request to the banking website. If the browser automatically includes the authentication cookie, the banking server may see the request as coming from the authenticated user.

For state-changing operations such as changing an email address or transferring money, applications should use appropriate CSRF defenses. These can include SameSite cookies, CSRF tokens, origin checks, and other server-side protections depending on the architecture.

Difference Between Cookies and Authorization Headers

Cookie-based authentication is not the only authentication approach for AJAX applications.

A request may instead use an authorization header:

Authorization: Bearer <token>

With cookie-based sessions, the browser can manage the session cookie automatically.

With token-based approaches, application code may explicitly attach an access token to requests.

The two approaches have different security and architectural considerations. Cookie-based authentication is particularly convenient for traditional browser applications, while bearer-token approaches are commonly used in certain APIs and distributed application architectures.

Practical Example

Consider an online shopping website. A user logs in and receives a session cookie:

Set-Cookie: sessionId=ABC123; HttpOnly; Secure; SameSite=Lax

The webpage then makes an AJAX request:

const xhr = new XMLHttpRequest();

xhr.open("GET", "/api/cart", true);

xhr.onload = function () {
    if (xhr.status === 200) {
        const cart = JSON.parse(xhr.responseText);
        console.log(cart);
    }
};

xhr.send();

The browser can send the session cookie with the request. The server receives the request, identifies session ABC123, determines which customer owns that session, retrieves that customer's shopping cart, and sends the cart information back.

The page can then update only the shopping-cart section instead of refreshing the entire webpage.

Conclusion

AJAX request credentials allow asynchronous requests to work with authentication mechanisms such as cookies and server-side sessions. Cookie-based sessions are especially useful because the browser can manage authentication cookies while AJAX requests communicate with protected server resources.

For same-origin requests, cookies are generally handled automatically according to browser rules. For cross-origin requests, credential handling requires additional configuration through withCredentials, CORS, and appropriate cookie attributes. Secure implementation also requires HTTPS, careful CORS configuration, suitable SameSite settings, HttpOnly and Secure cookies where appropriate, and protection against CSRF attacks.