TL;DR: Many Angular applications need some form of authentication to protect different sections of the app. Auth0 is the perfect tool to do just that. But what makes these two awesome tools even better is a simple way to deploy the application, and that's where Netlify comes in. In just a few short minutes, you can create a new Angular application, secure it with Auth0, and deploy it to Netlify.
"Learn how to build an Angular application, secure it with Auth0, and deploy to Netlify."
Tweet This
Deploying an Angular with Auth0 App to Netlify
Auth0 has integrations with many languages and frameworks, and Angular is no exception. Using the libraries that the team at Auth0 has created, you can secure your application and be confident that your users' data will be protected. The combination of security from Auth0 and route guards from Angular will prevent users that aren't logged in from getting into sensitive parts of your application.
Once your application is completed, Netlify is a great place to deploy that application. There is no need to manage a server, NGINX, certificates, or scaling due to high traffic. Netlify makes it easy to build and deploy your application automatically when you push to certain branches, and best of all, it's free in many situations.
In this article, you'll create an Angular application, secure it with Auth0 and route guards, and deploy it to Netlify. Let's go!
Create an Angular Project
To get started, you'll need to create a new Angular project. This example uses bootstrap for some styling, and you're welcome to do that as well. Before you create the application, make sure that the Angular CLI is installed and available on your computer. Run the following command, which will either install the CLI or update it:
npm install -g @angular/cli
Once you're sure the CLI is installed on your computer, run the following command in your terminal:
ng new my-ng-auth0-netlify --routing --style scss
This command creates a new Angular application. The --routing
flag initiates routing in the app, and the --style scss
flag sets the app's style file extensions to SCSS. After running the command, change into the new directory and install bootstrap:
cd my-ng-auth0-netlify
npm install bootstrap
You now have a new Angular project. Include the bootstrap styles in the styles.scss
file in the src
directory like this:
// src/styles.scss
@import '~node_modules/bootstrap/scss/bootstrap.scss';
Replace the default HTML in the src/app/app.component.html
file with the following content:
<!-- src/app/app.component.html -->
<ul>
<li>
<a routerLink="/home">Home</a>
</li>
<li>
<a routerLink="/profile">Profile</a>
</li>
</ul>
<div class="container">
<router-outlet></router-outlet>
</div>
Your application is now set up for routing, and a real navigation bar will be added later. The next thing you'll do is to create the components needed for the routes in the application. For this demonstration, you'll just create two components, a HomeComponent
and a ProfileComponent
:
ng generate component home
ng generate component profile
Add the following content to the HomeComponent
template in the src/app/home/home.component.html
file:
<!-- src/app/home/home.component.html -->
<h1>Welcome to Angular + Auth0 + Netlify</h1>
<h3>
The home route is not protected and anyone can visit it. The <code>/profile</code> route is protected, though. Log
in to see it!
</h3>
Next, add the following content to the ProfileComponent
template in the src/app/profile/profile.component.html
file:
<!-- src/app/profile/profile.component.html -->
<h1>Your Auth0 User Profile</h1>
After adding this content to these components, register the routes in the AppRoutingModule
in the src/app/app-routing.module.ts
.
// src/app/app-routing.module.ts
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { ProfileComponent } from './profile/profile.component';
import { HomeComponent } from './home/home.component';
const routes: Routes = [
{
path: '',
component: HomeComponent,
},
{
path: 'profile',
component: ProfileComponent,
},
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}
You now have an application with two components that are used as routes. To make sure everything is working properly, run the following command in your terminal to build and serve the application on port 4200 (the default port for Angular applications):
ng serve --open
The --open
flag will open your browser to http://localhost:4200
once the application is built and the development server is running. You should now be able to route between the home page and the profile page. Later on, you'll lock the profile page down so that it can only be visited after logging in with Auth0.
Secure the Application with Auth0
To secure the application with Auth0, you first need to create an application in your Auth0 dashboard. If you haven't an Auth0 account, you can sign up for a free one. Once you've logged in, click on the "Create Application" button in the upper right-hand corner.
In the modal that pops up, name your application and select the type, in this case, "Single Page Web Applications":
Your application is now created on Auth0. Before going back to work on the application, you need to add a callback URL that the Auth0 application will recognize and use for authenticating your app when running it on localhost. You can add the callback URL to the "Allowed Callback URLs" box on the Auth0 application settings tab. The value you should start within that box is http://localhost:4200/callback
. After deploying to Netlify, you'll add another callback for the deployed application.
The next step will be to add the authentication to your Angular application. Start by adding the Auth0 SDK through npm
with the following command:
npm install @auth0/auth0-spa-js
Once that's installed, generate an Angular service called AuthService
, like this:
ng generate service auth
Open this new AuthService
, located in the src/app/auth.service.ts
file, and add this content to it:
// src/app/auth.service.ts
import { Injectable } from '@angular/core';
import createAuth0Client from '@auth0/auth0-spa-js';
import Auth0Client from '@auth0/auth0-spa-js/dist/typings/Auth0Client';
import { from, of, Observable, BehaviorSubject, combineLatest, throwError } from 'rxjs';
import { tap, catchError, concatMap, shareReplay } from 'rxjs/operators';
import { Router } from '@angular/router';
@Injectable({
providedIn: 'root',
})
export class AuthService {
// Create an observable of Auth0 instance of client
auth0Client$ = (from(
createAuth0Client({
domain: 'YOUR_AUTH0_DOMAIN',
client_id: 'YOUR_AUTH0_APP_CLIENT_ID',
redirect_uri: `${window.location.origin}/callback`,
}),
) as Observable<Auth0Client>).pipe(
shareReplay(1), // Every subscription receives the same shared value
catchError(err => throwError(err)),
);
// Define observables for SDK methods that return promises by default
// For each Auth0 SDK method, first ensure the client instance is ready
// concatMap: Using the client instance, call SDK method; SDK returns a promise
// from: Convert that resulting promise into an observable
isAuthenticated$ = this.auth0Client$.pipe(
concatMap((client: Auth0Client) => from(client.isAuthenticated())),
tap(res => (this.loggedIn = res)),
);
handleRedirectCallback$ = this.auth0Client$.pipe(
concatMap((client: Auth0Client) => from(client.handleRedirectCallback())),
);
// Create subject and public observable of user profile data
private userProfileSubject$ = new BehaviorSubject<any>(null);
userProfile$ = this.userProfileSubject$.asObservable();
// Create a local property for login status
loggedIn: boolean = null;
constructor(private router: Router) { }
// When calling, options can be passed if desired
// https://auth0.github.io/auth0-spa-js/classes/auth0client.html#getuser
getUser$(options?): Observable<any> {
return this.auth0Client$.pipe(
concatMap((client: Auth0Client) => from(client.getUser(options))),
tap(user => this.userProfileSubject$.next(user)),
);
}
localAuthSetup() {
// This should only be called on app initialization
// Set up local authentication streams
const checkAuth$ = this.isAuthenticated$.pipe(
concatMap((loggedIn: boolean) => {
if (loggedIn) {
// If authenticated, get user and set in app
// NOTE: you could pass options here if needed
return this.getUser$();
}
// If not authenticated, return stream that emits 'false'
return of(loggedIn);
}),
);
checkAuth$.subscribe((response: { [key: string]: any } | boolean) => {
// If authenticated, response will be user object
// If not authenticated, response will be 'false'
this.loggedIn = !!response;
});
}
login(redirectPath: string = '/') {
// A desired redirect path can be passed to login method
// (e.g., from a route guard)
// Ensure Auth0 client instance exists
this.auth0Client$.subscribe((client: Auth0Client) => {
// Call method to log in
client.loginWithRedirect({
redirect_uri: `${window.location.origin}/callback`,
appState: { target: redirectPath },
});
});
}
handleAuthCallback() {
// Only the callback component should call this method
// Call when app reloads after user logs in with Auth0
let targetRoute: string; // Path to redirect to after login processsed
const authComplete$ = this.handleRedirectCallback$.pipe(
// Have client, now call method to handle auth callback redirect
tap(cbRes => {
// Get and set target redirect route from callback results
targetRoute = cbRes.appState && cbRes.appState.target ? cbRes.appState.target : '/';
}),
concatMap(() => {
// Redirect callback complete; get user and login status
return combineLatest(this.getUser$(), this.isAuthenticated$);
}),
);
// Subscribe to authentication completion observable
// Response will be an array of user and login status
authComplete$.subscribe(([user, loggedIn]) => {
// Redirect to target route after callback processing
this.router.navigate([targetRoute]);
});
}
logout() {
// Ensure Auth0 client instance exists
this.auth0Client$.subscribe((client: Auth0Client) => {
// Call method to log out
client.logout({
client_id: 'YOUR_AUTH0_APP_CLIENT_ID',
returnTo: `${window.location.origin}`,
});
});
}
}
This service provides all the needed methods for logging in and out, getting the user's profile data, handling authentication callbacks, and managing authentication data in the Angular app on app initialization.
There are two parts of the provided code for the AuthService
that you'll need to change. Those are the client_id
and the domain
. The placeholder text there is YOUR_AUTH0_APP_CLIENT_ID
and YOUR_AUTH0_DOMAIN
. You'll need to replace those placeholder values with the values from the Auth0 application settings tab.
The next step is to modify the src/app/app.component.ts
file to set up authentication when the application is reloaded. This is needed because when a single page application reloads, anything stored in app memory is cleared. Since that's where your authentication information is stored, you would normally lose your authentication data. To get around this, you'll call the localAuthSetup()
function from the AuthService
so that the data can be restored if necessary. Open the src/app/app.component.ts
file and add the following content:
// src/app/app.component.ts
import { Component, OnInit } from '@angular/core';
import { AuthService } from './auth.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent implements OnInit {
constructor(private auth: AuthService) { }
ngOnInit() {
this.auth.localAuthSetup();
}
}
Now, add a navbar component:
ng generate component navbar
Open the src/app/navbar/navbar.component.ts
file and add the following content to it:
// src/app/navbar/navbar.component.ts
import { Component, OnInit } from '@angular/core';
import { AuthService } from '../auth.service';
@Component({
selector: 'app-navbar',
templateUrl: './navbar.component.html',
styleUrls: ['./navbar.component.css'],
})
export class NavbarComponent implements OnInit {
constructor(public auth: AuthService) { }
ngOnInit() { }
}
In the HTML file for the NavbarComponent
, add the following:
<!-- src/app/navbar/navbar.component.ts -->
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" routerLink="/">Auth0 + Netlify</a>
<button
class="navbar-toggler"
type="button"
data-toggle="collapse"
data-target="#navbarNav"
aria-controls="navbarNav"
aria-expanded="false"
aria-label="Toggle navigation"
>
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item active">
<a class="nav-link" routerLink="/">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" routerLink="/profile">Profile</a>
</li>
<li class="nav-item">
<button class="btn btn-primary" (click)="auth.login()" *ngIf="!auth.loggedIn">Log In</button>
</li>
<li class="nav-item">
<button class="btn btn-primary" (click)="auth.logout()" *ngIf="auth.loggedIn">Log Out</button>
</li>
</ul>
</div>
</nav>
Add this NavbarComponent
to the src/app/app.component.html
file above the router-outlet
, removing the ul
that was there before for temporary routing:
<!-- src/app/app.component.html -->
<app-navbar></app-navbar>
<div class="container">
<router-outlet></router-outlet>
</div>
When you log in to an application using Auth0, it's best if there's a route that is used just for the authentication callback. When you go to that route, the authentication response is handled, and the data is stored in the application. Go ahead and create a component that can be used for that purpose:
ng generate component callback
In the src/app/callback/callback.component.ts
file, add the following code:
// src/app/callback/callback.component.ts
import { Component, OnInit } from '@angular/core';
import { AuthService } from '../auth.service';
@Component({
selector: 'app-callback',
templateUrl: './callback.component.html',
styleUrls: ['./callback.component.css'],
})
export class CallbackComponent implements OnInit {
constructor(private auth: AuthService) {}
ngOnInit() {
this.auth.handleAuthCallback();
}
}
The handleAuthCallback()
function call in that CallbackComponent
will make sure that the user is logged in and the data stored. Add this component to your src/app/app-routing.module.ts
file so that the application knows about it:
// src/app/app-routing.module.ts
const routes: Routes = [
...,
{ path: 'callback', component: CallbackComponent }
];
Next, update the profile component template to output the logged-in user's profile information. Replace the contents of the src/app/profile/profile.component.ts
file with the following:
// src/app/profile/profile.component.ts
import { Component, OnInit } from '@angular/core';
import { AuthService } from '../auth.service';
@Component({
selector: 'app-profile',
templateUrl: './profile.component.html',
styleUrls: ['./profile.component.css'],
})
export class ProfileComponent implements OnInit {
constructor(public auth: AuthService) {}
ngOnInit() {}
}
Next, add the following to the src/app/profile/profile.component.html
file, leaving the h1
tag in the template:
<!-- src/app/profile/profile.component.html -->
<pre *ngIf="auth.userProfile$ | async as profile">
<code>{{ profile | json }}</code>
</pre>
At this point, you have the authentication all set up, with a profile route, being able to log in and out, and having your authentication status restored when the app loads. But the only problem is that you can go to the /profile
route before you're logged in. You don't want this to happen, so next, you're going to add a route guard to prevent that. Create the route guard using the Angular CLI:
ng generate guard auth
You should see a prompt asking you which interfaces you would like to implement. Select CanActivate
, and then continue. After the guard is created, open the src/app/auth.guard.ts
file and replace its contents with the following:
// src/app/auth.guard.ts
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree, CanActivate } from '@angular/router';
import { Observable } from 'rxjs';
import { AuthService } from './auth.service';
import { tap } from 'rxjs/operators';
@Injectable({
providedIn: 'root',
})
export class AuthGuard implements CanActivate {
constructor(private auth: AuthService) {}
canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot,
): Observable<boolean> | Promise<boolean | UrlTree> | boolean {
return this.auth.isAuthenticated$.pipe(
tap(loggedIn => {
if (!loggedIn) {
this.auth.login(state.url);
}
}),
);
}
}
The last step is to add the guard to the /profile
route. Open the src/app/app-routing.module.ts
file and change the /profile
route definition to the following:
// src/app/app-routing.module.ts
const routes: Routes = [
...,
{
path: 'profile',
component: ProfileComponent,
canActivate: [AuthGuard]
},
...
]
This adds the AuthGuard
to that route, meaning that if the AuthGuard
service returns true when called, the user will be able to continue on to the route. If it returns false, they will be redirected to log in.
You should now be able to serve your application and try it out. If you run into any problems, go back through the steps from above, or check out the full quick start on Auth0's site.
You now have an Angular app to a point where it can be pushed to GitHub. Add and commit all your files. You'll push the project to GitHub in a minute.
git add .
git commit -m "My Angular + Auth0 + Netlify project"
Netlify Redirect Configuration
Before you push your code to the GitHub repository, you will need to configure Netlify redirects to work with your application. In single-page applications like the ones you can build with Angular, routing is handled by the application itself, not by redirecting to new pages on the actual server. So the application can route around until you refresh the app. If you refresh the app on any page besides an empty route, Netlify will not be able to find the route. You can fix this by telling Netlify where to find each route in your application. In essence, every route will be sent back to the Angular app by telling Netlify that all routes really just need to be sent to the index.html
file of the app.
All Netlify will expect for this to work is a _redirects
file in the root of the deployed app. The contents of that file should be:
# src/_redirects
/* /index.html 200
Again, this just says to Netlify that every route should just return the src/index.html
file. That way, Angular can handle the routing.
After adding the file, you need to make sure it's included in the build. The easiest way to do that is to edit the angular.json
file. If you add the path to this file (src/_redirects
) to the assets array by the favicon.ico
, it will be included in the build of the application.
{
...
"projects": {
"my-ng-auth0-netlify": {
...
"architect": {
"build": {
...
"options": {
...
"assets": ["src/favicon.ico", "src/_redirects", "src/assets"],
...
},
...
}
},
...
}
}
...
}
Create the GitHub Repository
Now it's time to go to GitHub and create a new repository.
Run the following commands back in your terminal, which will push your Angular project to the newly created GitHub repository:
git remote add origin git@github.com:pjlamb12/my-ng-auth0-netlify-project.git
git push -u origin master
Don't forget to change the
git@github.com:pjlamb12/my-ng-auth0-netlify-project.git
to the URL given to you by GitHub
Your project is now in a GitHub repository!
Configure Netlify to Build and Deploy your App
The next step is to setup Netlify to build and deploy your application. You'll need to connect your new GitHub repository to Netlify and choose the build options. After doing that, Netlify will build and deploy your application each time updates are pushed to the master branch. Go to app.netlify.com and get started.
If you haven't signed up for Netlify yet, go through the steps to create an account. It's free, and you can sign up easily with your GitHub account.
"Netlify is able to build and deploy your application each time updates are pushed to the master branch."
Tweet This
After you log in, click on the "New site from Git" button on the page. You should see this screen after clicking on that button:
Choose a git provider for continuous deployment, which will be GitHub if you've been following along. After selecting GitHub, you may be required to install the Netlify application for GitHub and authorize it on your account. Once you've done this, you can search for the repository where your project is located (my-ng-auth0-netlify
).
Netlify lets you select the team (if you're part of more than one) and which branch should be used for the builds. For the build command, enter ng build --prod
. If you have a different configuration you're using besides production, you can alter that build command by using the --configuration
flag. Before clicking the deploy site button, make sure to enter the location of the publish directory. The default location for the publish directory will be in the dist/project-name
folder. In your case, that's dist/my-ng-auth0-netlify
. Enter that in the field, and then click on the deploy site button.
After filling out that form and clicking "Deploy site", Netlify will take you to the project overview page. You should see a message at the top of the page saying that the application is deploying. You can also click on the "Deploys" menu item and select the most recent to watch the build progress.
On the overview page, there's also an automatically generated site ID. That site ID will be part of how you will be able to access the site. The URL will be generated-site-id.netlify.com. In the image above, you can see that the automatically generated site ID was "sad-fermi-0e08cb". You can change that site name on the settings page, in the general information section. The only requirement is that the name is unique. Changing the name will help you to be able to remember where to see your site. You can also go to the "Domain Management" section of the site settings and configure a custom domain name for the site.
One other thing that you'll need to do once you've finalized your site name (whether a custom domain, changing the automatically generated site ID or using the automatically generated site ID) is add the URL as the callback URL in the Auth0 settings for your application. You will not be able to log in without doing this. You'll add this to the same location you added http://localhost:4200
as a callback. That's on the Auth0 app settings page.
"Angular is great to build Web applications. Netlify is great to automate their deployment."
Tweet This
Once your application has finished building and is deployed, visit the site and make sure that you can log in using Auth0 and that you can route between the home page and the profile page. It should work just like the site did on your local machine. You should be able to start on the home page and click back and forth between the home link and the profile link. Pretty great, huh?
Conclusion
Netlify is an inexpensive, easy way to deploy Angular applications, and with a service like Auth0, you can limit access to the application as well. With tools like Netlify's Functions, you could build a full-fledged app without needing to manage servers or complex build processes. This is a perfect option for companies that are just starting and don't have the capacity to manage servers. You can find a repository with all the necessary, working code for this demo here. This application is deployed on Netlify and available at pjlamb12.ng-auth0.netlify.com.
About Auth0
Auth0 by Okta takes a modern approach to customer identity and enables organizations to provide secure access to any application, for any user. Auth0 is a highly customizable platform that is as simple as development teams want, and as flexible as they need. Safeguarding billions of login transactions each month, Auth0 delivers convenience, privacy, and security so customers can focus on innovation. For more information, visit https://auth0.com.