An example of the Ionic Auth service with Ionic 2

An example of the Ionic Auth service with Ionic 2

This post is more than 2 years old.

Note that I am writing this when Ionic 2 was in RC2 status. I expect that things may change a bit between now and the final release, but probably not too much.

Earlier this year I wrote a blog post demonstrating the newly released authentication system for Ionic applications: "Testing the New Ionic User Service". I thought it might be fun to work up a new example using Ionic 2. While this example doesn't do anything the docs don't, it does put them all together in one "complete" application. I thought it might be useful to see the various APIs put together into a demo that shows:

  • Checking if the user is logged in on start up
  • Moving the user to a login page if not...
  • And moving them to a home page if so
  • Support logout and send the user back to the login page

The full source code for what I'm about to demonstrate may be found here: https://github.com/cfjedimaster/Cordova-Examples/tree/master/ionicauth2

I began by creating a new Ionic 2 application using the "blank" template. This gave me an app with one page, home. My first task was to add logic to check for existing login and route the user appropriately. I did this within app.component.ts:


import { Component } from '@angular/core';
import { Platform } from 'ionic-angular';
import { StatusBar, Splashscreen } from 'ionic-native';

import { HomePage } from '../pages/home/home';
import { LoginPage } from '../pages/login/login';
import { Auth } from '@ionic/cloud-angular';

@Component({
  template: `<ion-nav [root]="rootPage"></ion-nav>`
})
export class MyApp {
  rootPage;

  constructor(platform: Platform, public auth:Auth) {
    platform.ready().then(() => {
      // Okay, so the platform is ready and our plugins are available.
      // Here you can do any higher level native things you might need.
      StatusBar.styleDefault();
      Splashscreen.hide();

      if(this.auth.isAuthenticated()) {
        this.rootPage = HomePage;
      } else {
        this.rootPage = LoginPage;
      }

    });
  }
}

I don't know if I need that rootPage declaration there. I kind of think I don't, but I've left it alone for now. The real important bit is this.auth.isAuthenticated(). The Auth system automatically caches your login for you (and that's something you can disable) so you don't have to worry about adding your own storage for logins.

In case you're curious about that - the Auth system uses LocalStorage when testing in the browser:

Screenshot removed

You'll notice the password is in plain text and they are also storing a third value as well. Outside of the other one, I feel like it's a mistake to use email and password as those are values I could see using myself. (I filed an [issue](https://github.com/driftyco/ionic-cloud-angular/issues/30)).

Sorry folks! I was totally wrong about this. I had written my own code to store auth info in LocalStorage but removed that code when I saw that Ionic was handling for me. However, I forgot that fact when I used devtools to look at my localstorage. Ionic does store a JWT token - but definitely not the password!

For my login, I decided to use a simple UI that defaults to login:

Login

Clicking register simply hides the initial form and shows the (slightly larger) registration form:

Register

I'm not entirely sold on that UX, but it gets the job done. Let's take a look at the HTML behind this:


<!--
  Generated template for the Login page.

  See http://ionicframework.com/docs/v2/components/#navigation for more info on
  Ionic pages and navigation.
-->
<ion-header>

  <ion-navbar>
    <ion-title>Login/Register</ion-title>
  </ion-navbar>

</ion-header>


<ion-content padding>

  <div *ngIf="showLogin">
    <ion-item>
      <ion-input type="email" placeholder="Email" [(ngModel)]="email"></ion-input>
    </ion-item>

    <ion-item>
      <ion-input type="password" placeholder="Password" [(ngModel)]="password"></ion-input>
    </ion-item>
  </div>

  <div *ngIf="!showLogin">

    <ion-item>
      <ion-input type="text" placeholder="Name" [(ngModel)]="name"></ion-input>
    </ion-item>

    <ion-item>
      <ion-input type="email" placeholder="Email" [(ngModel)]="email"></ion-input>
    </ion-item>

    <ion-item>
      <ion-input type="password" placeholder="Password" [(ngModel)]="password"></ion-input>
    </ion-item>
  </div>

  <button ion-button color="primary" full (click)="doLogin()">Login</button>
  <button ion-button color="primary" full (click)="doRegister()">Register</button>

</ion-content>

Nothing crazy here except the 2 divs with ngIf controlling their visibility. The real fun is the code behind this:


import { Component } from '@angular/core';
import { NavController, AlertController, LoadingController } from 'ionic-angular';
import { Auth, User, UserDetails, IDetailedError } from '@ionic/cloud-angular';
import { HomePage } from '../home/home';

@Component({
  selector: 'page-login',
  templateUrl: 'login.html'
})
export class LoginPage {

  showLogin:boolean = true;
  email:string = '';
  password:string = '';
  name:string = '';

  constructor(public navCtrl: NavController, public auth:Auth, public user: User, public alertCtrl: AlertController, public loadingCtrl:LoadingController) {}

  ionViewDidLoad() {
    console.log('Hello LoginPage Page');
  }

  /*
  for both of these, if the right form is showing, process the form,
  otherwise show it
  */
  doLogin() {
    if(this.showLogin) {
      console.log('process login');

      if(this.email === '' || this.password === '') {
        let alert = this.alertCtrl.create({
          title:'Register Error', 
          subTitle:'All fields are rquired',
          buttons:['OK']
        });
        alert.present();
        return;
      }     

      let loader = this.loadingCtrl.create({
        content: "Logging in..."
      });
      loader.present();
      
      this.auth.login('basic', {'email':this.email, 'password':this.password}).then(() => {
        console.log('ok i guess?');
        loader.dismissAll();
        this.navCtrl.setRoot(HomePage);        
      }, (err) => {
        loader.dismissAll();
        console.log(err.message);

        let errors = '';
        if(err.message === 'UNPROCESSABLE ENTITY') errors += 'Email isn\'t valid.<br/>';
        if(err.message === 'UNAUTHORIZED') errors += 'Password is required.<br/>';

        let alert = this.alertCtrl.create({
          title:'Login Error', 
          subTitle:errors,
          buttons:['OK']
        });
        alert.present();
      });
    } else {
      this.showLogin = true;
    }
  }

  doRegister() {
    if(!this.showLogin) {
      console.log('process register');

      /*
      do our own initial validation
      */
      if(this.name === '' || this.email === '' || this.password === '') {
        let alert = this.alertCtrl.create({
          title:'Register Error', 
          subTitle:'All fields are rquired',
          buttons:['OK']
        });
        alert.present();
        return;
      }

      let details: UserDetails = {'email':this.email, 'password':this.password, 'name':this.name};
      console.log(details);
      
      let loader = this.loadingCtrl.create({
        content: "Registering your account..."
      });
      loader.present();

      this.auth.signup(details).then(() => {
        console.log('ok signup');
        this.auth.login('basic', {'email':details.email, 'password':details.password}).then(() => {
          loader.dismissAll();
          this.navCtrl.setRoot(HomePage);
        });

      }, (err:IDetailedError<string[]>) => {
        loader.dismissAll();
        let errors = '';
        for(let e of err.details) {
          console.log(e);
          if(e === 'required_email') errors += 'Email is required.<br/>';
          if(e === 'required_password') errors += 'Password is required.<br/>';
          if(e === 'conflict_email') errors += 'A user with this email already exists.<br/>';
          //don't need to worry about conflict_username
          if(e === 'invalid_email') errors += 'Your email address isn\'t valid.';
        }
        let alert = this.alertCtrl.create({
          title:'Register Error', 
          subTitle:errors,
          buttons:['OK']
        });
        alert.present();
      });
     
    } else {
      this.showLogin = false;
    }
  }

}

Let's begin with doRegister. This handles either showing the register form (if hidden) or performing registration. For registration, I do a quick sanity check on the form fields (I know Angular 2 has a fancier way of working with forms, but I don't really know it yet) and if all fields have something in them, I then create a new instance of UserDetails and pass it to the signup method of auth. If it succeeds, awesome. I then log the user in and then send them to the home page.

If it fails, I look at the nice errors and create English versions of them to present to the user. Notice too I'm using the Ionic loading and alert controls as well. Also make note of this.navCtrl.setRoot. I use that instead of push because I do not want to provide a link back to the login page. Basically I'm saying, "This is your new home. Love it. Make peace with it. Share a good beer..."

Now take a look at login. For the most part it follows the same idea - check the form, login, then handle the result. But oddly, errors with login throw exceptions and not "pretty" errors with simple codes like registration. I'm sure there is a good reason for that, but I wish the docs had made it more clear. I literally had copied the error handling code from doRegister with the expectation that only the possible results would change and that wasn't so. For example, a bad email address throws UNPROCESSABLE ENTITY, which, ok, I guess means something, but it isn't terribly obvious. A failed login just returns UNAUTHORIZED, which is a bit more sensible perhaps.

The final part of this demo is the home page. All I do is print out the user's name and provide a logout button.

Home

Here's the HTML:


<ion-header>
  <ion-navbar>
    <ion-title>
      Home
    </ion-title>
    <ion-buttons end>
      <button ion-button icon-only (click)="logout()">
        <ion-icon name="exit"></ion-icon>
      </button>
    </ion-buttons>
  </ion-navbar>
</ion-header>

<ion-content padding>
    <h2>Welcome back, {{ user.details.name }}</h2>
</ion-content>

Nothing too special there, although I should point out that the name value of a user is optional. And here is the code behind the view:


import { Component } from '@angular/core';

import { NavController } from 'ionic-angular';
import { Auth, User } from '@ionic/cloud-angular';
import { LoginPage } from '../login/login';

@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {

  constructor(public navCtrl: NavController, public user:User, public auth:Auth) {
    console.log(user);
    
  }

  logout() {
    this.auth.logout();
    this.navCtrl.setRoot(LoginPage);
  }

}

Literally the only thing interesting here is logout and frankly, that's not terribly interesting either.

But all in all - I now have a complete Auth demo that actually handles the views and such. I built this just so I could write another blog post but that will be for next week. As always, I hope this helps. I've been kind of avoiding blogging much on Ionic 2 with it changing so much over the year, but with today's release of RC2, it's time to get back on the Ionic Love Train again.

"Cat Train"

What I imagine the "Ionic Love Train" would look like...

Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Archived Comments

Comment 1 by Bob Clingan posted on 11/4/2016 at 11:21 PM

Any advice on installing ionic? I've spent a few hours installing and reinstalling node, npm, and XCode just trying to the the iOS simulator working...

Comment 2 (In reply to #1) by Gabi Medina posted on 11/5/2016 at 12:47 PM

Are you having EACCES errors AKA permission errors? I was having the same yesterday and managed to fix them all by setting the permissions to the parent folder of every file that threw that error:

sudo chown -R $USER:$GROUP /path-to-directory

Comment 3 (In reply to #2) by Bob Clingan posted on 11/5/2016 at 1:01 PM

Yep- did all that. Error I left off on last time was:

Error: Error code 65 for command: xcodebuild with args: -xcconfig

Comment 4 (In reply to #3) by Bob Clingan posted on 11/5/2016 at 1:22 PM

Leaving off on today's attempt with:

** BUILD SUCCEEDED **

No target specified for emulator. Deploying to iPhone-SE, 10.1 simulator

An error was encountered processing the command (domain=com.apple.CoreSimulator.SimError, code=163):
Invalid device state

An error was encountered processing the command (domain=com.apple.CoreSimulator.SimError, code=163):
Invalid device state

Error: ENOENT: no such file or directory, stat '/Users/bclingan/Library/Logs/CoreSimulator/7F5B5E56-8BA6-490C-81E0-CC5B655590BC/system.log'

Comment 5 (In reply to #4) by Raymond Camden posted on 11/5/2016 at 2:36 PM

You got me there. I've had issues with the ios simulator before, but not this one in particular. However, since this really isn't on topic for this blog post, would you mind posting it to StackOverflow?

Comment 6 (In reply to #5) by Bob Clingan posted on 11/5/2016 at 4:01 PM

Will do - sorry for going off topic.

Comment 7 (In reply to #6) by Raymond Camden posted on 11/5/2016 at 4:24 PM

No worries. :)

Comment 8 by Reza Rahmati posted on 11/12/2016 at 6:32 PM

Thanks, it is great,
Just one issue, in doLogin after console.log('Login OK'); we need to add

loader.dismissAll(); to it.

Comment 9 by Reza Rahmati posted on 11/12/2016 at 6:33 PM

It would be great if you add an article for auth with LinkedIn, Google, Facebok (or atleast one of them)

Comment 10 (In reply to #8) by Raymond Camden posted on 11/12/2016 at 6:54 PM

I'll edit that - thanks (may be a day or two).

Comment 11 (In reply to #9) by Raymond Camden posted on 11/12/2016 at 6:54 PM

Sure thing - again - may be a day or two- got a busy week next week.

Comment 12 by Reza Rahmati posted on 11/13/2016 at 2:00 AM

Hi again
I used your article to create login page, but in SideMenu template,
Then I put the name of user in menu which is in app.component,
everything is fine except when user logout and login again with another user, menu doesn't refresh, it seems it doesn't understand that instance of user is changed

Comment 13 (In reply to #12) by Raymond Camden posted on 11/13/2016 at 6:43 PM

You got me on that. For something that specific, I'd suggest asking on Slack. (But let us know what you find.)

Comment 14 (In reply to #8) by Raymond Camden posted on 11/16/2016 at 4:49 PM

I also forgot to navigate the user - fixed that too. (Will be up here in 2 minutes.)

Comment 15 (In reply to #9) by Raymond Camden posted on 11/16/2016 at 9:27 PM

I'm done. I will blog it tonight or later this week - at a conference.

Comment 16 (In reply to #15) by Reza Rahmati posted on 11/16/2016 at 9:40 PM

That is great man, thank you

Comment 17 (In reply to #7) by José Ortega posted on 11/30/2016 at 5:32 PM

Bob Clingan, Were you able to solve the error?

Comment 18 by Paulo Ruca posted on 1/10/2017 at 1:53 PM

When i register a new user, where the user is stored ?

Comment 19 (In reply to #18) by Raymond Camden posted on 1/10/2017 at 3:10 PM

It's stored at Ionic as a service (hence the cost).

Comment 20 (In reply to #19) by Paulo Ruca posted on 1/11/2017 at 12:43 PM

Thanks.
But i upload the project into apps.ionic.io, when i register a new user the user is stored on the web.
sudo ionic login
sudo ionic upload

Just a simple tip ;)

Comment 21 (In reply to #20) by Raymond Camden posted on 1/11/2017 at 3:34 PM

To be clear, ionic login is a *different* login system. It is for Ionic developers. The user system I'm showing off here is for app users, the general public. Not related in any way.

Comment 22 by Abdo posted on 1/22/2017 at 11:04 PM

You sir, are my hero!

Comment 23 (In reply to #22) by Raymond Camden posted on 1/23/2017 at 12:31 PM

Thank you.

Comment 24 by Robrecht Van Melckebeke posted on 2/25/2017 at 6:56 PM

Thank you so much !! This is by far the most comprehensive tutorial about ionic 2 I read today..!

Comment 25 (In reply to #17) by Nesrine Belguith posted on 3/8/2017 at 10:32 AM

hi, https://uploads.disquscdn.c...
can you help me please i have this error when i add this code.

Comment 26 (In reply to #25) by Raymond Camden posted on 3/8/2017 at 4:15 PM

Double check you added the packages and imported everything correctly. The docs tell you to do this, just double check.

Comment 27 (In reply to #26) by Nesrine Belguith posted on 3/8/2017 at 5:10 PM

packages with npm install ?

Comment 28 (In reply to #27) by Raymond Camden posted on 3/8/2017 at 5:18 PM

No, I think you forgot to modify the app component file. Again - I hate to be brief, but be _sure_ you read the docs correctly and modified everything you need to.

Comment 29 (In reply to #28) by Nesrine Belguith posted on 3/8/2017 at 6:26 PM

thanks raymond it worked :)

Comment 30 (In reply to #29) by Raymond Camden posted on 3/8/2017 at 7:15 PM

Cool beans!

Comment 31 by Morin yves posted on 5/12/2017 at 9:02 AM

When I compile your example it displays the error below
I can not find the way of the solution on the web
I checked that the files are identical as your
I am a little novice in ionic2
Excuse me from my english i am french
thank you for helping me

Runtime Error
Cannot find module "./app.module."

http://localhost:8102/build/main.js:24942:16
__webpack_require__@http://localhost:8102/build/main.js:20:34
http://localhost:8102/build/main.js:66:37
global code@http://localhost:8102/build/main.js:67:12
Ionic Framework: 3.2.0
Ionic App Scripts: 1.3.7
Angular Core: 4.1.0
Angular Compiler CLI: 4.1.0
Node: 6.10.3
OS Platform: macOS Sierra
Navigator Platform: MacIntel
User Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.1 Safari/603.1.30

Comment 32 (In reply to #31) by Raymond Camden posted on 5/12/2017 at 1:34 PM

Unfortunately it is probably a RC->Ionic3 issue. I'd suggest starting new, and using my code as a general guide, but be aware it probably doesn't work 100% as is.

Comment 33 by Olu Dice posted on 5/18/2017 at 6:24 PM

Im getting this error.... Please help!

main.js:39339 Uncaught Error: Cannot find module "../pages/login/login"
at Object.<anonymous> (main.js:39339)
at __webpack_require__ (bootstrap 08ca007…:47)
at Object.<anonymous> (main.js:59707)
at __webpack_require__ (bootstrap 08ca007…:47)
at Object.<anonymous> (platform-browser-dynamic.es5.js:170)
at __webpack_require__ (bootstrap 08ca007…:47)
at Object.<anonymous> (src async:18)
at __webpack_require__ (bootstrap 08ca007…:47)
at Object.<anonymous> (main.js:159999)
at __webpack_require__ (bootstrap 08ca007…:47)


Comment 34 (In reply to #33) by Raymond Camden posted on 5/18/2017 at 6:26 PM

If you generated a *new* Ionic app, most likely my code here will not work with it. It was written with an earlier version of v2.

Comment 35 by ANuj posted on 6/7/2017 at 1:56 PM

Hi , Thanks for the post . I am facing one issue when i am using this in my app . When i am adding constructor(public user:User, public auth:Auth) then it is showing error : - Uncaught Error: Cannot find module "ionic-native" .. please give me idea what am i missing ?

Comment 36 (In reply to #35) by Raymond Camden posted on 6/7/2017 at 2:13 PM

You may need to npm install on my directory.

Comment 37 (In reply to #19) by Emiliano Ricci Aparicio posted on 6/14/2017 at 7:37 PM

Cost?

Comment 38 (In reply to #37) by Raymond Camden posted on 6/14/2017 at 7:50 PM

Looks to be free - http://ionicframework.com/p....

Comment 39 (In reply to #34) by Olu Dice posted on 6/24/2017 at 3:54 PM

You are right Thank you

Comment 40 by Sharanagouda K posted on 6/30/2017 at 7:39 AM

could you please post Linkedin login using ionic 2,angular 2

Comment 41 (In reply to #40) by Raymond Camden posted on 6/30/2017 at 1:17 PM

I believe folks have done so - or at least shown other social logins.

Comment 42 (In reply to #41) by Sharanagouda K posted on 7/3/2017 at 3:43 AM

other social logins I have done but linkedin only I am not able to do, I am not getting how to return the data from inappbrowser to app, redirect_uri http://localhost will work on laptop or browser not in app so please help me to solve this

Comment 43 (In reply to #42) by Raymond Camden posted on 7/3/2017 at 12:22 PM

Sorry - I don't have plans on working with LI and Ionic any time soon.

Comment 44 by schiffbruechige posted on 7/6/2017 at 3:44 PM

Just coming to try this mid-2017 - it looks like now in order to use the auth service, you need to also setup the Cloud Client first.. not sure if you left this out on purpose..

http://docs.ionic.io/setup....

Comment 45 (In reply to #44) by Raymond Camden posted on 7/6/2017 at 3:46 PM

Either a change or an accident on my part. ;)

Comment 46 by knightsjoker posted on 8/30/2017 at 6:24 PM

*hugs Raymond* thank you thank you thank you. I've been banging my head with the validation. I went about the complicated ways. And finally I found your blog. Just simple few lines... I went about the php ways, js, etc.

Comment 47 (In reply to #46) by Raymond Camden posted on 8/30/2017 at 8:03 PM

Hey - I'm sorry to say, but Ionic Auth is dead. :(

See: http://blog.ionic.io/sunset...

Comment 48 (In reply to #47) by knightsjoker posted on 8/31/2017 at 2:03 AM

Oh i'm not using the Auth ... it's these logic that I use :D if(this.data.username === '' || this.data.password === '')
I have my own auth-service

the validation .. I went about the long way ..using JS, even writing api php on the server side..cause i retrieve the data from mysql... It didn't even occur to me that the validation is that simple.

Comment 49 (In reply to #48) by Raymond Camden posted on 8/31/2017 at 1:15 PM

Ah - well it was just a way to short circuit checking the auth. No need to if no values. :)

Comment 50 by Rene César Pereira posted on 9/2/2017 at 5:03 PM

Very GOOD!

Comment 51 by Sanchit Mahajan posted on 9/27/2017 at 10:40 AM

After login.... I need some help... after login I have to get the all user details using token value.. pls help... i am really stuck here... thank you.

Comment 52 (In reply to #51) by Raymond Camden posted on 9/27/2017 at 1:25 PM

Please do not use this anymore - the Auth service is deprecated and will be shut down in January 2018.

Comment 53 (In reply to #52) by Sanchit Mahajan posted on 9/27/2017 at 3:26 PM

ohh... thanks..... But now I am taking users token value to get user details.. so in this case if i want to get users details and store users post with users id ... what process I have to use... pls guide sir... really thanking you sir...

Comment 54 (In reply to #53) by Raymond Camden posted on 9/27/2017 at 3:36 PM

I don't have an example of setting up a user system with Ionic outside of this. I believe other's exist and the best I can suggest is Googling for it.

Comment 55 (In reply to #54) by Sanchit Mahajan posted on 9/27/2017 at 3:49 PM

thanks.. But normally to get users details what I have to do? Is there any no use of token authentication in future?.. Pls guide Sir...Thanking You.

Comment 56 (In reply to #55) by Raymond Camden posted on 9/27/2017 at 3:50 PM

You're basically asking - how would I create a user system from scratch and use it with Ionic. I can't help you with that. It's definitely doable - it's just not something I can describe in a blog comment. I'd suggest Googling as there are a number of other bloggers out there demonstrating stuff with Ionic.

Comment 57 (In reply to #56) by Sanchit Mahajan posted on 9/27/2017 at 4:47 PM

Thanks sir.. I want just some guidelines to succeed..what to do.?... thanks..

Comment 58 (In reply to #57) by Raymond Camden posted on 9/27/2017 at 5:35 PM

Your question is so open ended there is no way I could answer it here. Sorry - but I really can't help more on this.

Comment 59 by Sanchit Mahajan posted on 10/24/2017 at 6:11 PM

How to use contact number instead email.. thank you..

Comment 60 (In reply to #59) by Raymond Camden posted on 10/24/2017 at 7:14 PM

The Ionic User service is deprecated and will be shut down next year.

Comment 61 (In reply to #60) by Sanchit Mahajan posted on 10/25/2017 at 3:35 PM

k. Thank You Sir.. Is any other way to succeed?

Comment 62 (In reply to #61) by Raymond Camden posted on 10/26/2017 at 1:03 PM

Use another service. :) There are others out there - I'd suggest what I'd do - Google.