Skip to main content

Command Palette

Search for a command to run...

ASP.NET Core Authentication & Authorization Interview Questions for Senior .NET Developers (2026)

Published
โ€ข15 min read
ASP.NET Core Authentication & Authorization Interview Questions for Senior .NET Developers (2026)

Authentication and authorization are among the most scrutinized areas in any senior .NET interview. Interviewers don't just want to know that you've plugged in AddAuthentication() and moved on โ€” they want to understand whether you can reason about token lifetimes, claims transformation, policy composition, and how identity decisions ripple through a distributed system. This guide covers the questions you're most likely to face in a senior ASP.NET Core role in 2026, with clear, direct answers that go beyond the basics.

๐ŸŽ Want implementation-ready .NET source code you can drop straight into your project? Join Coding Droplets on Patreon for exclusive tutorials, premium code samples, and early access to new content. ๐Ÿ‘‰ https://www.patreon.com/CodingDroplets


Basic Questions

What Is the Difference Between Authentication and Authorization in ASP.NET Core?

Authentication establishes who the user is โ€” it validates an identity claim, typically via credentials or a token. Authorization determines what that authenticated identity is allowed to do โ€” it enforces access rules based on roles, claims, or policies.

In ASP.NET Core, these are separate middleware stages. UseAuthentication() populates HttpContext.User from the incoming request, and UseAuthorization() evaluates that principal against configured policies. The order matters: authorization always runs after authentication in the middleware pipeline.

What Are the Main Authentication Schemes in ASP.NET Core?

ASP.NET Core supports multiple authentication schemes via handlers. Common schemes include:

  • JWT Bearer โ€” validates a signed JSON Web Token in the Authorization: Bearer header. Standard for APIs.
  • Cookie Authentication โ€” persists identity in an encrypted HTTP cookie. Standard for web apps.
  • OAuth2 / OpenID Connect โ€” delegates authentication to an external identity provider (Google, Azure AD, Duende IdentityServer, Keycloak).
  • Certificate Authentication โ€” validates the client's X.509 certificate. Used for mutual TLS in service-to-service scenarios.
  • API Key โ€” a custom handler that reads and validates a key from a header or query string.

Each scheme is registered via AddAuthentication() and resolved by scheme name during request processing.

How Does the ASP.NET Core Middleware Pipeline Handle Authentication and Authorization?

The pipeline order is critical. UseRouting() must appear before UseAuthentication(), and UseAuthentication() must appear before UseAuthorization(). If you also have UseCors(), it must appear before both auth middlewares.

When a request arrives:

  1. The authentication middleware reads the request and calls the configured scheme handler(s).
  2. The handler validates the token/cookie/certificate and sets HttpContext.User to a ClaimsPrincipal.
  3. The authorization middleware checks the ClaimsPrincipal against the policies applied to the matched endpoint.

If the user is not authenticated and the endpoint requires authentication, ASP.NET Core returns a 401. If the user is authenticated but lacks the required claims or roles, it returns a 403.

What Is a ClaimsPrincipal and How Is It Structured?

A ClaimsPrincipal is the security identity model in .NET. It contains one or more ClaimsIdentity objects, each representing an authenticated identity (you can have identities from multiple schemes simultaneously). Each ClaimsIdentity holds a collection of Claim objects โ€” key/value pairs representing facts about the user: name, email, role, tenant ID, custom application-level attributes.

When you access HttpContext.User, you're working with the ClaimsPrincipal. Methods like User.IsInRole(), User.HasClaim(), and User.Identity.IsAuthenticated all operate on this model.


Intermediate Questions

How Do You Configure JWT Bearer Authentication in ASP.NET Core?

JWT Bearer is configured via AddAuthentication().AddJwtBearer(). The key parameters in JwtBearerOptions are:

  • Authority โ€” the URL of the identity provider issuing the tokens. ASP.NET Core fetches the OpenID Connect discovery document from this URL to obtain signing keys automatically.
  • Audience โ€” the expected aud claim value. Prevents tokens issued for one service from being replayed at another.
  • TokenValidationParameters โ€” controls expiry validation, issuer validation, signing key validation, and clock skew.

For APIs that don't use an external identity provider, you set the signing key and issuer manually in TokenValidationParameters. The framework validates the token signature and claims on every request without you writing validation logic.

What Is the Difference Between Role-Based and Policy-Based Authorization?

Role-based authorization ([Authorize(Roles = "Admin")]) is a binary gate: either the user has the role claim, or they don't. It's simple but brittle โ€” role names are strings, requirements change, and roles tend to accumulate over time without clear semantics.

Policy-based authorization ([Authorize(Policy = "CanApproveOrders")]) decouples the authorization rule from the endpoint. A policy is a named collection of one or more IAuthorizationRequirement objects. Requirements are evaluated by IAuthorizationHandler implementations. This approach lets you encode complex rules โ€” checking a combination of claims, querying a database, or considering resource state โ€” without scattering that logic across your controllers.

For any production system beyond small internal tools, policy-based authorization is the correct choice. It scales, it's testable, and it separates concerns properly.

What Are Claims Transformations and When Should You Use Them?

Claims transformation lets you augment or modify the ClaimsPrincipal after authentication but before authorization runs. You implement IClaimsTransformation and register it in DI.

Common use cases:

  • Adding application-specific roles or permissions not present in the token (e.g., loading them from a database by user ID).
  • Normalising claim type URIs from an external identity provider to shorter, internal claim names.
  • Injecting tenant context into claims for multi-tenant applications.

Be careful: IClaimsTransformation is called on every request, including when checking authorization for resources. If you're hitting a database, cache aggressively. Also note that claims added here are not written back to the token โ€” they exist only for the lifetime of the request.

How Do Refresh Tokens Work and What Are the Security Implications?

A JWT access token is short-lived โ€” typically 5 to 15 minutes โ€” to limit the blast radius of theft. A refresh token is a long-lived, opaque credential stored securely by the client, used to obtain a new access token without re-authenticating the user.

Security implications for senior developers:

  • Refresh token rotation โ€” every time a refresh token is used, it is invalidated and a new one is issued. This enables detection of replay attacks: if the old token is used again after rotation, an attacker has stolen it, and the server can revoke the entire token family.
  • Storage โ€” refresh tokens must not be stored in localStorage (exposed to XSS). Use HttpOnly secure cookies or encrypted server-side sessions.
  • Revocation โ€” JWTs are stateless; you cannot revoke them before expiry without a blocklist or token introspection endpoint. Keeping access tokens short-lived and implementing refresh token rotation is the practical mitigation.

In ASP.NET Core API contexts, refresh token logic is usually handled by the identity provider (Duende IdentityServer, Keycloak) rather than your API service directly.

How Does ASP.NET Core Handle Resource-Based Authorization?

Standard endpoint-level policies can't make decisions based on a specific resource instance (e.g., "can this user edit this document?"). For resource-based authorization, ASP.NET Core provides IAuthorizationService.

You inject IAuthorizationService into your controller or service, pass the resource and a policy name (or requirement) to AuthorizeAsync, and act on the result. This keeps authorization logic out of your domain model while enabling per-resource decisions. The resource object is passed to your IAuthorizationHandler, where you can inspect both the user's claims and the resource's properties before returning Succeed or Fail.


Advanced Questions

How Would You Design a Multi-Tenant Authorization System in ASP.NET Core?

A robust multi-tenant authorization design typically involves three layers:

1. Tenant isolation via claims โ€” the JWT (or session) contains a tenant_id claim. Claims transformation enriches this with the tenant's configuration (feature flags, plan tier, allowed operations). All authorization policies have access to this context.

2. Policy-based tenant scoping โ€” an IAuthorizationRequirement called TenantScopeRequirement is applied globally via endpoint filters or a base controller. Its handler checks that HttpContext.User's tenant claim matches the resource's tenant. Cross-tenant data access fails at the authorization layer, not the data layer.

3. Data-layer enforcement as backup โ€” all queries include the tenant ID in WHERE clauses or via EF Core global query filters. This is defence-in-depth: authorization is the first gate, data scoping is the second.

Common pitfalls: caching claims per-tenant without proper invalidation when tenant settings change, and forgetting to apply tenant scoping to background jobs that run outside the HTTP context.

What Is the OAuth2 Authorization Code Flow with PKCE and Why Is It Required for SPAs?

The Authorization Code Flow with PKCE (Proof Key for Code Exchange) is the secure OAuth2 flow for public clients โ€” clients that cannot maintain a confidential client secret (SPAs, mobile apps).

The flow:

  1. The client generates a cryptographically random code_verifier and derives a code_challenge (SHA-256 hash of the verifier).
  2. The client sends the code_challenge to the authorization endpoint when initiating login.
  3. After user consent, the identity provider returns an authorization code.
  4. The client exchanges the code plus the original code_verifier at the token endpoint.
  5. The identity provider hashes the verifier and compares it to the stored code_challenge. If they match, tokens are issued.

Without PKCE, an authorization code intercepted in transit (e.g., via a malicious browser extension or redirect URI exploit) can be exchanged for tokens. PKCE ensures that only the client that initiated the flow can complete it, because only that client knows the code_verifier.

ASP.NET Core's AddOpenIdConnect() handler sends PKCE parameters automatically when configured with UsePkce = true.

How Do You Implement a Custom Authorization Requirement with Dynamic Rules?

You implement IAuthorizationRequirement to carry parameters and IAuthorizationHandler<T> to evaluate them. The handler receives the AuthorizationHandlerContext containing the user and optionally the resource.

For dynamic rules that can't be encoded statically (e.g., permission checks stored in a database), you inject the relevant service into the handler via DI. The handler calls the service, checks the result, and calls context.Succeed(requirement) or context.Fail().

Register the handler in DI as a transient or scoped service (use scoped if you need EF Core DbContext). Register the policy by name in AddAuthorization(). The ASP.NET Core authorization framework will resolve the handler from DI automatically.

A key subtlety: multiple handlers can be registered for the same requirement. By default, all must succeed unless any calls context.Fail() with FailCalled = true. Use this to compose authorization rules from independent, single-responsibility handlers.

How Does ASP.NET Core Identity Differ from JWT Bearer Authentication and When Should You Use Each?

ASP.NET Core Identity is a full membership system: it manages user accounts, password hashing, email confirmation, two-factor authentication, lockout, and role management. It persists user data to a store (typically EF Core + SQL). It uses cookie authentication by default.

JWT Bearer is a token validation mechanism, not a user management system. It validates tokens issued by some external source (your own token service, an identity provider, or Azure AD). It carries no notion of user accounts, password storage, or lockout.

When to use Identity:

  • Web applications with local accounts and cookie-based sessions.
  • Smaller applications where you own the full stack and don't need SSO or external IdP federation.

When to use JWT Bearer:

  • APIs accessed by SPAs, mobile clients, or other services.
  • Microservices where a central identity provider issues tokens.
  • Any scenario requiring SSO, federation, or external identity providers.

In enterprise architectures, the typical pattern is: a dedicated identity service (Duende IdentityServer or Keycloak) issues tokens using the OAuth2/OIDC protocols, and all API services validate those tokens via JWT Bearer. ASP.NET Core Identity may power the identity service itself, but the downstream APIs never see it directly.

What Are Anti-Forgery Tokens and When Are They Relevant for APIs?

Anti-forgery tokens (CSRF tokens) protect against Cross-Site Request Forgery โ€” attacks where a malicious site tricks an authenticated user's browser into making a request to your server using the user's existing session cookies.

For cookie-based web applications, CSRF protection is mandatory. ASP.NET Core provides IAntiforgery and the [ValidateAntiForgeryToken] attribute.

For APIs using JWT Bearer authentication, CSRF is generally not a concern because JWT tokens must be explicitly sent in the Authorization header โ€” a browser's automatic cookie-sending behaviour does not apply. Cross-origin requests with custom headers are blocked by CORS unless explicitly allowed.

The exception: if your API uses cookie-based JWT storage (e.g., HttpOnly cookie holding the access token), CSRF protection becomes relevant again. In this case, you combine SameSite cookie policy (SameSite=Strict or SameSite=Lax) with custom request headers as a CSRF mitigation strategy.

How Do You Secure Service-to-Service Communication in a Microservices Architecture on ASP.NET Core?

The primary pattern is the Client Credentials Flow (OAuth2 grant type). Each service is registered as a confidential client with the identity provider. When service A needs to call service B, it requests an access token from the identity provider using its client_id and client_secret. Service B validates the token via JWT Bearer authentication.

Key considerations:

  • Scopes define what service A is permitted to do on service B. Service B's authorization policies check the scope claim.
  • Token caching โ€” access tokens must be cached until near-expiry. Requesting a new token per outbound call is a common performance mistake. Use ITokenAcquisition (from Microsoft Identity Web) or a custom DelegatingHandler that manages token lifecycle.
  • Mutual TLS (mTLS) โ€” an additional layer where both services present client certificates. Used in zero-trust architectures or when regulatory requirements demand cryptographic proof of service identity in addition to token-based auth.
  • Short-lived tokens โ€” service-to-service tokens should have short TTLs. A compromised token from an internal service should not be usable for long.

FAQ

What is the difference between AddAuthentication and AddAuthorization in ASP.NET Core?

AddAuthentication registers authentication services and scheme handlers โ€” it defines how identities are verified (e.g., JWT Bearer, cookies). AddAuthorization registers the authorization system โ€” it defines what authenticated identities are allowed to do via policies and requirements. Both must be registered and their respective middleware (UseAuthentication, UseAuthorization) added to the pipeline in the correct order.

Can you have multiple authentication schemes active at the same time in ASP.NET Core?

Yes. ASP.NET Core supports multiple authentication schemes simultaneously. You can combine JWT Bearer for API routes and cookie authentication for MVC routes in the same application. Set a default scheme or specify the scheme explicitly on individual endpoints using the [Authorize(AuthenticationSchemes = "Bearer")] attribute or endpoint metadata. The authentication middleware tries schemes in the order they are configured.

How do you prevent JWT token replay attacks in ASP.NET Core?

Replay attacks are mitigated by: short access token lifetimes (5โ€“15 minutes), refresh token rotation (invalidate the used refresh token on every use), maintaining a server-side revocation list (jti claim blocklist) for sensitive operations, and using HTTPS exclusively so tokens cannot be intercepted in transit. For highly sensitive contexts, consider token binding or proof-of-possession (DPoP) tokens.

What is the purpose of the scope claim in OAuth2 and how does ASP.NET Core use it?

The scope claim defines what the bearer of the token is permitted to do at the resource server. When an API registers an authorization policy that requires a specific scope, the IAuthorizationHandler checks the scope claim in the token. This is the primary mechanism for enforcing coarse-grained, service-level permissions in OAuth2. In ASP.NET Core, you check scopes using HttpContext.User.HasClaim("scope", "orders.read") or via a requirement handler.

What is OpenID Connect and how does it relate to OAuth2?

OAuth2 is an authorization framework โ€” it defines how to obtain and use access tokens. It deliberately says nothing about how user identity is conveyed. OpenID Connect (OIDC) is an identity layer built on top of OAuth2. It adds the concept of the ID token (a JWT that contains user identity claims: sub, email, name, iat, exp) and standardises the UserInfo endpoint. In ASP.NET Core, AddOpenIdConnect() implements the OIDC protocol, while AddJwtBearer() only handles the downstream access token validation.

How do you test authorization policies in ASP.NET Core without running a full integration test?

You can unit-test authorization handlers by constructing an AuthorizationHandlerContext with a ClaimsPrincipal, the requirement, and the optional resource, then calling the handler directly. Assert that context.HasSucceeded or context.HasFailed returns the expected value. For policy-level tests (combining multiple requirements), use IAuthorizationService directly with WebApplicationFactory in an integration test, configuring a test JWT or using WithWebHostBuilder to replace the authentication scheme with a test scheme that injects a pre-built ClaimsPrincipal.

What is the Data Protection API in ASP.NET Core and how does it relate to authentication?

The Data Protection API (IDataProtector) provides symmetric encryption for sensitive data that must be stored temporarily and later decrypted โ€” specifically by the same application or application cluster. ASP.NET Core uses it internally to encrypt authentication cookies, anti-forgery tokens, and the payload of ITicketStore session tickets. For APIs using JWT Bearer only, you rarely interact with Data Protection directly. It becomes important when you host multiple instances and need to share keys (via Azure Key Vault, Redis, or a shared file system) so that cookies encrypted by one instance can be decrypted by another.


โ˜• Prefer a one-time tip? Buy us a coffee โ€” every bit helps keep the content coming!


For more .NET tutorials and premium source code, visit Coding Droplets and subscribe to the YouTube channel.