Then generate a new Angular 4 project boilerplate:
1
$ ng new angular4-auth
Fire up the app after the dependencies install:
12
$ cd angular4-auth
$ ng serve
It will probably take a minute or two to compile and build your application. Once done, navigate to http://localhost:4200 to ensure the app is up and running.
Open up the project in your favorite code editor, and then glance over the code:
In short, the client-side code lives in the “src” folder and the Angular app itself can be found in the “app” folder.
Take note of the AppModule within app.module.ts. This is used to bootstrap the Angular app. The @NgModule decorator takes metadata that lets Angular know how to run the app. Everything that we create in this tutorial will be added to this object.
Make sure you have a decent grasp of the app structure before moving on.
NOTE: Just getting started with Angular 4? Review the Angular Style Guide, since the app generated from the CLI follows the recommended structure from that guide, as well as the Angular4Crud Tutorial.
Did you notice that the CLI initialized a new Git repo? This part is optional, but it’s a good idea to create a new Github repository and update the remote:
First, use the CLI to generate a new Login component:
1
$ ng generate component components/login
This set up the component files and folders and even wired it up to app.module.ts. Next, let’s change the login.component.ts file to the following:
1234567891011
import{Component}from'@angular/core';@Component({selector:'login',templateUrl:'./login.component.html',styleUrls:['./login.component.css']})exportclassLoginComponent{test:string='just a test';}
If you haven’t used TypeScript before, then this code probably looks pretty foreign to you. TypeScript is a statically-typed superset of JavaScript that compiles to vanilla JavaScript, and it is the de facto programming language for building Angular 4 apps.
In Angular 4, we define a component by wrapping a config object with an @Component decorator. We can share code between packages by importing the classes we need; and, in this case, we import Component from the @angular/core package. The LoginComponent class is the component’s controller, and we use the export operator to make it available for other classes to import.
Add the following HTML to the login.component.html:
123
<h1>Login</h1><p>{{test}}</p>
Next, configure the routes, via the RouterModule in the app.module.ts file:
Finish enabling routing by replacing all HTML in the app.component.html file with the <router-outlet> tag:
1
<router-outlet></router-outlet>
Run ng serve in your terminal, if you haven’t already, and then navigate to http://localhost:4200/login. If all went well you should see the just a test text.
Bootstrap Style
To quickly add some style, update the index.html, adding in Bootstrap and wrapping the <app-root></app-root> in a container:
Remember how providers worked in Angular 1? They were global objects that stored a single state. When the data in a provider changed, any object that had injected that provider would receive the updates. In Angular 4, providers retain their special behavior and they are defined with the @Injectable decorator.
Sanity Check
Before adding anything significant to AuthService, let’s make sure the service itself is wired up correctly. To do that, within login.component.ts inject the service and call the test() method:
12345678910111213141516
import{Component,OnInit}from'@angular/core';import{AuthService}from'../../services/auth.service';@Component({selector:'login',templateUrl:'./login.component.html',styleUrls:['./login.component.css']})exportclassLoginComponentimplementsOnInit{test:string='just a test';constructor(privateauth:AuthService){}ngOnInit():void{console.log(this.auth.test());}}
We’ve introduced some new concepts and keywords. The constructor() function is a special method that we use to set up a new instance of a class. The constructor() is where we pass any parameters that the class requires, including any providers (i.e., AuthService) that we want to inject. In TypeScript, we can hide variables from the outside world with the private keyword. Passing a private variable in the constructor is a shortcut to defining it within the class and then assigning the argument’s value to it. Notice how the auth variable is accessible to the this object after it is passed into the constructor.
We implement the OnInit interface to ensure that we explicitly define a ngOnInit() function. Implementing OnInit ensures that our component will be called after the first change detection check. This function is called once when the component first initializes, making it the ideal place to configure data that relies on other Angular classes.
Unlike components, which are automatically added, services have to be manually imported and configured on the @NgModule. So, to get it working, you’ll also have to import the AuthService in app.module.ts and add it to the providers:
Here, we are using the Http service to send an AJAX request to the /user/login endpoint. This returns a promise object.
NOTE: Make sure to remove console.log(this.auth.test()); from the LoginComponent component.
User Registration
Let’s go ahead and add the ability to register a user as well, which is similar to logging a user in. Update src/app/services/auth.service.ts, taking note of the register method:
Follow the directions in the README to set up the project, making sure the tests pass before moving on. Once done, run the server with python manage.py runserver, which will listen on port 5000.
Sanity Check
To test, update LoginComponent to use the login and register methods from the service:
import{Component,OnInit}from'@angular/core';import{AuthService}from'../../services/auth.service';@Component({selector:'login',templateUrl:'./login.component.html',styleUrls:['./login.component.css']})exportclassLoginComponentimplementsOnInit{test:string='just a test';constructor(privateauth:AuthService){}ngOnInit():void{letsampleUser:any={email:'michael@realpython.com'asstring,password:'michael'asstring};this.auth.register(sampleUser).then((user)=>{console.log(user.json());}).catch((err)=>{console.log(err);});this.auth.login(sampleUser).then((user)=>{console.log(user.json());}).catch((err)=>{console.log(err);});}}
Refresh http://localhost:4200/login in the browser and you should see a success in the JavaScript console, after the user is logged in, with the token:
Take note of the form. We used the [(ngModel)] directive on each of the form inputs to capture those values in the controller. Also, when the form is submitted, the ngSubmit directive handles the event by firing the onLogin() method.
Now, let’s update the component code, adding in onLogin():
If you have the Angular web server running, you should see the error Cannot find module '../../models/user' in the browser. Before our code will work, we need to create a User model.
Our User model has two properties, email and password. The ? character is a special operator that indicates that initializing User with explicit email and password values is optional. This is equivalent to the following class in Python:
Next, let’s add the token to Local Storage for persistence by replacing the console.log(user.json()); with localStorage.setItem('token', user.data.token); in src/app/components/login/login.component.ts:
As long as that token is present, the user can be considered logged in. And, when a user needs to make an AJAX request, that token can be used.
NOTE: Besides the token, you could also add the user id and email to Local Storage. You would just need to update the server-side to send back that info when a user logs in.
Test this out. Ensure that the token is present in Local Storage after you log in.
User Status
To test out login persistence, we can add a new view that verifies that the user is logged in and that the token is valid.
Take note of Authorization: 'Bearer ' + token. This is called a Bearer schema, which is sent along with the request. On the server, we are simply checking for the Authorization header, and then whether the token is valid. Can you find this code on the server-side?
Then, generate a new Status component:
1
$ ng generate component components/status
Create the HTML template, src/app/components/status/status.component.html:
Right now, all routes are open; so, regardless of whether a user is logged in or not, they they can access each route. Certain routes should be restricted if a user is not logged in, while other routes should be restricted if a user is logged in:
/ – no restrictions
/login – restricted when logged in
/register – restricted when logged in
/status – restricted when not logged in
To achieve this, add either EnsureAuthenticated or LoginRedirect to each route, depending on whether you want to guide the user to the status view or the login view.
Start by creating two new services:
12
$ ng generate service services/ensure-authenticated
$ ng generate service services/login-redirect
Replace the code in the ensure-authenticated.service.ts file as follows:
Note how we are adding our services to a new route property, canActivate. The routing system uses the services in the canActivate array to determine whether to display the requested URL path. If the route has LoginRedirect and the user is already logged in, then they will be redirected to the status view. Including the EnsureAuthenticated service redirects the user to the login view if they attempt to access a URL that requires authentication.
Test one last time.
What’s Next?
In this tutorial, we went through the process of adding authentication to an Angular 4 + Flask app using JSON Web Tokens.
What’s next?
Try switching out the Flask back-end for a different web framework, like Django or Bottle, using the following endpoints:
/auth/register
/auth/login
/auth/logout
/auth/user
Add questions and/or comments below. Grab the final code from the angular4-auth repo.