← Open in the full interactive course (progress tracking, search & more)

Authentication answers exactly one question: who is making this request? Nothing more.

Two very different questions get asked, back to back, on almost every request a real application handles:

  1. Who are you? — Is this really Priya, or an anonymous stranger, or someone presenting stolen credentials?
  2. What are you allowed to do? — Now that we know it's Priya, can she delete this invoice, or only view it?

These sound similar, and beginners conflate them constantly — but they are genuinely two separate concerns, solved by two separate mechanisms, and this cluster of lessons treats them that way on purpose. This lesson is entirely about question 1: authentication — establishing identity. The very next lesson is entirely about question 2: authorization — deciding what that identity may do. Getting this distinction precisely right, from the start, is one of the single most valuable things you can take from this whole area — confusing the two is probably the most common beginner mistake in this entire subject, and it leads directly to real security bugs (checking "is this user logged in?" when the actual question that needed answering was "is this user allowed to do this specific thing?").

In this lesson: what authentication actually establishes, the ASP.NET Core authentication middleware model — schemes, AddAuthentication(), AddCookie(), AddJwtBearer() — how [Authorize] triggers an authentication challenge, and the conceptual difference between cookie-based and token-based authentication, which the next two lessons (JWT, then OAuth/OpenID Connect) build on directly.

What Is It?

The Simple Explanation

Authentication is the process of proving — and the server confirming — who is making a request. The outcome of successful authentication is an established identity: a set of facts the server now trusts about the caller (a user ID, a name, an email, maybe a list of roles). Nothing about authentication decides what that identity is permitted to do — it only decides who they are.

The Technical Definition

In ASP.NET Core, authentication is implemented as middleware — pieces of the pipeline covered generally elsewhere in this Part — that inspects an incoming request (a cookie, a header, a token) for credentials, verifies them, and, on success, constructs a ClaimsPrincipal: an object representing "the currently authenticated user," carrying a collection of claims (individual facts about that identity — name, email, role — covered in depth in the next lesson). That ClaimsPrincipal is attached to HttpContext.User, where the rest of the pipeline — including authorization, which runs afterward — can read it.

Authentication — this lesson

Authorization — next lesson

Why Does It Exist?

The Problem — HTTP Has No Idea Who's Asking

HTTP is stateless and, by default, completely anonymous. A server handling a request has no inherent notion of "this request came from the same person who logged in five minutes ago" — every request arrives as a blank slate, from the pipeline's point of view, unless something in that request proves otherwise. Without a deliberate mechanism to establish identity, every piece of code downstream that needs to know "whose data is this" or "who performed this action" has nothing reliable to ask.

The Need

What's needed is a standard, pluggable place in the request pipeline where "prove who you are" is checked once, consistently, regardless of how the proof was delivered (a cookie, a bearer token, an API key) — producing one trusted, shared representation of the caller's identity that the rest of the application, including authorization, can simply read.

The Solution

ASP.NET Core's authentication middleware, built around named authentication schemes. A scheme is a self-contained, named configuration of exactly how to authenticate a request using one particular mechanism — "Cookies," "Bearer" — registered once at startup, and the same pluggable model works whether the mechanism turns out to be a signed cookie, a JWT, or something else entirely, all covered by the concepts introduced here.

Big Picture

WHERE AUTHENTICATION SITS IN THE REQUEST PIPELINE
Incoming HTTP request (with a cookie, or an Authorization: Bearer ... header)

Authentication middleware — reads the credential, verifies it against the registered scheme

Success → builds a ClaimsPrincipal, attaches it to HttpContext.User
     Failure → HttpContext.User stays anonymous (no identity)

Authorization middleware (next lesson) — reads HttpContext.User, decides allow or deny

Your endpoint / controller action runs — already knowing exactly who called it (or that no one did)

How It Works

SCHEMES, REGISTRATION, AND THE [Authorize] CHALLENGE — STEP BY STEP
1. REGISTER AUTHENTICATION SERVICES AND ONE OR MORE SCHEMES
builder.Services
    .AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = "Bearer";
        options.DefaultChallengeScheme = "Bearer";
    })
    .AddJwtBearer("Bearer", options => { /* JWT validation settings — next lesson */ })
    .AddCookie("Cookies", options => { /* cookie settings */ });
2. ADD THE AUTHENTICATION MIDDLEWARE TO THE PIPELINE
app.UseAuthentication(); // must run before UseAuthorization()
app.UseAuthorization();
3. MARK AN ENDPOINT AS REQUIRING AUTHENTICATION
[Authorize]
[HttpGet("me")]
public IActionResult GetMyProfile() => Ok(User.Identity?.Name);
4. NO VALID IDENTITY → AN AUTHENTICATION "CHALLENGE"

Simple Example

Cookie-based authentication for a traditional server-rendered site — a browser holds a signed cookie, and the server reads it on every request:

builder.Services
    .AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(options =>
    {
        options.LoginPath = "/login";
        options.ExpireTimeSpan = TimeSpan.FromHours(8);
    });

// After the user submits valid credentials on a login form:
var claims = new List<Claim>
{
    new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
    new Claim(ClaimTypes.Name, user.Name)
};
var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
await HttpContext.SignInAsync(new ClaimsPrincipal(identity));
// The framework writes a signed authentication cookie to the response.
// Every future request from this browser includes it automatically; the middleware
// verifies its signature and rebuilds the ClaimsPrincipal from what's stored inside.

Meaning: After SignInAsync, the browser holds proof of identity (the cookie) and sends it back automatically on every subsequent request. The server never has to ask "who are you?" again for the life of that cookie — each request already carries the answer.

Real-World Example

Token-based authentication for an API consumed by a single-page app or a mobile client — the typical shape you'll actually build in modern ASP.NET Core APIs, and the exact setup the JWT lesson picks up in depth:

builder.Services
    .AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidIssuer = "https://api.myapp.com",
            ValidateAudience = true,
            ValidAudience = "myapp-clients",
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = signingKey,     // used to verify the token's signature
            ValidateLifetime = true            // rejects an expired token — see the JWT lesson
        };
    });

Here, the client authenticates once (typically via a login endpoint), receives a token, and sends it back on every request in an Authorization: Bearer <token> header. No server-side session storage is required to check it — the "Bearer" scheme's handler verifies the token itself on every request. Exactly what that token is, how it's structured, and why that verification doesn't need a database round-trip, is the entire subject of the next lesson but one.

Cookie-based

Token-based

Analogy

A photo ID at the front desk, not a permission slip

Authentication is like showing a photo ID at a building's front desk. The security guard checking it is answering exactly one question: does this ID genuinely belong to the person holding it, and does it prove who they say they are? The guard is not, at that moment, deciding whether you're allowed into the server room on the 12th floor — that's a completely separate check, against a completely separate list, that happens later (and is the next lesson's entire subject).

A cookie-based login is like a stamped hand pass you get once at the door, that gets scanned automatically every time you walk through an interior gate. A bearer token is like a badge you have to actively hold up to every scanner yourself. Different mechanisms, same underlying question at the front desk: who are you?

Under the Hood

HOW A SCHEME ACTUALLY BECOMES A ClaimsPrincipal
HANDLERS, AND HttpContext.User

Common Confusion

1. "Authentication and authorization are basically the same thing" — no, and this is the confusion that matters most in this whole area

Authentication establishes who. Authorization decides what they're allowed to do. They run as two distinct steps in the pipeline, in that specific order, and they fail with two distinct HTTP status codes: a failed authentication is 401 Unauthorized ("I don't know who you are, or your credentials weren't valid"); a failed authorization, given a perfectly valid identity, is 403 Forbidden ("I know exactly who you are, and you're not allowed to do this"). If your code only ever checks "is this user logged in?" when what it actually needs to know is "is this specific user allowed to do this specific thing?", you have silently collapsed authorization into authentication — and that's exactly the kind of gap that lets an authenticated-but-unprivileged user do something they shouldn't.

2. "[Authorize] is only about authorization" — its no-identity case is really about authentication

[Authorize] belongs conceptually to authorization (the next lesson covers its full behavior, including roles and policies) — but when a request arrives with no valid identity at all, what happens next (a challenge — redirect or 401) is authentication's job, not authorization's. [Authorize] is the trigger that makes both mechanisms visible at the same place in your code, which is exactly why the two get blurred together.

Common Mistakes

Mistake 1 — Registering UseAuthorization() before UseAuthentication()

Calling authorization middleware before authentication middleware — HttpContext.User won't be populated yet, so authorization checks run against an anonymous identity even for requests that carried valid credentials.

Always call app.UseAuthentication() before app.UseAuthorization() in the pipeline.

Mistake 2 — Treating "authenticated" as equivalent to "allowed"

Using bare [Authorize] (which only requires any valid identity) on an endpoint that actually needs a specific role or permission, like deleting another user's account.

Reach for role- or policy-based authorization (the next lesson) whenever the real question is more specific than "is someone logged in."

Mistake 3 — Mixing up 401 and 403 in your own mental model or error handling

Assuming a 403 response means "you need to log in" (it doesn't — it means the caller is already known and still isn't allowed) or assuming a 401 means "you're logged in but lack permission" (it doesn't — it means no valid identity was established at all).

401 = authentication failed or is missing. 403 = authentication succeeded, authorization didn't.

When Should I Use It?

Rule of thumb: Every time you're about to write a permission check, ask yourself first: "am I checking who this is, or what they're allowed to do?" If you can't clearly answer that, you likely haven't separated the two concerns yet.

Mental Model

Authentication = "Who is this?"
Scheme = a named recipe for how to answer that, for one specific mechanism (cookies, bearer tokens).
ClaimsPrincipal = the answer, attached to every request as HttpContext.User.
Challenge = what happens when there's no valid answer at all — a redirect, or a 401.

Remember: Authentication never asks "are you allowed to do this?" — only "who, exactly, are you?" That question belongs entirely to the next lesson.

Key Takeaway


Check Your Understanding

You've seen what authentication establishes, how schemes work, and exactly where it stops being authentication's job. Let's check your understanding.

1. Which question does authentication answer?

Show answer

Correct: B

Why B is correct: Authentication's entire job is establishing identity — who, exactly, is on the other end of this request.

Why A is incorrect: That's authorization, covered in the next lesson — a genuinely separate concern from establishing identity.

Why C is incorrect: That's request validation, covered in the previous lesson — unrelated to identity.

Why D is incorrect: Rate limiting (covered later in this Part) is about traffic volume, not identity.

Reinforcement: Authentication = who. Keep that word association exact.

2. A request hits an [Authorize]-protected API endpoint using JWT bearer authentication, with no Authorization header at all. What's the typical result?

Show answer

Correct: B

Why B is correct: With no credential at all, there's no identity to check permissions for — this is squarely authentication's failure mode. Bearer-scheme handlers typically respond with a plain 401 rather than a redirect, since there's no browser login page to send an API client to.

Why A is incorrect: 403 means an identity was established but denied — that's not what happened here; there was no identity to evaluate at all.

Why C is incorrect: [Authorize] specifically requires a valid identity — an anonymous request is rejected, not allowed through.

Why D is incorrect: A redirect is the cookie scheme's typical challenge behavior; a bearer-token API has no login page to redirect to, so it returns 401 instead.

Reinforcement: 401 = "I don't know who you are." That's exactly the case here.

3. What is an authentication scheme, in ASP.NET Core terms?

Show answer

Correct: B

Why B is correct: A scheme is a name ("Cookies," "Bearer") plus a handler that knows how to authenticate requests for one particular mechanism — registered via AddAuthentication() and configured with methods like AddCookie()/AddJwtBearer().

Why A is incorrect: A scheme is a pipeline configuration concept, not a data storage mechanism — password storage is a separate concern entirely.

Why C is incorrect: Roles belong to authorization, covered in the next lesson — schemes are about establishing identity, not deciding permissions.

Why D is incorrect: Claims are the content carried by an identity (covered in the next lesson and the JWT lesson) — a scheme is the mechanism that produces that identity in the first place, a different, broader concept.

Reinforcement: "Scheme" names the "how" of authenticating; it doesn't decide what the resulting identity is allowed to do.

4. Why must app.UseAuthentication() be called before app.UseAuthorization() in the pipeline?

Show answer

Correct: B

Why B is correct: Authorization's entire decision depends on reading the identity that authentication middleware establishes on HttpContext.User. If authorization runs first, that identity simply isn't there yet — every request looks anonymous to authorization, regardless of what credentials it actually carried.

Why A is incorrect: The ordering has a real, functional effect described above — it is not just a style preference.

Why C is incorrect: Reversing the order doesn't throw — it silently produces incorrect authorization behavior, which is arguably worse than an exception, since it can pass unnoticed.

Why D is incorrect: This ordering requirement has nothing to do with request body parsing.

Reinforcement: Pipeline order directly reflects the logical dependency: authorization needs authentication's output to do its job.

You now understand exactly what authentication establishes — and, just as importantly, exactly where its job ends. Next up: authorization, which picks up from here to decide what an authenticated identity is actually allowed to do.


dotnetmadeeasy.com — Learn C# and .NET, the right way.