When I started this series, I began with a simple observation: security is often treated as an afterthought. A feature added at the end, a checkbox ticked before launch, a topic left to an external specialist. The result is predictable — systems with solid authentication but flawed authorization, APIs protected with JWT but vulnerable to SQL Injection, applications with correctly configured certificates but no audit trail.
15 articles later, the common thread throughout the series is the same: security is not a layer added on top of the application — it is a property of the entire system, deliberately built, layer by layer, from day one.
This is the story of that common thread.
Act I — Fundamentals: who you are and what you are allowed to do
Any security system answers two fundamental questions: who are you? (authentication) and what are you allowed to do? (authorization). Before answering them technically, we needed to understand why it matters how we ask these questions.
Security by Design established the mental framework for the entire series. The principles — least privilege, defense in depth, fail secure, separation of concerns — are not arbitrary rules. They are lessons drawn from decades of compromised systems. A system designed with these principles from scratch is fundamentally harder to attack than one where security was added later, regardless of how many security tools are installed on it.
With the conceptual foundation laid, we entered the first concrete authentication mechanism you encounter in any modern API: JWT. Not at the level of "how to use the library," but at the level of anatomy: header, payload, signature. What it means that a JWT is signed but not encrypted. Why an expired token does not automatically become invalid on the server if you don't have a revocation mechanism. Understanding these details is the difference between using JWT correctly and using it with an illusion of security.
But JWTs expire — and Refresh Tokens immediately raised the question: how do you renew access without asking the user to authenticate again, and without creating a new vulnerability? Rotation, secure storage, revocation at logout, family invalidation for token theft detection — a poorly implemented refresh token is more dangerous than no refresh token at all.
With identity established, we moved on to authorization. Role-based vs. Policy-based Authorization showed why roles are a simple but rigid mechanism, and why policies allow granularity impossible to achieve with roles. A system that grows in complexity will inevitably outgrow the limits of roles — policies are the correct long-term answer.
And yet, policies are only as good as the data they evaluate. Claims Transformation completed the picture: the token issued by an external Identity Provider contains claims in its format. Your application works with claims in its own format. Transformation is the bridge — and the place where most authorization information is lost if not implemented deliberately.
ASP.NET Identity closed the first act with an inspection of the complete infrastructure that .NET offers out-of-the-box: user management, password hashing, lockout, two-factor authentication, token providers. A system many use superficially, but whose extensibility points could be used to solve real problems without reinventing the wheel.
Act II — Protocols and standards: authentication at scale
Local authentication works until the first difficult question arises: how do you authenticate users from external systems? How do you delegate authentication without sharing passwords? How do you allow an application to act on behalf of a user without knowing their credentials? The answer to all these is the same: open standards.
OAuth2 and OpenID Connect represented the moment the series made the leap from local mechanisms to industry protocols. OAuth2 for delegated authorization, OIDC for the identity layer on top of it. The flows — Authorization Code with PKCE, Client Credentials, Device Flow — are not arbitrary options but responses to specific scenarios. Choosing the wrong flow produces vulnerabilities that are not obvious until it is too late.
The theory of protocols was immediately followed by practice. IdentityServer and Keycloak showed that regardless of the chosen Authorization Server, the ASP.NET Core code on the client and API side remains remarkably similar. The Authority URL and a few configuration details differ. Proof that the OAuth2 and OIDC standards have achieved their goal.
But password- and token-based protocols have a fundamental limitation: there is something to steal. X.509 Certificates introduced a different paradigm: authentication based on public key cryptography, no shared secrets, no passwords that can leak. The private key never leaves the client — the server verifies identity without knowing it. The price is the complexity of managing the certificate lifecycle: issuance, distribution, renewal, revocation.
mTLS took certificates to their logical conclusion: mutual authentication, both parties cryptographically verified before the first byte of data is sent. The standard mechanism in zero-trust architectures and service mesh, where no component is implicitly trusted — not even if it is in the same datacenter.
XML Signing extended the same certificates into a different domain: documents. The same public key cryptography, the same X.509 certificate, but applied to guarantee the integrity and non-repudiation of a document — not a connection. The e-Invoice context gave immediate relevance: in Romania, electronic fiscal documents require a digital signature to be legally valid.
Act III — Active defense: protection against known attacks
Authentication and authorization protect against unauthorized access. But there is a whole class of attacks that do not try to bypass authentication — but to exploit the application from the position of a legitimate user, or to inject malicious behavior into normal flows.
CSRF, XSS, and Injection are almost three decades old and still dominate the OWASP Top 10. Not because they are sophisticated — but because they arise from small decisions made under time pressure: a "temporary" string concatenation, an Html.Raw() for convenience, a cookie without SameSite. ASP.NET Core has excellent tooling for all three — Razor encodes automatically, antiforgery is built-in, EF Core parameterizes by default. Vulnerabilities appear when you bypass these mechanisms, not when you use them.
Blazor WebAssembly brought a necessary perspective shift: when your code runs in the client's browser, the rules change fundamentally. [Authorize] and AuthorizeView in WASM are UX tools, not security guards. Compiled assemblies can be decompiled. Hardcoded secrets are visible. Business logic on the client can be manipulated. The real guards are on the server — the API that validates each request independently, without assuming Blazor has already verified something.
Rate Limiting addressed a different category of attack: not infiltration, but exhaustion. An API without rate limiting can be brought down by anyone — intentionally or accidentally. .NET offers four built-in algorithms, each suitable for different scenarios. The critical architectural decision is not the choice of algorithm, but in-process vs. distributed: if you scale horizontally, in-memory counters are no longer sufficient.
Act IV — Visibility: when prevention is not enough
Prevention is never 100%. Any system sufficiently complex, used by enough people, over a long enough period, will experience a security incident. The question is not if — but when. And especially: how quickly you will detect it, and what information you will have available to understand what happened.
Audit Logging and Security Events closed the series with the answer to this question. A structured, append-only audit trail with adequate retention and automatic alerting turns an incident from a mystery into an investigation with concrete data: who accessed what, at what time, from what IP, with what result. Combined with automatic pattern detection — brute-force, unusual administrative access, abnormal volumes of 403 errors — the system becomes proactive, not just reactive.
The common thread
Looking back at all 15 articles, there are a few themes that appear repeatedly, regardless of the subject:
Understanding before use. JWT, OAuth2, X.509 certificates, XMLDSig — all can be used correctly superficially and incorrectly in depth. Knowing how a JWT works at the byte level, why PKCE exists, how XML canonicalization works — all these details make the difference between using a mechanism and understanding it.
The server is the only source of truth. Authorization checks in Blazor WASM are UX. Client-side validation is convenience. Pricing logic on the client is a suggestion. Any calculation, any security decision, any validation that matters must happen on the server — independently and unconditionally of what the client has verified.
Defense in depth. No single mechanism is sufficient. Antiforgery tokens + SameSite cookie + Origin verification. Output encoding + CSP + HtmlSanitizer. Rate limiting in-app + WAF + APIM. Each independent layer compensates for possible failures of the others.
Complexity lives in configuration, not in code. From audience validation in JWT, to middleware order in rate limiting, to PreserveWhitespace in XML signing, to IP spoofing behind a proxy — the most dangerous mistakes are not visible logic errors in code. They are configuration decisions with non-obvious consequences.
The lifecycle matters. An expired X.509 certificate that stops production at 3 a.m. A refresh token without rotation that allows unlimited impersonation. An audit log without TTL that grows until it fills storage. Security is not a moment — it is a continuous maintenance process.
What’s next
This series covers the foundations of security in ASP.NET Core. But foundations are just the beginning — security is a living field that evolves with technology, threats, and the increasingly complex requirements of modern systems.
Follow the articles on LudoProgramming. The next topics are already taking shape — some will deepen concepts from this series, others will open completely new territories. You don’t know what’s coming, but if you have read the 15 articles so far, you know it’s worth waiting for.
If you have followed the series from article #1 to here: thank you. I hope you found at least one concept that changed the way you think about security in your projects.
If this is the first article you have reached: every link in the retrospective leads to the complete implementation. You can read in order, or jump directly to the topic that is relevant to you now. The series is designed so that each article is useful and independent.