localstorage value visible only after page reload - jwt

When the user login I want to display the username of that user at the navbar. I have set the token and username to the localStorage after user succesfully login. My issue is username is not displayed at the navbar unless I refresh the page.
I am not sure how can I fix this problem.
Can anybody help me
Thank You.
login component
onSubmit = function () {
this.userService.loginUser(this.loginUserData).subscribe(
res => {
this.tokenService.handle(res);
this.authService.changeAuthStatus(true);
},
error => console.log(error)
);
}
auth service
export class AuthService {
private loggedIn = new BehaviorSubject<boolean>(this._tokenService.loggedIn());
authStatus = this.loggedIn.asObservable();
user = this.tokenService.getUser();
changeAuthStatus(value: boolean) {
this.loggedIn.next(value);
}
constructor(private tokenService: TokenService) {}
}
token service
handle(res) {
this.setToken(res);
}
setToken(res) {
localStorage.setItem('token', res.access_token);
localStorage.setItem('user', res.user);
}
getToken() {
return localStorage.getItem('token');
}
getUser() {
return localStorage.getItem('user');
}
}
navbar component
ngOnInit() {
this.authService.authStatus
.subscribe(
value => {
this.loggedIn = value
}
);
//set the username on navbar
this.user = this.tokenService.getUser();
}

You auth service function is a callback that will fire success or failure event when all operations are complete hence the code this.user = this.tokenService.getUser(); executed before the localstorage is populated. Try moving this code inside subscribe method of authService.authStatus.
ngOnInit() {
this.authService.authStatus
.subscribe(
value => {
this.loggedIn = value
}
);
//set the username on navbar
this.user = this.tokenService.getUser();
}
like this.
ngOnInit() {
this.authService.authStatus
.subscribe(
value => {
this.loggedIn = value
this.user = this.tokenService.getUser();
}
);
}

Try making the call
this.user = this.tokenService.getIser()
inside the subscribe.

Related

Events in Ionic v4

I'm using Ioni v4Beta and I'm traying to update the sidemenu when the user is login.
I search but the usual solution is use Events:
Ionic 3 refresh side menu after login
https://ionicframework.com/docs/api/util/Events/
But in the new version I don't find it, and I don't know how to do it
https://beta.ionicframework.com/docs/api
Thanks a lot, but I finally find how to import it:
import { Events } from '#ionic/angular';
Example on how to do it with subjects:
export const someEvent:Subject = new Subject();
export class ReceivingClass implements OnDestroy, OnInit
{
private someEventSubscription:Subscription;
public OnInit():void{
someEventSubscription = someEvent.subscribe((data) => console.log(data);
}
public onDestroy():void{
someEvent.unsubscribe();
}
}
export class SendingClass implements OnInit
{
public OnInit():void{
setTimeout(() => {
someEvent.next('hi');
}, 500);
}
}
Are you aware that Ionic v4 events will be deprecated soon?
I was also trying to update the sidemenu when a user logs in as well, so i tried using: import { Events } from '#ionic/angular';
However I got a warning referring me to this link https://angular.io/guide/observables#basic-usage-and-terms which I failed to follow because am not that familiar with observables.
After much research I found that I can still use events but I had to import them from angular's router directive.
This was my code before:
/* import was */
import { Events } from '#ionic/angular';
import { Storage } from '#ionic/storage';//ignore this import if doesn't apply to your code
/* inside the class */
constructor(
private events: Events,
private storage: Storage
) {
this.events.subscribe("updateMenu", () => {
this.storage.ready().then(() => {
this.storage.get("userLoginInfo").then((userData) => {
if (userData != null) {
console.log("User logged in.");
let user = userData.user;
console.log(user);
}
else {
console.log("No user found.");
let user = {};
}
}).catch((error)=>{
console.log(error);
});
}).catch((error)=>{
console.log(error);
});
});
}
changes i made that actually got my code working and deprecation warning gone:
/* import is now */
import { Router,RouterEvent } from '#angular/router';
import { Storage } from '#ionic/storage';//ignore this import if it does't apply to your code
Rest of code
constructor(
public router: Router,
public storage: Storage
){
this.router.events.subscribe((event: RouterEvent) => {
this.storage.ready().then(() => {
this.storage.get("userLoginInfo").then((userData) => {
if (userData != null) {
/*console.log("User logged in.");*/
let user = userData.user;
/*console.log(this.user);*/
}
else {
/*console.log("No user found.");*/
let user = {};
}
}).catch((error)=>{
console.log(error);
});
}).catch((error)=>{
console.log(error);
});
});
}
I got the idea after seeing this https://meumobi.github.io/ionic/2018/11/13/side-menu-tabs-login-page-ionic4.html. I hope my answer can be useful.
Steps to resolve the issue
import events in login page and in sidemenu view
In login page, after login success do your logic to publish the events.
for eg:
this.authService.doLogin(payload).subscribe((response) => {
if (response.status) {
this.storage.set('IS_LOGGED_IN', true);
this.events.publish('user:login');
}
}, (error) => {
console.log(error);
});
In sidemenu view, create a listener to watch the events 'user:login'
for eg:
this.menus = [];
// subscribe events
this.events.subscribe('user:login', () => {
// DO YOUR LOGIC TO SET THE SIDE MENU
this.setSidemenu();
});
// check whether the user is logged in or not
checkIsUserloggedIn() {
let isLoggedIn = false;
if (this.storage.get('IS_LOGGED_IN') == '' ||
this.storage.get('IS_LOGGED_IN') == null ||
this.storage.get('IS_LOGGED_IN') == undefined) {
isLoggedIn = false;
} else {
isLoggedIn = true;
}
return isLoggedIn;
}
// to set your sidemenus
setSidemenu() {
let isUserLoggedIn = this.checkIsUserloggedIn();
if(isUserLoggedIn) {
this.menus = ['Home', 'Aboutus', 'Contactus', 'My Profile', 'Logout'];
} else {
this.menus = ['Login', 'Home', 'Aboutus', 'Contactus'];
}
}

Ionic push view over modal (need to login)

I have a mobile app where the user needs to re-login after some time for security reasons. The thing is that the content that was open in the background needs to stay there, and open after the login. So, even if it was a modal.
What is the best way to do this.
Pushing the login view when a modal is open doesn't help, since the view is put behind the modal.
Thanks in advance!
on the current page
public openLogin() {
let loginModal = this.modalController.create(LoginPage, { modal: true });
loginModal.present();
loginModal.onDidDismiss(data => {
if (data) {
this.profileData = data;
} else {
}
});
};
on loginPage
this.userService.login(username, password)
.subscribe(
data => {
console.log(data);
if (data.success) {
var user = data.result;
this.userService.setSession(user);
if (this.itsModal) {
this.closeModal(data)
}
else {
this.gotoHome();
}
} else {
// error handling
}
},
error => {
console.log(error);
}
);
public closeModal(data: any = null) {
this.viewController.dismiss(data);
}

Facebook and Firebase Authentication in Angular2

I found some troubles with firebase and facebook authentication using Angular2. I´ve created this method to verify with the console if the user is logged in but I think this isn´t the correct way to do this because the console is not reporting me the correct values.
So here is my code:
export class AppComponent {
user: Observable<firebase.User>;
constructor(private afAuth: AngularFireAuth, private db: AngularFireDatabase) {
}
ngOnInit() {
this.user = this.afAuth.authState;
if (this.user)
console.log('NOT LOGGED');
else
console.log('LOGGED IN', this.afAuth.authState);
}
login() {
this.afAuth.auth.signInWithPopup(new firebase.auth.FacebookAuthProvider())
.then((res) => console.log(res));
}
logout() {
this.afAuth.auth.signOut();
}
}
How can I use the user observable in a better way?
Since authState returns an observable you need to subscribe to get the value:
ngOnInit() {
this.afAuth.authState.subscribe(auth => {
if (auth) {
console.log('LOGGED IN', auth);
} else {
console.log('NOT LOGGED');
}
}

REST service exception handling in Angular2

First, I must mention that I'm a beginner in Angular and I'm kind of stucked with my sample code.
I created some simple login app which prompts for username and password, calls login REST service (written in Java) that returns some token at login success or throws an exception at login failure.
Here's some of my code.
Login component:
import { Component, OnInit } from '#angular/core';
import { Router } from '#angular/router';
import { AuthenticationService } from '../_services/index';
#Component({
moduleId: module.id,
templateUrl: 'login.component.html'
})
export class LoginComponent implements OnInit {
model: any = {};
error = '';
constructor(
private router: Router,
private authenticationService: AuthenticationService) { }
ngOnInit() {
// reset login status
this.authenticationService.logout();
}
login() {
this.authenticationService.login(this.model.username, this.model.password)
.subscribe(result => {
if (result === true) {
this.router.navigate(['/']);
} else {
this.error = 'Login failed!';
}
},
err => {
this.error = 'Login failed!';
});
}
}
Authentication service:
import { Injectable } from '#angular/core';
import { Http, Headers, RequestOptions, Response } from '#angular/http';
import { Observable } from 'rxjs';
import { CookieService } from 'angular2-cookie/core';
import { CookieOptionsArgs } from 'angular2-cookie/services/cookie-options-args.model';
import 'rxjs/add/operator/map';
#Injectable()
export class AuthenticationService {
public token: string;
constructor(private http: Http, private cookieService: CookieService) {
// set token if saved in cookie
this.token = cookieService.get('token');
}
login(username, password): Observable<boolean> {
return this.http.post('http://localhost:9081/MyApp/login?username=' + username + '&password=' + password, new RequestOptions({}))
.map((response: Response) => {
// login successful if there's a token in the response
let token = response.text();
if (token !== '-1') {
// set token
this.token = token;
// store token in cookie to keep user logged
let opts: CookieOptionsArgs = {
path: '/'
};
this.cookieService.put('token', token, opts);
// return true to indicate successful login
return true;
} else {
// return false to indicate failed login
return false;
}
});
}
logout(): void {
// clear token, remove cookie to log user out
this.token= null;
this.cookieService.remove('token');
}
}
Everything works as expected. When login is successful, token is returned and I'm redirected to a "home" page. Otherwise, a "Login falied" message appears on a login page and no redirection occurs. What bothers me is that I don't exactly know why login fails: is it because username doesn't exist or is it maybe because password is wrong. What is the proper way to handle exceptions thrown by REST service? I assume that authentication service is the right place but I don't exactly know how to do it. I tried to extract some info from request object but request mapping doesn't happen if exception is thrown.
Thanks for help!
It seems you're looking for catching the exception occuring on error login in AuthenticationService . If it's the case add .catch section after .map, like in this subject :
best practives catching error Angualr 2
.catch((error: any) => { //catch Errors here using catch block
if (error.status === 500) {
// Display your message error here
}
else if (error.status === 400) {
// Display your message error here
}
});
i have implemented my code this way :
login(email: string, password: string): Observable<boolean> {
return new Observable(observer => {
var data = { email: email, password: password };
this.http.post(this.server_url + '/auth/authenticate', data).subscribe(x => {
var result = {
email: x.json().email,
token: x.json().token,
roles: x.json().roles.map(x => x.name)
}
localStorage.setItem(this._userKey, JSON.stringify(result));
observer.next(true);
observer.complete();
}, er => {
if (er.status == 401) {
observer.next(false);
observer.complete();
} else {
console.log(er);
observer.error(er);
observer.complete();
}
});
});
}
so it handle three possibilities :
if cridential is OK it returns true
if credential is wrong return false (remember your server must
return 401 status !)
otherwise there is problem in server and throw error
and in handler i got :
login() {
this.loading = true;
this.authenticationService.login(this.model.username, this.model.password)
.subscribe(result => {
if (result == true) {
this.router.navigate(['/home']);
} else {
this.error = 'Username or password is incorrect';
this.loading = false;
}
}, err => {
this.error = 'Unexpected error occured.. please contact the administrator..';
this.loading = false;
});
}

Angular 2 data service

I'm building an observable data service based on the following article: https://coryrylan.com/blog/angular-2-observable-data-services
In the article he used an array as an example, here I will use the user object since I'm developing the user service.
Here's what I got:
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Events, SqlStorage, Storage } from 'ionic-angular';
import { Subject } from 'rxjs/Subject';
export interface DataStore {
user: Object
}
#Injectable()
export class UserService {
private baseUrl: string;
private storage: Storage;
private _user$: Subject<Object>;
private dataStore: DataStore;
constructor(
private http: Http
) {
this.baseUrl = 'http://localhost:3000';
this.storage = new Storage(SqlStorage);
this._user$ = <Subject<Object>>new Subject();
this.dataStore = {
user: { name: '' }
};
}
set user$(user: Object) {
this.storage.set('user', JSON.stringify(user));
this.dataStore.user = user;
this._user$.next(this.dataStore.user);
}
get user$() {
return this._user$.asObservable();
}
loadUser() {
return this.storage.get('user').then(
((user: string): Object => {
this.dataStore.user = JSON.parse(user);
this._user$.next(this.dataStore.user);
return this.dataStore.user;
})
);
}
login(accessToken: string) {
return this.http
.post('http://localhost:3000/login', { access_token: accessToken })
.retry(2)
.map((res: Response): any => res.json());
}
logout(): void {
this.storage.remove('user');
}
}
To authenticate I call the login() function and set the user data if everything ok.
this.userService.login(this.data.accessToken)
.subscribe(
(user: Object) => {
this.userService.user$ = user;
this.nav.setRoot(EventListComponent);
},
(error: Object) => console.log(error)
);
I feel it is better set the user data inside the service. I could do the following:
login(accessToken: string) {
return this.http
.post('http://localhost:3000/login', {
access_token: accessToken
})
.retry(2)
.map((res: Response): any => res.json())
.subscribe(
(user: Object) => {
this.userService.user$ = user;
this.nav.setRoot(EventListComponent);
},
(error: Object) => console.log(error)
);
}
But I won't be able to subscribe to the login() function in the component since it's already subscribed. How could I redirect the user if everything ok or show an alert if anything goes wrong in the component but setting the user inside the service?
In the main component I load the user data and set the rootPage:
this.userService.loadUser().then(
(user: Object) => this.rootPage = EventListComponent,
(error: Object) => this.rootPage = LoginComponent
);
I thought that calling the loadUser() function at this time I would not have to call it again, but I have to call it in all components that I need the user data:
this.user = this.userService.user$;
this.userService.loadUser();
I don't think the service is the way it should, what could I improve? Is there any better way to achieve what I want? Any example or idea?