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
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:
Clicking register simply hides the initial form and shows the (slightly larger) registration form:
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.
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.
What I imagine the "Ionic Love Train" would look like...
Archived Comments
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...
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
Yep- did all that. Error I left off on last time was:
Error: Error code 65 for command: xcodebuild with args: -xcconfig
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'
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?
Will do - sorry for going off topic.
No worries. :)
Thanks, it is great,
Just one issue, in doLogin after console.log('Login OK'); we need to add
loader.dismissAll(); to it.
It would be great if you add an article for auth with LinkedIn, Google, Facebok (or atleast one of them)
I'll edit that - thanks (may be a day or two).
Sure thing - again - may be a day or two- got a busy week next week.
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
You got me on that. For something that specific, I'd suggest asking on Slack. (But let us know what you find.)
I also forgot to navigate the user - fixed that too. (Will be up here in 2 minutes.)
I'm done. I will blog it tonight or later this week - at a conference.
That is great man, thank you
Bob Clingan, Were you able to solve the error?
When i register a new user, where the user is stored ?
It's stored at Ionic as a service (hence the cost).
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 ;)
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.
You sir, are my hero!
Thank you.
Thank you so much !! This is by far the most comprehensive tutorial about ionic 2 I read today..!
hi, https://uploads.disquscdn.c...
can you help me please i have this error when i add this code.
Double check you added the packages and imported everything correctly. The docs tell you to do this, just double check.
packages with npm install ?
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.
thanks raymond it worked :)
Cool beans!
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
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.
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)
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.
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 ?
You may need to npm install on my directory.
Cost?
Looks to be free - http://ionicframework.com/p....
You are right Thank you
could you please post Linkedin login using ionic 2,angular 2
I believe folks have done so - or at least shown other social logins.
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
Sorry - I don't have plans on working with LI and Ionic any time soon.
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....
Either a change or an accident on my part. ;)
*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.
Hey - I'm sorry to say, but Ionic Auth is dead. :(
See: http://blog.ionic.io/sunset...
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.
Ah - well it was just a way to short circuit checking the auth. No need to if no values. :)
Very GOOD!
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.
Please do not use this anymore - the Auth service is deprecated and will be shut down in January 2018.
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...
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.
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.
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.
Thanks sir.. I want just some guidelines to succeed..what to do.?... thanks..
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.
How to use contact number instead email.. thank you..
The Ionic User service is deprecated and will be shut down next year.
k. Thank You Sir.. Is any other way to succeed?
Use another service. :) There are others out there - I'd suggest what I'd do - Google.