@notiz/nest-auth-formly

Nest Auth UI with TailwindCSS and Formly for Angular

Usage no npm install needed!

<script type="module">
  import notizNestAuthFormly from 'https://cdn.skypack.dev/@notiz/nest-auth-formly';
</script>

README

NestAuthFormly

npm version

Works together with nest-auth

Installation

Requires Tailwind, Tailwind Forms Plugin, Formly and Tailwind-Formly

# select forms plugin
ng add ngx-tailwind

# alternative install via
npm install -D @tailwindcss/forms

ng add @ngx-formly/schematics
# or
npm i @ngx-formly/core

npm i @notiz/formly-tailwindcss

Your AppModule should look like this and import HttpClientModule

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { ReactiveFormsModule } from '@angular/forms';
import { FormlyModule } from '@ngx-formly/core';
import { FormlyTailwindcssModule } from '@notiz/formly-tailwindcss';
import { HttpClientModule } from '@angular/common/http';

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    AppRoutingModule,
    ReactiveFormsModule,
    FormlyModule.forRoot({
      extras: { lazyRender: true },
    }),
    FormlyTailwindcssModule,
    HttpClientModule,
  ],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

Now install ... NOT YET AVAILABLE

Nest Application works great with code generators such as ng-openapi-gen for Rest or graphql-code-generator for GraphQL to create Angular services for your endpoints.

Usage

NestAuthFormlyModule

Import NestAuthFormlyModule which includes JwtInterceptor, read more about Interceptor.

import { environment } from '@env/environment';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { ReactiveFormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { FormlyModule } from '@ngx-formly/core';
import { FormlyTailwindcssModule } from '@notiz/formly-tailwindcss';

import { NestAuthFormlyModule } from '@notiz/nest-auth-formly';

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    AppRoutingModule,
    ReactiveFormsModule,
    FormlyModule.forRoot({
      extras: { lazyRender: true },
      validationMessages: [{ name: 'required', message: 'Is required' }],
    }),
    FormlyTailwindcssModule,
    HttpClientModule,
    NestAuthFormlyModule.forRoot({
      httpInterceptor: {
        // attaches access token only to the allowed endpoints
        allowedList: [
          // `${environment.baseUrl}/users/me`,
          // `${environment.baseUrl}/graphql`,
          `${environment.baseUrl}/*`,
        ],
      },
    }),
  ],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

Social Login

To add social login buttons via formly import SocialLoginModule to register formly type socialLogin.

import { environment } from '@env/environment';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { ReactiveFormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { FormlyModule } from '@ngx-formly/core';
import { FormlyTailwindcssModule } from '@notiz/formly-tailwindcss';

import { NestAuthFormlyModule } from '@notiz/nest-auth-formly';
import { SocialLoginModule } from '@notiz/nest-auth-formly/social';

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    AppRoutingModule,
    ReactiveFormsModule,
    FormlyModule.forRoot({
      extras: { lazyRender: true },
      validationMessages: [{ name: 'required', message: 'Is required' }],
    }),
    FormlyTailwindcssModule,
    HttpClientModule,
    NestAuthFormlyModule,
    SocialLoginModule,
  ],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

Interceptor

JwtInterceptor adds the access token, if available, to the request as Authorization Bearer token.

Import NestAuthFormlyModule or add JwtInterceptor as a provider

import { environment } from '@env/environment';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { ReactiveFormsModule } from '@angular/forms';
import { FormlyModule } from '@ngx-formly/core';
import { FormlyTailwindcssModule } from '@notiz/formly-tailwindcss';
import { JwtInterceptor } from '@notiz/nest-auth-formly';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    AppRoutingModule,
    ReactiveFormsModule,
    FormlyModule.forRoot({
      extras: { lazyRender: true },
      validationMessages: [{ name: 'required', message: 'Is required' }],
    }),
    FormlyTailwindcssModule,
    HttpClientModule,
  ],
  providers: [
    { provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true },
  ],
  bootstrap: [AppComponent],
})
export class AppModule {}

Guards

AuthGuard protects your routes from not authenticated users.

import { AuthGuard } from '@notiz/nest-auth-formly';

const routes: Routes = [
  { path: '', redirectTo: 'dashboard', pathMatch: 'full' },
  {
    path: 'login',
    loadChildren: () =>
      import('./auth/login/login.module').then((m) => m.LoginModule),
  },
  {
    path: 'dashboard',
    loadChildren: () =>
      import('./dashboard/dashboard.module').then((m) => m.DashboardModule),
    canActivate: [AuthGuard],
  },
];

Customize Guard behavior

Customize the behavior of AuthGuard by passing an rxJS pipe through the route data's authGuardPipe key.

The following pre-built pipes are available:

Pipes  Description
loggedIn The default pipe, rejects if the user is not authenticated
hasRole Rejects authenticated users without specific role
hasRoleOf Rejects authenticated users without one of the specified roles
emailVerified Rejects authenticated users without verified email
redirectLoggedInTo Redirect authenticated users to a different route
redirectUnauthorizedTo Redirect unauthenticated users to a different route

Example use:

import {
  AuthGuard,
  loggedIn,
  redirectLoggedInTo,
  redirectUnauthorizedTo,
} from '@notiz/nest-auth-formly';

const routes: Routes = [
  { path: '', redirectTo: 'dashboard', pathMatch: 'full' },
  {
    path: 'login',
    loadChildren: () =>
      import('./auth/login/login.module').then((m) => m.LoginModule),
    canActivate: [AuthGuard],
    data: { authGuardPipe: () => redirectLoggedInTo(['']) },
  },
  {
    path: 'profile',
    loadChildren: () =>
      import('./profile/profile.module').then((m) => m.ProfileModule),
    canActivate: [AuthGuard],
    data: { authGuardPipe: () => loggedIn }, // Default behavior of AuthGuard
  },
  {
    path: 'dashboard',
    loadChildren: () =>
      import('./dashboard/dashboard.module').then((m) => m.DashboardModule),
    canActivate: [AuthGuard],
    data: { authGuardPipe: () => redirectUnauthorizedTo(['login']) },
  },
  {
    path: 'admin',
    loadChildren: () =>
      import('./admin/admin.module').then((m) => m.AdminModule),
    canActivate: [AuthGuard],
    data: { authGuardPipe: () => hasRole('ADMIN') },
  },
];

Use canActivate helper and spread syntax to make your routes more readable:

import {
  AuthGuard,
  canActivate
  loggedIn,
  redirectLoggedInTo,
  redirectUnauthorizedTo,
} from '@notiz/nest-auth-formly';

const routes: Routes = [
  { path: '', redirectTo: 'dashboard', pathMatch: 'full' },
  {
    path: 'login',
    loadChildren: () =>
      import('./auth/login/login.module').then((m) => m.LoginModule),
    ...canActivate(() => redirectLoggedInTo(['']))
  },
  {
    path: 'profile',
    loadChildren: () =>
      import('./profile/profile.module').then((m) => m.ProfileModule),
    ...canActivate(() => loggedIn([''])) // Default behavior of AuthGuard
  },
  {
    path: 'dashboard',
    loadChildren: () =>
      import('./dashboard/dashboard.module').then((m) => m.DashboardModule),
    ...canActivate(() => redirectUnauthorizedTo(['login'])),
  },
  {
    path: 'admin',
    loadChildren: () =>
      import('./admin/admin.module').then((m) => m.AdminModule),
    ...canActivate(() => hasRole('ADMIN')),
  },
];

Compose your own pipes

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { canActivate, hasRole } from '@notiz/nest-auth-formly';
import { pipe } from 'rxjs';

const adminOnly = pipe(hasRole('ADMIN'));

const routes: Routes = [
  ...{
    path: 'admin',
    loadChildren: () =>
      import('./admin/admin.module').then((m) => m.AdminModule),
    ...canActivate(() => adminOnly),
  },
];

Using router state

const onlyAllowSelf = (next) =>
  map((user) => !!user && next.params.userId === user.id);

Development

Start the demo of nest-auth

npm i

ng s

# generate rest
npm run codegen:rest
# generate graphql
npm run codegen:graphql