kohanyirobert / dotnet-authn

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

ASP.NET Web AuthN

Samples and notes of different authentication techniques demonstrated with ASP.NET Core 7 sample projects.

Prequel

Goals

  • Have examples of different authentication techniques
  • Build upon builtin dotnet project templates
  • See how things work with and without Identity
  • Learn!

Non-goals

  • Be super-duper focused on what is the most secure way of doing things (it depends)
  • To create setups for same-site/cross-site/secure/insecure/etc. cookie scenarios in CORS/no-CORS settings

First Things First

A quick refresher about .NET for everyone

  • .NET Framework is the past
  • .NET Core is kind of the past also, but still relevant to some extent
  • .NET is the new and shiny thing

About ASP.NET

  • ASP.NET is old news
  • ASP.NET Core is the new shiny thing

.NET

With .NET and ASP.NET Core one can write

  • "regular" server-side rendered apps with MVC (using Razor as the backend templating language)

  • use Razor Pages, which is

    1. a different way of organizing MVC apps
    2. also it is referred to as being MVVM (but I didn't find any evidence of this in any Microsoft docs)
    3. it has data-binding support as opposed to MVC; something similar to what WPF has, but WPF has real two-way data-binding, Razor Pages does not ... so WPF is MVVM, Razor Pages are somewhat similar (but a fact is I personally have no idea what MVVM is 😁)
  • thirdly Blazor which is writing server- and/or client-side rendered webapps using .NET languages (C#) and Razor

    1. server-side Blazor is what it sounds like, but it's leverages SignalR (which is a layer on top WebSockets or as a fallback HTTP most probably) to only send DOM changes over a SignalR connection, the other main difference being a component-oriented approach (Blazor and/or Razor components - I'm unsure, this is pretty confusing 😵)
    2. client-side Blazor is trickier as it uses WebAssembly to run .NET code on the client 😲 so it's client-side C# essentially, but the goal is the same - small changes to the DOM instead of big ass complete page reloads, aka more responsiveness
  • and lastly using a (Web) API project without any built-in Microsoft-endorsed UI solution using

    1. React
    2. Angular
    3. insert another popular SPA framework here

This sums it up very nicely..

.NET Framework

With .NET Framework and ASP.NET (watch out, no Core or anything) one could

  • still write web apps in older ways, most notably using WebForms which uses (or used at a point in history) non-standard web "stuff" and was left behind; it still works, people know it, use it, just as with WinForms
  • and probably this framework also has something akin to a Web API and/or Restful project archetype that was left behind and completely replaced with the advent of .NET Core/.NET and Web API projects

Authentication vs Authorization

This is what it is, but it is crucial to get certain aspects of what's outlined further in the document.

ASP.NET Core Identity

This is an API shipped with/as an extension for ASP.NET Core which is kind of hard to miss in the ecosystem. It pretty much always comes up when dealing with authentication in .NET. It supposed to help things, but this isn't all that obvious. In order to tackle how to best leverage it let's focus on it in the examples.

Important: since Identity is tailored to MVC style apps it works wonky when used in an SPA, meaning when an endpoint on a backend is accessed without the required authorization level instead of just returning a 401/403 status code to the frontend it sends a 302 with a redirect to /Account/Login which is not always useful. If this feature needs to be disabled here's a way to do it. This related Microsoft document might be of interest too.

What now?

Project types

Let's settle on the kind of project type from the above selection to focus on

  • ASP.NET Core MVC with Razor (but not Razor Pages)
  • ASP.NET Web API with React

Coincidentally these two are the ones I personally know the most about at this point in time 🎉

Authentication Types

I personally seen the following authentication types/formats while working in the IT industry

  1. Basic authentication
  2. Cookie authentication using forms
  3. JWT token-based authentication using the Bearer scheme
  4. OAuth2, OICD and friends

There may be several others, and even the ones I write may not be real "types" of authentication methods. I'll focus on the first three, because OAuth2, OICD, etc. are on a whole other complexity level, maybe in a follow up project similar to this.

Sample Projects

Based on what's above the following "matrix' can be deducted for better or worse.

  1. Not using ASP.NET Core Identity
    1. Basic Auth w/o Identity
      1. MVC (mvc-basic-without-identity)
      2. API (api-basic-without-identity)
      3. SPA (spa-basic-without-identity)
    2. Cookie Session Stateful(?) Auth w/o Identity
      1. MVC (mvc-stateful-without-identity)
      2. API (api-stateful-without-identity)
      3. API + React (spa-stateful-without-identity)
    3. JWT Stateless(?) Auth w/o Identity
      1. MVC (mvc-stateless-without-identity)
      2. API (api-stateless-without-identity)
      3. API + React (spa-stateless-without-identity)
  2. Using ASP.NET Core Identity
    1. Basic Auth /w Identity
      1. MVC (mvc-basic-with-identity)
      2. API (api-basic-with-identity)
      3. API + React (spa-basic-with-identity)
    2. Cookie Session Stateful(?) Auth /w Identity
      1. MVC (mvc-stateful-with-identity)
      2. API (api-stateful-with-identity)
      3. API + React (spa-stateful-with-identity)
    3. JWT Stateless(?) Auth /w Identity
      1. MVC (mvc-stateless-with-identity)
      2. API (api-stateless-with-identity)
      3. API + React (spa-stateless-with-identity)

This is kind of an simplification of things and won't always be super logical and straightforward, but it's okay. Some combination of things doesn't really work, I may skip those without prior warning.

TODO: what comes below will be restructured.

No Identity

~~JWT?~~Stateless? Auth w/o Identity

  • JWT is actually just something that travels around the wire, a different kind of "session identifier", it's not a protocol

  • E.g. one could use OAuth2 with JWT or something else, and JWT tokens can travel as cookies between client and server

  • It's important to note that using cookies with JWT makes it a simple stateless authentication solution

  • After a client receives a JWT it can be then used to:

    • authentication (just as basic authentication)
    • authorization (the token can encapsulate authorization details, claims of what the holder of the token can do in a system)
    • as it is digitally signed it can be passed around freely
    • this comes at a price: a token shouldn't be valid forever, it is usually valid for a specific amount of time after it needs to refreshed, which fact in itself present several headaches :)
  • Here we won't use OAuth2 or anything as that's a complex thing, a framework for authentication (with authorization servers and resource servers and whatnot)

WebApplication7 = MVC

  • First and most important: JWT is supported via a first-party library

    • add it via dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
    • and/or via some GUI functionality
  • On a side note: NuGet dependencies are listed as follows in .NET Rider

  • The MVC GUI is useless here, since by default AddJwtBearer configures the system in a way that the ASP.NET server expects the token via the Authorization header and this cannot be made available via forms, or clicking on links

  • However, based on this guide one can generate JWT tokens (for development - in a production scenario one would get such tokens from authorization server)

  • One can create such tokens with commands like

    • dotnet user-jwts create –role Admin
    • Different roles or claims can be encapsulated into the claim and this is respected by the [Authorize] attribute
  • And after test the token like this

WebApplication8 = API

  • This project example, without a GUI would be same as the previous WebApplication7 so I'll skip it for now

WebApplication9 = API + React

  • The backend would look like as with WebApplication8

  • However, since in React we could handle anything we can build a custom login form that would asynchronously

    • acquire a JWT token after a successful login
    • store that in localStorage (not secure at all)
    • use the token stored in localStorage in subsequent API calls
  • Here the user-jwts could work like instead having a working "login" we could just create a localStorage entry manually and set the token obtained from the command like, but that's not very cool

  • So let's create a security token server/service or STS embedded into the backend

  • Note: in the AccountController the FromBody attribute is used on the method parameters instead of FromForm

  • Validation works by default iff a proper token received by the backend and user secrets are correctly in place

WebApplication9b = API + React

  • Same thing as before just with using cookies to transmit token between client and server instead of the Authorization header and the Bearer keyword

  • No new project just a different commit (I should have done things like this since earlier)

  • This version is probably really picky when dealing with a cross-site request scenario

    • Maybe demo this by getting the JWT cookie and making call in the browser's console to a 3rd party endpoint (something like the Pokemon API)

With Identity

Basic Auth w/ Identity

WebApplication10 = MVC

  • Even though there's no real login as the credentials need to be present on the client side Identity still could be used for validating login credentials and registration

  • Identity's default UI is tuned towards form-based login, which basic authentication is not suited for

  • As we don't want to use cookie-based authentication we don't need AddDefaultIdentity

  • When using "Individual authentication" the MapRazorPages call is added to Program.cs, we don't need that

  • When using AddIdentityCore no SignInManager is configured by default

  • Also need to add AddRoles to be able fiddle with roles

  • By default "strong" passwords are used, this is turned off in the options to AddIdentityCore

  • AddAuthentication needs to be configured as with WebApplication1, the main difference is in how BasicAuthenticationHandler uses SignInManager to use the underlying database

WebApplication11 = API

  • Skipping it, would work as WebApplication11 just without the default MVC index page, etc.

WebApplication12 = API + React

  • Again, same as WebApplication11 just with React, see WebApplication3 for details

~~Cookie?Session?~~Stateful? Auth w/ Identity

WebApplication13 = MVC

  • This is the defacto default way of using Identity, defaults works like a charm

WebApplication14 = API

WebApplication15 = API + React

~~JWT?~~Stateless? Auth w/ Identity

WebApplication16 = MVC

WebApplication17 = API

WebApplication18 = API + React

Miscellaneous stuff

About


Languages

Language:JavaScript 44.6%Language:C# 34.3%Language:HTML 16.9%Language:CSS 4.2%