Our website is made possible by displaying online advertisements to our visitors. Please consider supporting us by disabling your ad blocker.

Authenticate With JWT In A NativeScript Angular Mobile Application

TwitterFacebookRedditLinkedInHacker News

Any mobile application that accesses remote data will need authentication at some point in time. There are many different authentication strategies out there, one of which is with Json Web Tokens (JWT) that we explored in one of my previous Node.js articles. With JWT, users can authenticate via username and password, receive a signed token back, and use that token for any future API request rather than continuing to distribute the username and password.

In this tutorial we’re going to explore how to build an Android and iOS mobile application using NativeScript and Angular that authenticates with an API and then uses a Json Web Token for future requests to that same API.

While the concepts in this tutorial can be applied towards any of your own projects, this particular tutorial is going to be based off the JWT in Node.js tutorial that I had previously written. In that tutorial we had created an API with no client front-end. As you can guess, the NativeScript application will be our front-end.

NativeScript Angular JWT Example

In the above animated image we have two screens, one with a form and one that displays data from a protected API endpoint. The responses displayed in the Toast notification come from the API and the data displayed on the second screen is a response from the API. What you don’t see is that after signing in on the first page you receive a Json Web Token and on the second page the token is used to obtain the message.

So let’s work towards a working example in NativeScript!

The Requirements

There are a few requirements that must be met before starting on this project. While there is some flexibility, it is best to try to match them as much as possible.

  • NativeScript 2.4+
  • JWT Capable Backend Application

By now you should already have the NativeScript CLI installed. This was obtained through the Node Package Manager (NPM) which is a part of Node.js. For simplicity, it might make sense to use the Node.js Json Web Token tutorial I wrote as your backend, rather than exploring on your own. However, if you’re up for the challenge, the same rules apply if you use something else.

Creating a New NativeScript Project for JWT Authentication

To keep things simple and easy to understand, we’re going to start with a fresh NativeScript project that uses Angular. This project will use various plugins and build platforms.

With the NativeScript CLI, execute the following commands:

tns create TokenProject --ng
cd TokenProject
tns platform add ios
tns platform add android

The --ng tag indicates that we are creating an Angular with TypeScript project. I’ve added two build platforms, but it is important to note that if you’re not using a Mac with Xcode installed, you cannot add the iOS build platform.

To give this application a little flair, we’re going to be using a plugin for Toast notifications. These are the notifications that appear at the bottom of the screen for a short period of time and disappear.

From the Command Prompt or Terminal, execute the following command:

tns plugin add nativescript-toast

Toast notifications are very useful for any application because they are non-blocking on the UI. More information on them can be read about in a previous article that I wrote.

Defining an iOS App Transport Security (ATS) Policy

If you’re using iOS like I am, you’ll need to do a further configuration, otherwise you won’t be able to use an API that isn’t using HTTPS. As of iOS 9.0, Apple introduced what is known as App Transport Security (ATS) and it is part of an effort to protect users from unsafe remote services.

Open the project’s app/App_Resources/iOS/Info.plist file and include the following XML markup:

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true />
</dict>

In a production application you won’t want the above lines in particular because the above lines allow HTTP connections to any remote service. In a production application you’ll want to define a list of allowed applications.

Android applications do not suffer from this so if you’re not using iOS then you don’t have to worry about ATS. For more information on ATS, check out a previous article I wrote about on the subject.

Bootstrapping the Application in Preparation for Development

Now let’s worry about creating all of our application files and linking them together. This is going to be a two page mobile application, so let’s create each of the two pages.

From the command line, execute the following commands:

mkdir -p app/components/login
mkdir -p app/components/authenticated
touch app/components/login/login.ts
touch app/components/login/login.html
touch app/components/authenticated/authenticated.ts
touch app/components/authenticated/authenticated.html

If you’re not using a system that supports the mkdir or touch commands, don’t worry. Just create all of the above files and directories manually.

Now let’s take our business into the project’s app/app.module.ts file where we can link all these pages together with application routing.

Open the project’s app/app.module.ts file and include the following TypeScript code. Don’t worry, we’ll break it down after.

import { NgModule, NO_ERRORS_SCHEMA } from "@angular/core";
import { NativeScriptModule } from "nativescript-angular/nativescript.module";
import { NativeScriptHttpModule } from "nativescript-angular/http";
import { NativeScriptRouterModule } from "nativescript-angular/router";
import { NativeScriptFormsModule } from "nativescript-angular/forms";

import { AppComponent } from "./app.component";
import { LoginComponent } from "./components/login/login";
import { AuthenticatedComponent } from "./components/authenticated/authenticated";

let routes = [
    { path: "", component: LoginComponent },
    { path: "authenticated", component: AuthenticatedComponent }
];

@NgModule({
    declarations: [
        AppComponent,
        LoginComponent,
        AuthenticatedComponent
    ],
    bootstrap: [AppComponent],
    imports: [
        NativeScriptModule,
        NativeScriptFormsModule,
        NativeScriptHttpModule,
        NativeScriptRouterModule,
        NativeScriptRouterModule.forRoot(routes)
    ],
    schemas: [NO_ERRORS_SCHEMA]
})
export class AppModule { }

There are a few modules that need to be imported for each of the things we wish to accomplish:

import { NativeScriptHttpModule } from "nativescript-angular/http";
import { NativeScriptRouterModule } from "nativescript-angular/router";
import { NativeScriptFormsModule } from "nativescript-angular/forms";

The NativeScriptHttpModule will allow us to make HTTP requests against a remote web service, in this case our JWT service. The NativeScriptRouterModule will allow us to navigate between pages of the application and the NativeScriptFormsModule allows us to do data binding between our HTML UI inputs and the TypeScript backend logic.

import { LoginComponent } from "./components/login/login";
import { AuthenticatedComponent } from "./components/authenticated/authenticated";

let routes = [
    { path: "", component: LoginComponent },
    { path: "authenticated", component: AuthenticatedComponent }
];

Although we haven’t created the LoginComponent and AuthenticatedComponent classes yet, we plan to import them and assign them to a route. The default route will have an empty path. This means that particular component will display on the screen first.

Inside the @NgModule block we can add our components to the declarations array and import each of our modules.

We’re not done yet though. While we have the routes to our pages defined, there is no passthrough in the actual application. Open the project’s app/app.component.html file and include the following HTML markup:

<page-router-outlet></page-router-outlet>

At this point in time the core application functionality is ready for development. All of the boilerplate and bootstrapping logic has been completed.

Developing the Page for Authentication

The first thing we want to do is create our page for authenticating the user against the remote web service. Open the project’s app/components/login/login.ts file and include the following TypeScript code:

import { Component } from "@angular/core";
import { Http, Headers, RequestOptions } from "@angular/http";
import { Router } from "@angular/router";
import * as Toast from "nativescript-toast";
import "rxjs/Rx";

@Component({
    selector: "login",
    templateUrl: "./components/login/login.html",
})
export class LoginComponent {

    public constructor(private http: Http, private router: Router) { }

    public login(username: string, password: string) {
        let headers = new Headers({ "Content-Type": "application/json" });
        let options = new RequestOptions({ headers: headers });
        this.http.post("http://localhost:3000/authenticate", JSON.stringify({ username: username, password: password }), options)
            .map(result => result.json())
            .subscribe(result => {
                this.router.navigate(["authenticated"], { queryParams: { jwt: result.token } });
            }, error => {
                Toast.makeText(error.json().message).show();
            });
    }

}

In the above code we import several Angular components as well as RxJS and the Toast plugin that we downloaded. In the constructor method of the LoginComponent class we inject each of the Angular services so they can be used throughout the page.

This brings us to our core method, the login method:

public login(username: string, password: string) {
    let headers = new Headers({ "Content-Type": "application/json" });
    let options = new RequestOptions({ headers: headers });
    this.http.post("http://localhost:3000/authenticate", JSON.stringify({ username: username, password: password }), options)
        .map(result => result.json())
        .subscribe(result => {
            this.router.navigate(["authenticated"], { queryParams: { jwt: result.token } });
        }, error => {
            Toast.makeText(error.json().message).show();
        });
}

Because we’re doing a POST request, we need to define the content body being sent. If we don’t, the receiving server will think it is text data, but in reality we want it to be JSON data. With the request we are passing the header information and a serialized JSON object containing the username and password of the user.

The request uses RxJS so we can work with the returned observable. First we transform the response into a JSON object, and subscribe to it. In the event there is an error, we’ll show it in a Toast notification, otherwise we’ll pass the JWT token as a query parameter to the next page.

So what does the UI look like for this particular form?

Open the project’s app/components/login/login.html file and include the following HTML markup:

<ActionBar title="{N} JWT Example"></ActionBar>
<StackLayout verticalAlignment="center">
    <Label text="Sign into the application" class="h2 text-center"></Label>
    <StackLayout class="form">
        <StackLayout class="input-field">
            <Label class="label" text="Username:"></Label>
            <TextField #username class="input input-border" autocorrect="false" autocapitalizationType="none"></TextField>
        </StackLayout>
        <StackLayout class="input-field">
            <Label class="label" text="Password:"></Label>
            <TextField #password class="input input-border" secure="true" autocorrect="false" autocapitalizationType="none"></TextField>
        </StackLayout>
        <StackLayout class="input-field">
            <Button text="Login" (tap)="login(username.text, password.text)" class="btn btn-primary btn-active w-full"></Button>
        </StackLayout>
    </StackLayout>
</StackLayout>

You’ll notice that we have an action bar and a core layout that is centered vertically. All of the CSS class names are coming from the NativeScript Theme package that is bundled with every application.

However, what is important here is the <TextField> and <Button> Angular Template tags. For form input we are using variables and passing them into the tap event of the page button.

Developing the Page for Working with Authenticated Data

Now that we have the Json Web Token from the authentication process, we can take that information to query for data on the second page. The token will be used to get further API data. Without it the API requests will fail.

Open the project’s app/components/authenticated/authenticated.ts file and include the following TypeScript code:

import { Component, OnInit } from "@angular/core";
import { Http, Headers, RequestOptions } from "@angular/http";
import { ActivatedRoute } from "@angular/router";
import "rxjs/Rx";

@Component({
    selector: "authenticated",
    templateUrl: "./components/authenticated/authenticated.html",
})
export class AuthenticatedComponent implements OnInit {

    public content: string;

    public constructor(private http: Http, private route: ActivatedRoute) {
        this.content = "";
    }

    public ngOnInit() {
        this.route.queryParams.subscribe(params => {
            let headers = new Headers({ "Authorization": "Basic " + params["jwt"] });
            let options = new RequestOptions({ headers: headers });
            this.http.get("http://localhost:3000/protected", options)
                .map(result => result.json())
                .subscribe(result => {
                    this.content = result.message;
                });
        });
    }

}

Looks familiar doesn’t it? Well, not exactly.

In the AuthenticatedComponent class we have a public variable that will be bound to the UI of the application. This variable is initialized in the constructor method as well as certain service injections.

Because we want the data to appear at load, we use the ngOnInit method rather than trying to load it inappropriately in the constructor method. In the ngOnInit method we construct an authentication header using the passed JWT token, at which point we make the request. The response of the request will be rendered to the screen.

Open the project’s app/components/authenticated/authenticated.html file and include the following HTML markup:

<ActionBar title="{N} Member Content">
    <NavigationButton text="Back"></NavigationButton>
</ActionBar>
<StackLayout>
    <Label text="{{ content }}" class="text-center"></Label>
</StackLayout>

The above HTML is basic, but all we really wanted to do was display the public variable from the TypeScript file. This variable contains whatever the protected API endpoint provided us with.

Seeing the Project in Action

So let’s give this project a run. From the NativeScript CLI, execute the following command to build and run the application on an emulator:

tns emulate android

If you were using an iOS emulator, you could switch out the platform of the above command.

Not interested in running through the entire tutorial? You can download the full source code here. After downloading the source code and extracting it, execute the following:

tns install

The above command will install all the project dependencies, at which point, the project can be built and run.

Conclusion

You just saw how to create a NativeScript application with Angular that communicates with a Json Web Token (JWT) protected API. By obtaining a valid JWT with our credentials we could then use the token to authenticate on further endpoints. This particular tutorial used a Node.js JWT example that I write previously, but it could be expanded to whatever you’d like to use.

If you’d like to give this application a spin, the full project source code can be obtained here. The Node.js source code can be obtained from that particular example as well.

Nic Raboy

Nic Raboy

Nic Raboy is an advocate of modern web and mobile development technologies. He has experience in C#, JavaScript, Golang and a variety of frameworks such as Angular, NativeScript, and Unity. Nic writes about his development experiences related to making web and mobile development easier to understand.