I'm having a bit of trouble getting some routing stuff working in Aurelia.
When a user goes to my app, if they have previously authenticated, I want to redirect them to a landing page. If not, direct to a login page.
I have the authenticated user redirect working fine (app.js -> login.js -> setupnav.js -> landing page).
The problem I have now is:
When a user refreshes a page (http://localhost:8088/aurelia-app/#/landing), the landing route doesn't exist anymore and an error is thrown in the console (ERROR [app-router] Error: Route not found: /landing(…)). I would like to direct the user to login if a route cannot be found.
Does anybody know how I can redirect a user from a missing route to my login page?
Also any comments on how I set the routing up is welcome.
app.js
import {inject} from 'aurelia-framework';
import {Router} from 'aurelia-router';
import {FetchConfig} from 'aurelia-auth';
import {AuthorizeStep} from 'aurelia-auth';
import {AuthService} from 'aurelia-auth';
#inject(Router,FetchConfig, AuthService )
export class App {
constructor(router, fetchConfig, authService){
this.router = router;
this.fetchConfig = fetchConfig;
this.auth = authService;
}
configureRouter(config, router){
config.title = 'VDC Portal';
config.addPipelineStep('authorize', AuthorizeStep); // Add a route filter to the authorize extensibility point.
config.map([
{ route: ['','login'], name: 'login', moduleId: './login', nav: false, title:'Login' },
{ route: '', redirect: "login" },
{ route: 'setupnav', name: 'setupnav', moduleId: './setupnav', nav: false, title:'setupnav' , auth:true}
]);
this.router = router;
}
activate(){
this.fetchConfig.configure();
}
created(owningView: View, myView: View, router){
/* Fails to redirect user
if(this.auth.isAuthenticated()){
console.log("App.js ConfigureRouter: User already authenticated..");
this.router.navigate("setupnav");
}
*/
}
}
login.js
import {AuthService} from 'aurelia-auth';
import {inject} from 'aurelia-framework';
import {Router} from 'aurelia-router';
#inject(AuthService, Router)
export class Login{
constructor(auth, router){
this.auth = auth;
this.router = router;
if(this.auth.isAuthenticated()){
console.log("Login.js ConfigureRouter: User already authenticated..");
this.router.navigate("setupnav");
}
};
heading = 'Login';
email='';
password='';
login(){
console.log("Login()...");
return this.auth.login(this.email, this.password)
.then(response=>{
console.log("success logged");
console.log(response);
})
.catch(err=>{
console.log("login failure");
});
};
}
Redirecting to:
setupnav.js
import {Router} from 'aurelia-router';
import {inject} from 'aurelia-framework';
#inject(Router)
export class Setupnav{
theRouter = null;
constructor(router){
console.log("build setupnav. router:" + this.theRouter);
this.theRouter = router
};
activate()
{
this.theRouter.addRoute( { route: 'landing', name: 'landing', moduleId: 'landing', nav: true, title:'Integration Health' , auth:true});
this.theRouter.addRoute( { route: 'tools', name: 'tools', moduleId: 'tools', nav: true, title:'Integration Tools' , auth:true});
this.theRouter.refreshNavigation();
this.theRouter.navigate("landing");
}
}
To map an unknown route to a specific page, use the mapUnknownRoutes feature:
configureRouter(config, router) {
...
config.mapUnknownRoutes(instruction => {
return 'login';
});
}
That said, it might be easier to keep all auth related logic out of routing and instead use setRoot to set the appropriate root module depending on the user's auth state.
A standard main.js looks like this:
main.js
export function configure(aurelia) {
aurelia.use
.standardConfiguration()
.developmentLogging();
aurelia.start().then(a => a.setRoot());
}
You could change the logic to something like this:
main.js
export function configure(aurelia) {
aurelia.use
.standardConfiguration()
.developmentLogging();
aurelia.start().then(() => {
if (userIsAuthenticated) {
return aurelia.setRoot('app');
}
if (userPreviouslyAuthenticated) {
return aurelia.setRoot('login');
}
return aurelia.setRoot('landing');
});
}
In the example above, the app module is the only module that would configure routes. The login module would be a login page which called setRoot('app') once the user was successfully logged in. The landing page would call setRoot('login') when the user clicked the link/button.
Here's an answer to a related question that might be helpful:
https://stackoverflow.com/a/33458652/725866
Related
Im am working on an Angular 9 application that uses OneLogin for authentication purposes.
In the auth.component.ts file I have an authentication service that I use in the authentication component:
import { AuthService } from 'path/to/core/services/auth/auth.service';
import { AuthApiService } from 'path/to/core/core/services/auth/auth-api.service';
import { Component, OnInit } from '#angular/core';
import { authCodeFlowConfig } from 'path/to/config/onelogin-api/config-auth.component';
#Component({
selector: 'auth',
templateUrl: './assets/auth.component.html',
styleUrls: ['./assets/auth.component.scss']
})
export class AuthComponent implements OnInit{
constructor(private _authService: AuthService) {
}
startAuthentication() {
this._authService.startAuthentication();
}
ngOnInit(): void {
this.startAuthentication();
}
}
In auth.service.ts I have the startAuthentication() method:
startAuthentication(): Observable<any> {
const {issuer, redirectUri, clientId, responseType, scope} = authCodeFlowConfig;
const url = `someURL`;
this.redirectTo(url);
return of(false);
}
redirectTo(url: string): void {
window.location.href = url;
}
In the app.module.ts file I have this array of routes:
import { AuthService } from './core/services/auth/auth.service';
// more imports
const appRoutes: Routes = [
{
path : 'myroute',
redirectTo: 'myroute'
},
{
path: 'auth',
component: AuthComponent
}
];
In other words, I want the application to reach a certain url if login is successful and otherwise redirect to the login form.
What I want to happen is: when login is sucessful - in other words, when startAuthentication() is executed - there should be a redirect to myroute.
I tried {path: 'auth', component: AuthComponent, startAuthentication:[AuthService]} bit it fails.
What am I doing wrong?
As I don't have any further information about your StartAuthentication method, I'd say that you should inject the router service in your component and navigate using it:
import { Router } from '#angular/router';
...
constructor(
private _authService: AuthService,
private _router: Router) {}
startAuthentication() {
this._authService.startAuthentication();
this._router.navigate(['/', 'myroute']);
}
I have a module which initially redirects to
redirectTo: "/profile/bbb"
Where profile has two children :
{
path: '',
component: ProfileComponent,
children: [{
path: 'aaa',
component: AComponent
}, {
path: 'bbb',
component: BComponent
}]
}
Now - In profile's constructor I want to know what is the current route (which is profile) and what is the next route which it is goint to execute ( which is bbb).
This is the code :
profile.component.ts
constructor(private route: ActivatedRoute) {
route.children[0].url.subscribe(f => console.log(f))
}
But it shows me :
I guess I just queried the structure of the routes rather than the current active route
Question
How can I , in each path along the route , can know what is the parent route , current route , and where is it going to navigate ?
ps I don't want to do it via snapshot solution since component can be reused.
Online Demno
You can do this using ActivatedRoute.
Import it like:
import { ActivatedRoute } from '#angular/router';
Now inject it in the component:
constructor( private route: ActivatedRoute) {
console.log('Parent URL:', route.parent.url['value'][0].path);
console.log('Child URL:', route.firstChild.url['value'][0].path);
}
If you want to know complete URL for every navigation.
Then you can do this by using Router. Probably in AppComponent.
import { Router, NavigationStart, NavigationEnd } from '#angular/router';
A component will look like:
constructor(private router: Router) {
router.events.subscribe((route) => {
if (route instanceof NavigationStart) {
console.log("NavigationStart called:", route.url);
}
if (route instanceof NavigationEnd) {
console.log("NavigationEnd called:", route.url);
}
});
}
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;
}
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'])
I'm new to angular 2 and trying to create a login app which I managed okay however after checking user/pass then redirect to dashboard it reloads the app. Is there a way to not refresh the page using router.navigate?
Edit: it redirects to dashboard first then reloads the page then redirects again back to dashboard.
import { Component } from '#angular/core';
import { Router, ROUTER_DIRECTIVES } from '#angular/router';
#Component({
selector: 'login',
templateUrl: './app/login/views/login.html',
})
export class LoginComponent {
constructor(public router: Router) {}
data = {
username: "",
password: "",
};
loginAction (){
if(this.data.username=="user1" && this.data.password=="pass1"){
console.log('do redirect to dashboard');
this.router.navigate(['dashboard']);
} else {
console.log('Something is wrong with your user/password.');
}
}
}
this.router.navigate(['/dashboard']);
Did you try relativeTo
router.navigate(['dashboard'], {relativeTo: route});