Introduction

Welcome to the third lesson of the "Securing your REST API application with ASP.NET Core" course! In our previous lessons, we explored the basics of cross-origin resource sharing (CORS) and how to handle preflight requests. Now, we're going to tackle one of the most challenging aspects of modern web development: enabling and customizing CORS with credentials for cross-origin cookie authentication.

This lesson addresses a common real-world scenario where your frontend application runs on one domain (perhaps app.example.com or even a CDN) while your API runs on another (api.example.com). You'll learn how to securely share cookies across these different origins while maintaining strong security posture. This capability is essential for maintaining session state and authenticating users across distributed applications.

By the end of this lesson, you'll understand not just how to implement cross-origin cookie authentication, but also when it's appropriate to use this approach and what security considerations you must address. We'll work through practical code examples that you can immediately apply to your projects. Let's dive in! 🌟

Cookies And Web Authentication

Before we tackle the complexities of cross-origin scenarios, let's establish a solid foundation. Cookies are fundamental to how web applications maintain session state — they're small pieces of data that servers send to browsers, which browsers then store and send back with subsequent requests.

Think of cookies as a claim ticket at a coat check. When you log in (check your coat), the server gives you a unique identifier (the ticket). Every time you make a request (want your coat back), you present this ticket, and the server knows who you are without requiring you to log in again. This mechanism allows users to navigate through your application while remaining authenticated.

In cross-origin scenarios, however, browsers enforce strict security policies about cookie sharing. By default, browsers won't send cookies when making requests to a different origin (domain, port, or protocol). This restriction exists for good reason — imagine if any website could make requests to your banking application using your cookies! Understanding these security boundaries is crucial before we learn how to carefully and safely expand them for legitimate use cases.

Cross-Origin Cookie Authentication

Cross-origin cookie authentication involves using cookies for authentication when your frontend and backend exist on different origins. While this sounds straightforward, browsers block this by default due to serious security concerns.

Consider these security implications that browsers protect against:

  • Cross-site request forgery (CSRF) represents a particularly dangerous threat. Without proper restrictions, a malicious website could make authenticated requests to your API using a user's existing cookies, potentially performing unauthorized actions like transferring funds or changing account settings — all without the user's knowledge.

  • Information leakage poses another risk. Cookies often contain sensitive session data or identifiers that shouldn't be accessible to third-party sites. If every website could read cookies from every other domain, user privacy would be completely compromised.

  • Privacy implications extend beyond security. User tracking across multiple domains could violate privacy expectations and regulations. When we enable cross-origin cookie sharing, we must do so carefully and only with trusted domains.

To address these concerns while enabling legitimate cross-origin scenarios, we need a coordinated approach between server and client. The server must explicitly permit specific origins to receive credentials — via AllowCredentials() in the CORS policy — and the client must explicitly request to include credentials with each request. This mutual agreement ensures that cookie sharing only happens when both parties intend it. Additionally, we must configure cookies themselves with proper security attributes that protect against various attack vectors.

Benefits And Trade-Offs

Understanding when to enable CORS with credentials requires carefully evaluating whether the benefits justify the additional complexity and security considerations for your specific use case.

The Benefits:

Session continuity becomes seamless when properly implemented. Users can navigate between related applications or services without repeatedly authenticating. For example, a user logged into your main application at app.example.com can seamlessly access resources from your API at api.example.com without seeing login prompts.

Centralized authentication enables elegant architectures where a dedicated authentication service manages sessions for multiple applications. This approach simplifies identity management across your entire ecosystem and provides a single source of truth for user authentication state.

Familiar patterns mean your team can leverage existing knowledge of cookie-based authentication rather than learning entirely new paradigms. Most developers understand cookie-based sessions, and many existing systems already use this approach.

Real-World Use Cases:

Microservice architectures often require shared authentication across services running on different domains. For instance, your user service, payment service, and notification service might all need to validate the same user session.

Single sign-on (SSO) systems are perhaps the most common use case. When a user logs into one application in your organization's suite, they expect to be authenticated across all related applications without multiple login prompts.

API gateways frequently need to validate authentication before routing requests to multiple backend services. A central gateway can check session validity once rather than each service implementing its own authentication logic.

Frontend-backend separation has become standard practice, with frontend applications deployed on CDNs for performance while backend APIs run on traditional servers. For example, a React application on Netlify communicating with an ASP.NET Core API on Azure App Service requires to maintain user .

Enabling CORS With Credentials

Now let's implement secure cross-origin cookie sharing in our ASP.NET Core application using Minimal APIs. The key is enabling CORS with credentials while specifying exactly which origins can access our resources.

First, let's configure the CORS policy in our application setup:

This configuration does several critical things. The WithOrigins() method specifies exactly which domain can access our resources — we must use a specific origin because wildcard * is incompatible with credentials for security reasons. The AllowCredentials() method is the heart of this configuration, instructing the server to include the Access-Control-Allow-Credentials: true header in responses.

However, this server-side configuration alone isn't sufficient — both sides must opt in for cross-origin cookie sharing to work. While the server uses AllowCredentials(), the client must also explicitly request to include credentials. In browser fetch() calls, this means setting credentials: 'include':

Creating Session Cookies

When a user successfully authenticates, we need to create a session cookie that will be sent with subsequent requests. Let's build a realistic login endpoint using Minimal APIs that demonstrates proper cookie creation with all necessary security attributes.

First, let's define our data structures and session storage:

These simple classes represent our login request and session data. We use ConcurrentDictionary rather than a regular Dictionary because ASP.NET Core processes HTTP requests concurrently across multiple threads — simultaneous reads and writes from different requests against a standard Dictionary could cause race conditions or data corruption. ConcurrentDictionary provides built-in thread-safe operations designed for exactly these concurrent access patterns. In a production application, SessionData would likely include additional information like user roles, permissions, and session metadata.

Now let's implement the authentication endpoints using Minimal APIs:

Validating Session Cookies

Once we've established a session, we need to validate it on protected endpoints. Let's create endpoints that check for valid session cookies and demonstrate the complete request-response flow using Minimal APIs:

The validation process starts by attempting to retrieve the session cookie from the incoming request. ASP.NET Core's HttpRequest.Cookies collection provides convenient access to all cookies sent by the client. If the cookie doesn't exist or is empty, we immediately return an unauthorized response.

If the cookie exists, we validate that it corresponds to an active session in our store. This two-step verification (cookie exists and session is valid) provides defense in depth — even if an attacker somehow obtains a session ID, it must correspond to an active session to grant access.

Testing With HttpClient And CookieContainer:

To test the complete cookie authentication flow programmatically, you can use HttpClient with a CookieContainer. The CookieContainer automatically stores cookies from responses and includes them in subsequent requests, simulating how a browser handles cookies:

Security Best Practices

Implementing cross-origin cookie authentication securely requires adherence to several critical best practices that protect against common vulnerabilities.

  • Origin Whitelisting should be strict and explicit. Never use wildcard * origins when credentials are enabled — this configuration is not only insecure but also invalid according to CORS specifications. Maintain a carefully curated list of allowed origins, preferably loaded from configuration rather than hardcoded. Consider using environment-specific origin lists so development, staging, and production environments only allow their respective domains.

  • Cookie Security Attributes must all be properly configured. The combination of HttpOnly, Secure, and SameSite=None provides layered defense against different attack vectors. Never compromise on these attributes for convenience — they exist to protect your users. Additionally, implement appropriate cookie expiration strategies. Short-lived session cookies with reasonable MaxAge values reduce the window of opportunity for attackers.

  • Session Management deserves careful attention. Implement proper session expiration and cleanup to prevent indefinitely valid sessions from accumulating. Consider implementing session renewal for active users while enforcing absolute session timeouts. Store session data securely — use distributed caches like Redis in production rather than in-memory stores, which don't scale and lose data on application restarts.

  • CSRF Protection remains essential even with CORS properly configured. While CORS provides some protection, implementing additional CSRF tokens adds defense in depth. ASP.NET Core provides built-in anti-forgery tokens that should be used for state-changing operations.

  • HTTPS Enforcement is non-negotiable when using . Modern browsers require the attribute, which means only travel over HTTPS. Configure your hosting environment to redirect all HTTP traffic to HTTPS and use HTTP Strict Transport Security (HSTS) headers to enforce this at the browser level.

Conclusion

In this lesson, we've explored the intricacies of enabling and customizing CORS with credentials for secure cross-origin cookie authentication. You've learned how to properly configure CORS policies that allow credential sharing while maintaining security, how to create session cookies with appropriate security attributes, and how to validate these sessions in protected endpoints.

The key takeaway is that cross-origin cookie authentication requires careful, deliberate configuration at every level — from CORS policies to cookie attributes to session management. While this approach adds complexity compared to same-origin scenarios, it enables powerful architectural patterns like microservices and frontend-backend separation that are fundamental to modern web development.

As you move forward, practice implementing these concepts in your projects while remaining mindful of the security implications. Test your implementation thoroughly, paying special attention to error cases and edge conditions. In the next lesson, we'll continue building on these security foundations to further protect your ASP.NET Core REST API. Keep up the excellent work! 🚀

Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal