Using auth filter for navigation results in undefined routes
Bitfiddler opened this issue · 5 comments
app.ts
import { inject } from "aurelia-framework";
import { AuthenticateStep } from 'aurelia-authentication';
import { Router, RouterConfiguration } from "aurelia-router";
import {Routing} from "./common/routing";
@inject(Routing)
export class App {
routing: Routing;
constructor(routing: Routing) {
this.routing = routing;
}
configureRouter(config: RouterConfiguration, router: Router) {
config.title = 'Holdings Manager';
config.addPipelineStep('authorize', AuthenticateStep); // Add a route filter to the authorize extensibility point.
config.map([
{ route: ['', 'pending'], name: 'pending', moduleId: './views/pending', nav: true, title: 'Pending', auth: true },
{ route: ['/delivered', 'delivered'], name: 'delivered', moduleId: './views/delivered', nav: true, title: 'Delivered', auth: true },
{ route: ['/holdings', '/holdings/:clientId/:asOfDate', 'holdings'], name: 'holdings', moduleId: './views/holdings', nav: false, title: 'Holdings', auth: true },
{ route: ['/help', 'help'], name: 'help', moduleId: './views/help', nav: true, title: 'Help' },
{ route: ['/login', 'login'], name: 'login', moduleId: './views/login', nav: false, title: 'Login' }
]);
this.routing.router = router;
}
isAuthenticated() {
return this.routing.auth.isAuthenticated();
}
// use authService.logout to delete stored data
// set expiredRedirect in your settings to automatically redirect
logout() {
this.routing.auth.logout();
this.routing.gotoApp('src/UnAuthorized');
}
}
app.html
<template>
<div class="navbar navbar-inverse" role="navigation">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="#">Holdings Manager</a>
<ul class="nav navbar-nav">
<li repeat.for="row of routing.router.navigation | authFilter: isAuthenticated() & signal: 'authentication-change'" class="${row.isActive ? 'active' : ''}">
<a href.bind="row.href">
${row.title}
<i if.bind="row.settings.icon" class="fa fa-${row.settings.icon}"></i>
</a>
</li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li><a href="" click.delegate="logout()">Logout</a></li>
</ul>
</div>
</div>
</div>
<div class="container">
<router-view></router-view>
</div>
</template>
login.ts
import { AuthService } from 'aurelia-authentication';
import { inject } from 'aurelia-framework';
import { DialogService } from 'aurelia-dialog';
import { InfoDialog } from "../components/modal/infoDialog";
import { Routing } from "../common/routing";
@inject(Routing, AuthService, DialogService)
export class Login {
heading: string;
auth: AuthService;
userName: string;
password: string;
routing: Routing;
dialogService: DialogService;
constructor(routing: Routing, authService, dialogService) {
this.heading = 'Login';
this.routing = routing
this.auth = authService;
this.dialogService = dialogService;
}
activate(urlParams, routeConfig, navInstr) {
if (this.auth.isAuthenticated()) {
this.routing.gotoApp('src/app');
}
}
login() {
var credentials = {
username: this.userName,
password: this.password,
grant_type: "password"
};
this.auth.login(credentials,
{ headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }
).catch(e => {
this.dialogService.open({
viewModel: InfoDialog,
model: ExceptionHelpers.exceptionToString(e)
});
});
this.routing.gotoApp('src/app');
};
}
routing.ts
import { AppRouter, Router } from "aurelia-router";
import { Aurelia, inject, transient } from "aurelia-framework";
import { AuthService } from 'aurelia-authentication';
@transient()
@inject(Aurelia, AppRouter, Router, AuthService)
export class Routing {
aurelia: Aurelia;
auth: AuthService;
router: Router;
appRouter: AppRouter;
constructor(aurelia: Aurelia, appRouter: AppRouter, router: Router, authService: AuthService) {
this.aurelia = aurelia;
this.router = router;
this.appRouter = appRouter;
this.auth = authService;
}
gotoApp(targetApp: string) {
this.router.navigate('/', { replace: true, trigger: false });
this.router.reset();
this.appRouter.deactivate();
this.aurelia.setRoot(targetApp);
}
}
When I log in, the authentication signal happens and re-activates all the routes on the app.ts (the authentication change happens after the routes get loaded), but all the route hrefs are undefined until I refresh the page by hand.
turns out the reason I thought I needed to jump through all the above hoops is because there is a typo in the documentation. When you are setting up your routes in your app.ts and app.html, you need to include parentheses in the authFilter.
In the docs it gives the example:
repeat.for="row of routing.router.navigation | authFilter: isAuthenticated & signal: 'authentication-change'"
It should be with parens:
repeat.for="row of routing.router.navigation | authFilter: isAuthenticated() & signal: 'authentication-change'"
and then you don't need to have 2 different app roots:
app.ts
<template>
<div class="navbar navbar-inverse" role="navigation">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="#">Holdings Manager</a>
<ul class="nav navbar-nav">
<li repeat.for="row of routing.router.navigation | authFilter: isAuthenticated() & signal: 'authentication-change'" class="${row.isActive ? 'active' : ''}">
<a href.bind="row.href">
${row.title}
<i if.bind="row.settings.icon" class="fa fa-${row.settings.icon}"></i>
</a>
</li>
</ul>
<ul if.bind="!authenticated" class="nav navbar-nav navbar-right">
<li><a href="/#/login" >Login</a></li>
</ul>
<ul if.bind="authenticated" class="nav navbar-nav navbar-right">
<li><a href="" click.delegate="logout()">Logout</a></li>
</ul>
</div>
</div>
</div>
<div class="container">
<router-view></router-view>
</div>
</template>
app.html
import { inject, computedFrom } from "aurelia-framework";
import { AuthenticateStep } from 'aurelia-authentication';
import { Router, RouterConfiguration } from "aurelia-router";
import {Routing} from "./common/routing";
@inject(Routing)
export class App {
routing: Routing;
constructor(routing: Routing) {
this.routing = routing;
}
configureRouter(config: RouterConfiguration, router: Router) {
config.title = 'Holdings Manager';
config.addPipelineStep('authorize', AuthenticateStep); // Add a route filter to the authorize extensibility point.
config.map([
{ route: ['/pending', 'pending'], name: 'pending', moduleId: './views/pending', nav: true, title: 'Pending', auth: true },
{ route: ['/delivered', 'delivered'], name: 'delivered', moduleId: './views/delivered', nav: true, title: 'Delivered', auth: true },
{ route: ['/holdings', '/holdings/:clientId/:asOfDate', 'holdings'], name: 'holdings', moduleId: './views/holdings', nav: false, title: 'Holdings', auth: true },
{ route: ['/help', 'help'], name: 'help', moduleId: './views/help', nav: true, title: 'Help' },
{ route: ['', '/login', 'login'], name: 'login', moduleId: './views/login', nav: false, title: 'Login' }
]);
this.routing.router = router;
}
isAuthenticated() {
return this.routing.auth.isAuthenticated();
}
// make a getter to get the authentication status.
// use computedFrom to avoid dirty checking
@computedFrom('routing.auth.authenticated')
get authenticated() {
return this.routing.auth.authenticated;
}
// use authService.logout to delete stored data
// set expiredRedirect in your settings to automatically redirect
logout() {
this.routing.auth.logout();
this.routing.router.navigate('login');
}
}
@Bitfiddler Hey, thanks!
It's really simple to PR for the docs, would you like to submit one? If not, it's alright, I can do it.
I'm not to familiar with github, when I click on 'edit this page' it instructs me to fork the repo. Off the top of your head do you know a resource that goes over the process of how you would go about editing the page? If not I can look it up, or you can find the docs I was referring to at:
https://aurelia-authentication.spoonx.org/Quick%20start.html#
Under the header "Making the Aurelia Router authentication aware".
@Bitfiddler All the more reason for you to do it, could be educational :) It needs a fork because you don't have permissions to edit our repository (which is good, otherwise the world could just mess it up). After making the changes, it creates a PR that then can be merged by us (most likely me).
Or you can fork, clone, make a branch, edit, push, PR (the regular flow).
If you don't feel like learning how to do this stuff that's okay, too. Just let me know and I'll do it :)
Sure, I'll give it a try on the weekend. Something I should know how to do by now anyway (just never got around to it) ;-)