angular identity

in ASPNET Core, Informatica

Angular, ASPNet Core 3.1 e autenticazione

Reading Time: 4 minutes

Nel post precedente erano state analizzate alcune metodologie per impostare l’autenticazione in un’applicazione angular e ASPNet Core 3.1. Negli esempi, non ho trattato architettutre “complesse” di autenticazione, come ad esempio IdentityServer4 che ho già trattato in un articolo introduttivo. In questo post, analizzeremo i componenti da configurare all’interno di un’applicazione angular per garantire l’accesso protetto alle risorse.

Per farlo, partiamo dal template base messo a disposizione da ASPNet Core 3.1, che consente di creare tutto quello che serve per la gestione di pagine protette. Dalla linea di comando possiamo utilizzare il comando:

dotnet new angular -o <output_directory_name> -au Individual

che utilizza l’autenticazione Individual. Lo stesso risultato può essere utilizzato da Visual Studio 2019, creando una WebApp ed indicando di voler utilizzare Angular con l’autenticazione Individual.

Descrizione dell’ applicazione

L’applicazione utilizza l’autenticazione messa a disposizione da ASPNET Core per la registrazione e l’autenticazione degli utenti. Il template standard mette a disposizione due pagine:

  • counter : visualizza un contatore che viene incrementato premendo un pulsante. Non richiede l’autenticazione
  • fetch-data: visualizza una serie di dati relativi alla temperatura (dati che provengono dal controller dell’api WeatherForecast e quindi dal relativo controller). Questa pagina richiede l’autenticazione.

Inoltre, l’applicazione utilizza SqlLite per la memorizzazione degli utenti, utilizzando la stringa di connessione “DefaultConnection”.

ASPNET Core 3.1

Partendo dal file di configurazione Startup.cs, analizziamo le righe di codice più importanti:

   public void ConfigureServices(IServiceCollection services)
        {
            ...

            services.AddAuthentication()
                .AddIdentityServerJwt();

            ...

            // In production, the Angular files will be served from this directory
            services.AddSpaStaticFiles(configuration =>
            {
                configuration.RootPath = "ClientApp/dist";
            });
        }

in particolare l’aggiunta dell’autenticazione e della generazione di token in formato Jwt. Quando l’applicazione verrà pubblicata in produzione, i file statici saranno presenti all’interno della cartella ClientApp/dist.

 public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            ...
            ...
            app.UseAuthentication();
            app.UseIdentityServer();
            app.UseAuthorization();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller}/{action=Index}/{id?}");
                endpoints.MapRazorPages();
            });

            app.UseSpa(spa =>
            {
                // To learn more about options for serving an Angular SPA from ASP.NET Core,
                // see https://go.microsoft.com/fwlink/?linkid=864501

                spa.Options.SourcePath = "ClientApp";

                if (env.IsDevelopment())
                {
                    spa.UseAngularCliServer(npmScript: "start");
                }
            });
        }

L’altro aspetto importante riguarda l’attivazione dell’autenticazione, di identity server, delll’autorizzazione, della definizione delle rotte di default e dell’utilizzo del middleware per la gestione di applicazioni SPA (nel nostro caso presenti all’interno della cartella ClientApp). Questo tipo di configurazione non presenza particolari differenze da quelle utilizzate nelle applicazioni ASPNET Core MVC.

L’applicazione necessita di accedere i dati dal controller WeatherForecastController, che come detto in precedenza, non è accessibile senza autenticazione. Ecco quindi, la parte di decoratori presenti nella dichiarazione del controller:

    [Authorize]
    [ApiController]
    [Route("[controller]")]
    public class WeatherForecastController : ControllerBase
    {
      ....

trattandosi di un API Controller viene utilizzato [ApiController], il nome della rotta viene definitito dal nome del controller, ma soprattutto la presenza dei [Authorize]. La presenza di [Authorize] richiede l’autenticazione per poter accedere alle action del controller. La presenza di [Authorize] nella defizione del controller non consente a nessuna Action di essere eseguita senza autenticazione.

La action che dovrà essere richiamata per lo scaricamento dei dati è:

        [HttpGet]
        public IEnumerable<WeatherForecast> Get()
        {
            var rng = new Random();
            return Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = DateTime.Now.AddDays(index),
                TemperatureC = rng.Next(-20, 55),
                Summary = Summaries[rng.Next(Summaries.Length)]
            })
            .ToArray();
        }

che risponderà ad una chiamata di tipo GET.

Applicazione Angular

L’applicazione Angular è posizionata all’interno della cartella ClientApp, e contiene tutti gli elementi standard per il suo funzionamento. In particolare, all’interno della cartella ClientApp/src/app troviamo il core dell’applicazione ed il file app.module.ts , dove vengono configurate le route dell’applicazione:

RouterModule.forRoot([
      { path: '', component: HomeComponent, pathMatch: 'full' },
      { path: 'counter', component: CounterComponent },
      { path: 'fetch-data', component: FetchDataComponent, canActivate: [AuthorizeGuard] },
    ])

In particolare, oltre alla route predefinita sono presenti la route counter e la route fetch-data. Poichè la route fetch-data deve essere raggiunbile solo se siamo autenticati anche nel caso dell’applicazione client è necessario utilizzare uno strumento in grado di farlo: AuthorizeGuard.

E’ importante notare che proteggere una route non significa proteggere automaticamente l’end-point. Ecco perchè, lato ASPNET Core è stato inserito [Authorize] sulla rotta.

Questo parametro indica che la rotta è una rotta consentita o meno. In pratica si tratta di interfaccia che indica se la rotta è consentita o meno: la decisione viene presa in base al valore ritornato (true o false) da una classe che implementa tale interfaccia. Ci sono cinque differenti tipologie che sono chiamati in una particolare sequenza:

  • CanActivate
  • CanActivateChild
  • CanDeactivate
  • CanLoad
  • Resolve

Nel nostro caso la rotta deve essere eseguita solo nel caso in cui l’utente è autenticato: nel nostro caso utilizziamo CanActivate.

Per maggiori dettagli sulla tipologia, si può fare riferimento alla documentazione ufficiale di Angular.

Sempre all’interno di app.module.ts troviamo anche :

providers: [
    { provide: HTTP_INTERCEPTORS, useClass: AuthorizeInterceptor, multi: true }
  ],

che indica la presenza di un HTTP_INTERCEPTORS, che consente di intercettare le richieste HTTP eventualmente modificandole. Nel nostro caso, dovremmo gestire il token di autenticazione. Nella configurazione viene specificata la classe che dovrà essere utilizzata come “interceptor” AuthorizeInterceptor.

La parte dell’applicazione dedicata all’autenticazione è tutta raggruppata all’interno della cartella api-authorization.

In particolare, all’interno della cartella troviamo i component:

  • login
  • login-menu
  • logout

che si occupano rispettivamente del login, della visualizzazione del menu del login e del logout.

All’interno della root di api-authorization troviamo anche altri file:

  • api-authorization.constants.ts: contiene le costanti utilizzate dall’applicazione
  • api-authorization.module.ts: il component all’interno del quale sono definite le route di api-authorization
  • authorize.guard.ts: il file dove viene implementata l’autenticazione. all’interno di questo file è presenta la classe AuthorizeGuard che implementa CanActivate, la tipologia che abbiamo indicato in precedenza all’interno del file app.module.ts
  • authorize.interceptor.ts: all’interno del quale viene implementato l’interceptor per aggiungere il token alle richieste http. Il token è di tipo Bearer. In pratica ad ogni chiamata viene aggiunto l’header nel formato:  Authorization: `Bearer TOKEN`
  • authorize.service.ts: per poter gestire le fasi di autenticazione è stato introdotto un service, che viene iniettato all’interno dei component che necessitano di collegarsi

In questo post abbiamo analizzato il template messo a disposizione da ASPNET Core per la creazione di applicazioni angular con autenticazione.

Nei prossimi post, utilizzeremo IdentityServer4 per creare l’endpoint dell’autenticazione.