Angular 2 Perform Login Authentication (i am getting into infinite loop.) - javascript

I am trying to create a website backend for which I need User Authentication in Angular 2. But I am unable to resolve some of issue, this is what I am doing.
This is my routing file.
const appRoutes: Routes = [
{
path: 'admin',
component: AdminloginComponent,
canActivate: [LoggedInGuard]
},
{
path: '',
component: HomepageComponent
},
{
path: '**',
component: OurservicesComponent
},
];
This is my gaurd file: // after successfull login i am setting is_logged_in in local storage.
import { Injectable } from '#angular/core';
import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '#angular/router';
#Injectable()
export class LoggedInGuard implements CanActivate {
constructor(private router: Router) { }
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
if(localStorage.getItem("is_logged_in"))
{
this.router.navigate(['/admin/dashboard']);
return true;
}
return false;
}
}
** My issue is that when the url is /admin canActivate comes into action and if I get local storage I am navigating to admin/dashboard.
But if user is logged out then in this case on /admin their should be loginform, but if I navigate to /admin if not logged in then it comes into canActivate and again their is nothing in local storage so it goes to admin and so on. So what is the correct way to solve this.
Thanks in advance.

It unclear what exactly you try to accomplish, but redirect with router.navigate should go with return false;, not with return true;
Either
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
if(localStorage.getItem("is_logged_in")) {
return true;
}
this.router.navigate(['/admin/dashboard']);
return false;
}
or
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
if(localStorage.getItem("is_logged_in")) {
this.router.navigate(['/admin/dashboard']);
return false;
}
return true;
}

Related

How to go directly to login page if home component is loaded

I have angular 8 application and I am using identity server. And I have a home component and a navbar component. And on a navbar component you have a login button what the user will directed to the login page.
But what I want now is that if you go to home: http://localhost:4200/home. That the use will directly go to the login page.
So this is the home component:
export class HomeComponent implements OnInit {
constructor(public oidSecurityService: OidcSecurityService) {}
ngOnInit() {
this.login();
console.log(this.oidSecurityService.getState);
}
login() {
this.oidSecurityService.authorize();
}
}
And this is the navbar component:
export class NavBarComponent implements OnInit, OnDestroy {
currentUser$: Observable<any> ;
userData: any;
isAuthenticated$: Observable<boolean> ;
isAuthenticated: boolean;
constructor(
public oidSecurityService: OidcSecurityService,
private _oidcConfigService: OidcConfigService,
public platform: Platform
) {}
ngOnInit(): void {
this.isAuthenticated$ = this.oidSecurityService.isAuthenticated$;
this.oidSecurityService.checkAuth().subscribe(auth => {
console.log('is authenticated', auth);
});
this.oidSecurityService.userData$.subscribe(userData => {
console.log(this.userData = userData);
});
this.isAuthenticated$.subscribe((data) => {});
}
ngOnDestroy(): void {}
login() {
this.oidSecurityService.authorize();
console.log(this.oidSecurityService.getState);
}
logout() {
this.oidSecurityService.logoff();
}
get token() {
const claims: any = this.oidSecurityService.getToken();
return claims ? claims : null;
}
}
So when you trigger the login button the user sees the login page.
But so how to trigger directly the login page from home? So if you go to: http://localhost:4200/home user sees directly the login page
So what I have to change?
I don't have login component. I am using Identity server. So this: this.oidSecurityService.authorize(); will go to this page, where user can login:
http://localhost:4430/Account/Login?ReturnUrl=%2Fconnect%2Fauthorize%2Fcallback%3Fclient_id%3Dcrowd-dashboard%26redirect_uri%3Dhttp%253A%252F%252Flocalhost%253A4200%26response_type%3Dcode%26scope%3Dopenid%2520profile%2520dashboard-api%26nonce%3Ddde8a3159556f497f4d3cf223540b26a1dYah1g3W%26state%3D90d1eb8c5acbe26ededcbf0a61c4e4da70sjjLw1b%26code_challenge%3DEJ1nAdtWM45Z3IoNnwVZWzAzKTz2xDC1y1c1qi1bwTU%26code_challenge_method%3DS256
You mean like this in ngOninit:
window.location.replace('http://localhost:4430/Account/Login?ReturnUrl=%2Fconnect%2Fauthorize%2Fcallback%3Fclient_id%3Dcrowd-dashboard%26redirect_uri%3Dhttp%253A%252F%252Flocalhost%253A4200%26response_type%3Dcode%26scope%3Dopenid%2520profile%2520dashboard-api%26nonce%3D3daf0245322085adfada4bb19abb7da0c8YW1v4iG%26state%3D90d1eb8c5acbe26ededcbf0a61c4e4da70sjjLw1b%26code_challenge%3DQ-R9LMcBmSyrUCS4ZK0WljuA__z2p7S3WMXsFOjMyY0%26code_challenge_method%3DS256');
so this is my guard:
#Injectable({ providedIn: 'root' })
export class AuthorizationGuard implements CanActivate {
constructor(private oidcSecurityService: OidcSecurityService, private router: Router) {}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
return this.oidcSecurityService.isAuthenticated$.pipe(
map((isAuthorized: boolean) => {
console.log('AuthorizationGuard, canActivate isAuthorized: ' + isAuthorized);
if (!isAuthorized) {
this.router.navigate(['/unauthorized']);
return false;
}
return true;
})
);
}
}
and this is my app.routing.module.ts:
const routes: Routes = [
{ path: 'home', component: HomeComponent, canActivate: [AuthorizationGuard] },
{ path: '**', redirectTo: 'home' }
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule {}
Use routers and guards as described in
https://itnext.io/handle-restricted-routes-in-angular-with-route-guards-95c93be9d05e
The Guards will instruct the routers when user can access to specific routes.
Also take a look at:
How to check: is the User logged in with angular-6-social-login npm package?

How to redirect to a route on ngOnit?

I am trying to redirect to a route when user directly paste the url.
I want to direct the user to /en/sell page on hitting the url
http://localhost:3000/en/sell/confirmation
here is my code,
ngOnInit() {
this.storedListing = this.sellerFlow.getSellerFlowObject();
if (this.storedListing === null) {
this.router.navigate(['./']);
}
}
i can see this.router getting executed but the application does not have any change. what is the issue?
You have to use ../ if you want to go from /en/sell/confirmation to /en/sell/:
this.router.navigate(['../']);
See the documentation.
You could instead redirect from your routing:
{
path: 'en/sell/confirmation',
redirectTo: 'en/sell',
pathMatch: 'full'
},
import {OnInit} from '#angular/core';
import {Router} from '#angular/router';
export class AddDisplay {
constructor(private router: Router){}
ngOnInit() {
this.router.navigate(['./SomewhereElse']);
}
}
You have to specify the relativeTo param, as below:
import { ActivatedRoute, Router } from '#angular/router';
constructor(private route: ActivatedRoute, private router: Router) { }
ngOnInit() {
this.storedListing = this.sellerFlow.getSellerFlowObject();
if (this.storedListing === null) {
this.router.navigate(['../'], { relativeTo: this.route });
}
}

Struggling through AuthGuard in Angular

Tried to follow AuthGuard example available here:
http://www.sparkbit.pl/angular-2-route-guards-real-life-example/
Unfortunately, while trying to implement the ActivationGuard.ts file, I'm receiving few errors.
ERROR in C:/Users/app/src/app/ActivationGuard.ts (6,24): Cannot find name 'ActivatedRouteSna
pshot'.)
C:/Users/app/src/app/ActivationGuard.ts (6,55): Cannot find name 'RouterStateSnapshot'.)
C:/Users/app/src/app/ActivationGuard.ts (13,62): Cannot find name 'CurrentUserService'.)
C:/Users/app/src/app/ActivationGuard.ts (15,31): Cannot find name 'ActivatedRouteSnapshot'.)
C:/Users/app/src/app/ActivationGuard.ts (15,62): Cannot find name 'RouterStateSnapshot'.)
Which basically means that the elements inside the CanActivate interface and inside constructors are not defined.
routing file:
import { WorksheetAccessGuard } from "./ActivationGuard";
const appRoutes: Routes = [
{ path: '', component: LoginComponent },
{ path: 'app', component: AppComponent, canActivate: [WorksheetAccessGuard] },
{ path: '**', redirectTo: '' }
];
My question: From where could I get these missing elements?
Provided image of my IDE: (the red words are the missing ones)
EDIT
I have made a custom service. I'm not sure if its fine or not:
import {Injectable} from '#angular/core';
import {Http} from '#angular/http';
#Injectable()
export class UserAuthenticationService {
isUserAuthenticated: boolean = false;
username: string;
constructor(private http: Http) {
}
authentication() {
this.http.get(`http://localhost/api/auth/isLogged/${this.username}`)
.subscribe(res => {
this.isUserAuthenticated = res.json();
},
err => {
console.error('An error occured.' + err);
});
}
}
Now I'm receiving some error inside the AuthGuard file:
ERROR PIC
**My main goal is checking with every component change (when user navigates over the page) if he is logged or not. If not - return him to the login page.
EDIT2
Can I just post all logic from the service in the AuthGuard file? It will look like:
import {Injectable} from '#angular/core';
import {Router, RouterStateSnapshot, ActivatedRouteSnapshot} from '#angular/router';
import {Observable} from 'rxjs/Observable';
import {UserAuthenticationService} from './UserAuthenticationService';
import {Http} from '#angular/http';
interface CanActivate {
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean>|Promise<boolean>|boolean
}
#Injectable()
export class WorksheetAccessGuard implements CanActivate {
private static username: string;
isUserAuthenticated: boolean = false;
constructor(private router: Router, private userService: UserAuthenticationService, private http: Http) {
}
public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
this.http.get(`http://localhost/api/auth/isLogged/${this.username}`)
.subscribe(res => {
this.isUserAuthenticated = res.json();
},
err => {
console.error('An error occured.' + err);
});
if (!this.isUserAuthenticated) {
this.router.navigate(['/']);
return false;
}
return true;
}
}
RouterStateSnapshot and ActivatedRouteSnapshot are imported from #angular/router, while the currentUser Service is supposed to be your own where you should store the authenticated state of your User (with a boolean for example).
You retrieve an instance of it through Dependency Injection in your guard's constructor like so :
import { CurrentUserService } from './path/to/your/service/file';
import { RouterStateSnapshot, ActivatedRouteSnapshot } from '#angular/router';
constructor(private userService: CurrentUserService)
{}
Your service needs to be provided in your module, (as well as your guard), and you need to have a property like this in your CurrentUserService :
CurrentUserService :
isAuthenticated: boolean = false;
That way, when you log in from your Login Component (I assume you have one), you can set the service property to true :
LoginComponent :
import { CurrentUserService } from './path/to/your/service/file';
constructor(private userService: CurrentUserService)
{}
login() {
... // Your existing code where you login on form submit or anything
this.userService.isAuthenticated = true;
}
EDIT :
Check out my example, it should fit for yours.
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
if (!this.authService.isAuthenticated) {
// Deny navigation and redirect to login
this.router.navigate(['/path/to/login']);
return false;
}
// Allow navigation (be careful that the guard always resolve a value)
return true;
}

Angular make canActivateGuard asynchronous

I have a web application where there is a JWT token passed to the admin service. This JWT comes from the query URL as there is a redirect from another application. The constructor in the service checks for that URL and set the token value to it (if that params is there).
The problem I am facing is that the canActivateGuard fires too early. When this is called the observable to get the JWT in the service is not solved yet, so the JWT is always not there when the guard is being fired.
I have figure out that to make this work isLoggedIn() in the AdminService has to become an observable that listen to the changes in the URL, and the canActivate() in the guard has to subscribe to it, but can not make this works.
The below code is what I got so far
// Admin Service
import { Injectable } from '#angular/core';
import { Observable } from 'rxjs/Rx';
import 'rxjs/Rx';
import {Router, ActivatedRoute, Params} from '#angular/router';
#Injectable()
export class AdminService {
token: string;
constructor(private activatedRoute: ActivatedRoute) {
activatedRoute.queryParams.subscribe(
(params) => {
console.log('queryParams', params);
if(localStorage.getItem('jwt')) {
this.token = localStorage.getItem('jwt');
}
else if(params['jwt']) {
localStorage.setItem('jwt', params['jwt']);
this.token = params['jwt'];
}
});
}
// Check that JWT is in local storage and valid
isLoggedin() {
return (localStorage.getItem('jwt') !== null && localStorage.getItem('jwt') !== 'undefined');
}
}
// Can Activate guard
// Note that this.authService.isLoggedIn() is called before the set JWT in the service is solved
#Injectable()
export class AuthGuard implements CanActivate {
constructor(
private authService: AdminService,
private router: Router
) {
}
canActivate() {
if (this.authService.isLoggedin()) {
console.log('all ok, proceed navigation to routed component')
return true;
}
else {
// start a new navigation to redirect to login page
this.router.navigate(['/unauthorized']);
return false;
}
}
}
using ActivatedRouteSnapshot and RouterStateSnapshot your problem will be get resolve, and you don't need to subscribe for JWT in your service.
Here is a sample of my code which i had used in my Angular2 application.
auth-guard.ts
import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '#angular/router';
import { Injectable } from '#angular/core';
import { Observable } from 'rxjs/Observable';
import { AuthCookie } from '../shared/services/auth-cookies-handler';
#Injectable()
export default class AuthGuard implements CanActivate {
constructor(private router: Router, private _authCookie: AuthCookie) { }
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | boolean {
if (this._authCookie.getAuth()) {
return true;
}
else {
this.router.navigate(['/login']);
return false;
}
}
}
Hope this will help you.

Angular 2 Role based navigation on same path

I've a small question regarding Angular 2 router using version 3.0.0-rc.1 I want to navigate to different home component based on user role such as AdminComponent or UserComponent. Can anyone please help in modifying below routes so that I can achieve the desired functionality?
{path: 'login', component: LoginComponent}, // <--- This redirects to '/' in case user is logged in
{
path: '',
component: HomeComponent,
canActivate: [AuthGuardService], // <--- Check if user is logged in, else redirect to login
children: [
{
path: '',
component: AdminComponent // <--- Want to navigate here if user role is 'admin'
},
{
path: '',
component: UserComponent // <--- Want to navigate here if user role is 'user'
}
]
}
AuthGuardService.ts
import {Injectable} from "#angular/core";
import {CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot} from "#angular/router";
import {AuthService} from "./auth.service";
#Injectable()
export class AuthGuardService implements CanActivate {
constructor(private authService: AuthService, private router: Router) {
}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
if (this.authService.isLoggedIn()) {
return true;
}
// Store the attempted URL for redirecting
this.authService.redirectUrl = state.url;
// Navigate to the login page with extras
this.router.navigate(['/login']);
return false;
}
}
AuthService.ts
import {Injectable} from "#angular/core";
#Injectable()
export class AuthService {
redirectUrl: string;
logout() {
localStorage.clear();
}
isLoggedIn() {
return localStorage.getItem('token') !== null;
}
isAdmin() {
return localStorage.getItem('role') === 'admin';
}
}
Thanks.
You can achieve it by below way.
{path: 'login', component: LoginComponent}, // <--- This redirects to '/' in case user is logged in
{
path: '',
component: HomeComponent,
canActivate: [AuthGuardService],
}
this is your home component html(home.component.html)
<app-admin *ngIf="user_role==='admin'"></app-admin>
<app-user *ngIf="user_role==='user'"></app-user>
make sure you are assigning user_role in your typescript file of home component
this is your admin component html(admin.component.html)
<div>
this is admin component
</div>
this is your user component html(user.component.html)
<div>
this is user component
</div>
Hope, This will help you.
The problem is that you can't have two routes with the same path. The easiest change you can make is to change the path to something like this:
{
path: 'admin',
component: AdminComponent
},
{
path: 'user',
component: UserComponent
}
This is probably the best option because since you want your components to be different based on the user role. You might also want other components to be different and you can do that easily by adding children to the admin or the user routes.
In your AuthGuard you still only return true, but you make two other guards for the admin and user routes. Which check if the user is or isn't the admin.
And you redirect to the correct route by checking the user role once he loges in and then in the component you do router.navigate(['/admin']) or router.navigate(['/user'])

Categories