I have an angular application where I want to use authguard to protect some routes but for some reason it does not work. I first make a post request to my spring boot backend, if that gives me success I set a value to true and check that value in my canActivate methode. But it does not work, it does not even go into that method. I thought it is called automatically if I set it in my path in app-route.module. How can I use it correctly?
Authenticationservice with authguard together:
export class AuthenticationService {
constructor(
private http: HttpClient,
private router: Router,
) { }
authenticateUser(login: LoginModel){
return this.http.post(environment.rootUrl + 'authenticate', {
username: login.username,
password: login.password,
}).subscribe({
next: (data) => {
localStorage.setItem('token', data.toString())
}, error: (error) => {
this.isAuthenticated = false
}
})
}
isUserLoggedIn(){
return !!localStorage.getItem('token')
}
}
Authguard:
#Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate {
constructor(
private auth: AuthenticationService,
private router: Router
) {
}
canActivate(): Promise<boolean> {
return new Promise(resolve => {
if (this.auth.isUserLoggedIn()) {
resolve(true)
} else {
this.router.navigate(['authenticate'])
resolve(false)
}
})
}
}
App-module:
#NgModule({
declarations: [AppComponent, NxWelcomeComponent],
imports: [
BrowserModule,
BrowserAnimationsModule,
NbLayoutModule,
LoginComponentModule,
AppRoutingModule,
NbThemeModule.forRoot({ name: 'default' }),
NbLayoutModule,
NbEvaIconsModule,
],
providers: [AuthGuard],
bootstrap: [AppComponent],
})
export class AppModule {}
App-routing-module:
const routes: Routes = [
{path: 'dashboard' , component: DashboardComponent, canActivate: [AuthGuard]},
{path: 'authenticate', component: LoginComponent},
{path: '' , redirectTo: 'authenticate', pathMatch: 'full'}
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
providers: [AuthGuard]
})
export class AppRoutingModule {
}
You need to route to the dashboard after you do the login:
The routes:
const routes: Routes = [
{
path: 'dashboard',
component: DashboardComponent,
canActivate: [AuthGuard],
},
{ path: '', redirectTo: 'authenticate', pathMatch: 'full' },
];
The Component:
export class AppComponent {
constructor(
private authService: AuthenticationService,
private router: Router
) {}
callServiceMethod(value: string) {
// This will need to wait for auth to finish before navigating in the actual app.
this.authService.authenticateUser(value);
if (this.authService.isAuthenticated) {
this.router.navigate(['dashboard']);
}
}
}
The Guard:
#Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate {
constructor(private auth: AuthenticationService, private router: Router) {}
canActivate(): Promise<boolean> {
return new Promise((resolve) => {
console.log('running auth guard!');
if (this.auth.isUserLoggedIn()) {
resolve(true);
} else {
this.router.navigate(['authenticate']);
resolve(false);
}
});
}
}
Here is an updated example:
https://stackblitz.com/edit/angular-ivy-5excpc?file=src%2Fapp%2Fapp.component.ts
Your canActivate should only take your AuthGuard class which has the resolver.
{path: 'dashboard' , component: DashboardComponent, canActivate: [AuthGuard]}
Related
How can I get my service variable in routing file.
I made a UserService which has a variable user and I want to access that variable in my routing file.
Below is my approach which didn't work:
In routing file, I wrote:
const steps = userService.user.onboardingStatus; //shows error ---> *cannot find name userService*
const routes: Routes = [
{
path: 'welcome',
component: WelcomeComponent,
},
{
path: 'product-selection',
component: ProductSelectionComponent,
canActivate: [ClientRoutesGuard],
data: {
isStepAccessible: steps.['welcome'].status, //will return true or false
},
},
]
#NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class ClientRoutingModule {
constructor(public userService: UserService) {}
}
Thanks in advance!
If you want the user to be able to go to the 'product-selection' route only when steps['welcome'].status is true, you should create a guard for it. For example:
export class ProductSelectionGuard implements CanActivate {
constructor(private userService: UserService) {}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
return this.userService.user.steps['welcome'].status;
}
}
Then include your guard in the canActivate array for your route:
// ... other routes
{
path: 'product-selection',
component: ProductSelectionComponent,
canActivate: [ClientRoutesGuard, ProductSelectionGuard],
// data: {
// isStepAccessible: steps.['welcome'].status, //will return true or false
// },
},
im starting to work with Angular, and im trying to create a simple route guard, to redirect user to login page, if my service return unauthorized.
To do that i created this route schema ->
const routes: Routes = [
{
path: '',
component: LoggedComponent,
children: [
{path: '', component: HomeComponent}
],
canActivate: [RouteGuard]
},
{
path: '',
component: AuthComponent,
children: [
{path: '', redirectTo: '/login', pathMatch: 'full'},
{path: 'login', component: LoginComponent},
{path: 'signin', component: SigninComponent}
]
},
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
And this is my guard service ->
PS: Im setting a default value false.
import {Subject, Observable} from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class RouteGuard implements CanActivate {
authorized: Subject<boolean> = new Subject();
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot): boolean {
this.setObservable(false)
return false;
}
getObservable(): Observable<boolean> {
console.log(`starts to observe`)
return this.authorized.asObservable();
}
setObservable(newState: boolean) {
console.log(`new observable state: ${newState}`)
this.authorized.next(newState)
}
}
Ok, since the value is returning false as default, I expect the route to be automatically redirected to the AuthComponent, because Auth is the second option at my routes[]. Right?
So...
At the AuthComponent i stated to observe the authorized status:
import {RouteGuard} from '#acn-collections-ws/shared';
#Component({
selector: 'acn-collections-ws-auth',
templateUrl: './auth.component.html',
styleUrls: ['./auth.component.scss']
})
export class AuthComponent implements OnInit {
constructor(private guard: RouteGuard, router: Router) {
console.log('im here');
this.guard.getObservable().subscribe(authorized => {
})
}
ngOnInit(): void {
}
}
But AuthComponent dosent load. it seems that when the canActivate parameter returns false, it does not go to the AuthComponent, it does not load anything. When the authorized (canActivate) returns true, it runs normally. Has anyone had a similar problem and can help me?
This is how I do it when authenticating using Firebase:
export class GuardGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router){}
async canActivate() {
const user = await this.authService.isLogged();
if(user){
return true;
}
else {
this.router.navigate(['login']);
return false;
}
}
}
If the user's logged return true so it loads the requested route if not redirects to the login route and return false.
And this is the routing:
import { NgModule } from '#angular/core';
import { Routes, RouterModule } from '#angular/router';
import { LoginComponent } from './modules/login/pages/login/login.component';
import { SignupComponent } from './modules/login/pages/signup/signup.component';
import { HeroComponent } from './shared/components/hero/hero.component';
import { NotFoundComponent } from './shared/components/not-found/not-found.component';
import { GuardGuard } from './shared/guards/guard.guard';
const routes: Routes = [
{ path: 'home', component: HeroComponent },
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: 'signup', component: SignupComponent },
{ path: 'login', component: LoginComponent },
{ path: 'tasks', loadChildren: ()=> import('./modules/task/task.module').then(m => m.TaskModule), canActivate: [GuardGuard] },
{ path: 'profile', loadChildren: ()=> import('./modules/user/user.module').then(m => m.UserModule), canActivate: [GuardGuard] },
{ path: '**', component: NotFoundComponent }
];
I am using the same component for my router, on the first click the component affected, but on the next click the component still in the first state.
Here is the script for changing the route
<a [routerLink]="['react/1']">link 1</a>
<a [routerLink]="['react/2']">link 2</a>
Here is my router module
panel-routing.module.ts
import { NgModule } from '#angular/core';
import { Routes, RouterModule } from '#angular/router'
import { PanelCoursesComponent } from 'src/app/components/panel-courses/panel-courses.component';
import { PanelHomeComponent } from 'src/app/components/panel-home/panel-home.component';
import { PanelIntroComponent } from 'src/app/components/panel-intro/panel-intro.component';
const routes: Routes = [
{ path: '', component: PanelHomeComponent },
{ path: 'react', component: PanelIntroComponent },
{ path: 'react/:no', component: PanelCoursesComponent } //the target
]
#NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class PanelRoutingModule { }
panel-course.component.ts
import { Component, OnInit } from '#angular/core';
import { Router, ActivatedRoute } from '#angular/router'
#Component({
selector: 'app-panel-courses',
templateUrl: './panel-courses.component.html',
styleUrls: ['./panel-courses.component.scss']
})
export class PanelCoursesComponent implements OnInit {
url!: any
constructor(private route: ActivatedRoute, private router: Router) {
console.log('route')
}
ngOnInit(): void {
this.url = this.router.url
console.log(this.route.snapshot.params) //the test script
}
}
On the PanelCourseComponent I try to console log the params, but that's only executed one time on the first click.
Am I missing something?
You can use this.route.params.subscribe method for this case
Here is the example
ngOnInit(): void {
this.route.params.subscribe(params => {
console.log(params) // It will be executed whenever you click the link
})
}
by default pathMatch is set to 'prefix'. so paths will be matched against your current location and the first one witch "matches" will render its component. to make your paths match only "exact" match add pathMatch: 'full' for your routes
const routes: Routes = [
{ path: '', component: PanelHomeComponent, pathMatch: 'full' },
{ path: 'react', component: PanelIntroComponent, pathMatch: 'full' },
{ path: 'react/:no', component: PanelCoursesComponent } //the target
]
I have the following routes in the application. the problem here is if I navigate to say getEmp-by-id or page-not-found and hit refresh, then application is landing on app-home,. But I want it to stay on the same page where refresh is hit.I am not implementing any RouteGuards, simple navigations. Is there a way I can acheive this.
const appRoutes: Routes = [
{path: '', component: HomeComponent, children: [
{path: 'app-home', component: AppHomeComponent, resolve: {ApphomeResolver : AppHomeResolver}},
{path: 'getEmp-by-id', component: EmpComponent},
{path: 'page-not-found', component: pageNotFoundComponent},]
},
{path: '**', redirectTo: 'page-not-found', pathMatch: 'full'}
];
export class EmpComponent implements OnInit {
constructor(private router: Router, private route: ActivatedRoute, private alertService: AlertService, private employeeService: EmployeeService) { }
ngOnInit() {}
onSubmit() {
this.employeeService.getEmployee(empId).subscribe(
(data) => {
var responseCode = JSON.parse(data).responseCode;
var responseMessage = JSON.parse(data).responseMessage
if (responseCode === 200) {
this.router.navigate(['../emp-details'], { relativeTo: this.route });
} else {
this.router.navigate(['../page-not-found'], { relativeTo: this.route });
}
}, error => {
this.router.navigate(['../page-not-found'], { relativeTo: this.route });
});
} else {
this.alertService.error("Error");
}
}
}
One way of handling page refreshes is to using hash routing. To implement this, write the following code in app.module.ts:
import { APP_BASE_HREF, LocationStrategy, HashLocationStrategy } from '#angular/common';
#NgModule({
......
providers: [
{ provide: APP_BASE_HREF, useValue: '', }
, { provide: LocationStrategy, useClass: HashLocationStrategy }
.....
]})
export class AppModule {
}
Please note that this will add # to your route.
I have this code.
Route 'new' this child route for 'users'.
Route 'users' has resolver.
This work fine.
But after success create user.
I redirect to 'users', but new user not display in list, because
DataResolver not work after redirect from child route.
How Can i fix it?
//Roiting
export const ROUTES: Routes = [
{ path: 'dashboard',
component: Dashboard,
children: [
{ path: 'users',
component: Users,
resolve: {
users: DataResolver
},
children: [
{ path: 'new', component: NewUser }
]
},
]
}
];
//Resolver
#Injectable()
export class DataResolver implements Resolve<any> {
constructor(private userService: UserService) {}
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> {
return this.userService.all(); // return users
}
}
//Component
export class NewUser {
errorMessage: string;
user: User;
constructor(public route: ActivatedRoute,
private userService: UserService,
private router: Router) {
this.user = new User();
}
onSubmit(): void {
this.userService
.createUser(this.user)
.subscribe(
user => {
this.router.navigate(['/dashboard', 'users']);
},
error => this.errorMessage = <any>error);
}
}
// USers component
export class Users implements OnInit {
users: User[];
constructor(public route: ActivatedRoute,
private router: Router) {
this.route.data.subscribe((data: any) => {
this.users = data.users;
});
}
}
Your 'Users' component should be like this, I assume you return User[] type from your web service;
export class Users {
users;
constructor(private _route: ActivatedRoute, ...){
_route.data.subscribe((wrapper: {res: any[] }) => {
this.users = <User[]>wrapper.res;
});
}
}
When your link is .../dashboard/user, it gets users via resolver and wrap it. I just call it as wrapper, you can give another name. Just know that after resolving, it wraps the response from your service.
Please let me know if you need more information.
Edit 1:
Here is how I use resolver, I hope it helps
//app.route
const appRoutes: Routes = [
{
path: '',
redirectTo: 'user',
pathMatch: 'full'
},
{
path: 'user',
loadChildren: '.../user.module#UserModule'
}
];
const rootRoutes: Routes = [
...appRoutes
];
export const appRouting = RouterModule.forRoot(rootRoutes, { useHash: true });
//user.route
const userRoutes: Routes = [
{
path: '',
component: UserComponent,
children: [
{
path: 'create',
component: UserCreateComponent
},
{
path: '',
component: UserListComponent,
resolve: {
res: UserResolver
}
}
]
}
];
export const userRouting = RouterModule.forChild(userRoutes);
//user.module
#NgModule({
imports: [
CommonModule,
ReactiveFormsModule,
userRouting,
FormsModule
],
declarations: [
UserComponent,
UserCreateComponent,
UserListComponent
],
providers: [
UserService,
UserResolver
]
})
export class UserModule { }
//user.resolver
export class UserResolver implements Resolve<any>{
constructor(private service: UserService) { }
public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> {
return this.service.get();//return all users
}
}