(function(angular){
"use strict";
console.clear();
angular
.module("Test", ["ngRoute"])
.config(function($routeProvider){
$routeProvider
.when("/", {
templateUrl: "public",
controller: "GeneralController",
controllerAs: "context"
})
.when("/login", {
templateUrl: "login",
controller: "LoginController",
controllerAs: "context"
})
.when("/private", {
templateUrl: "private",
controller: "GeneralController",
controllerAs: "context",
authorize: true
})
.otherwise({
redirectTo: "/"
});
})
.run(function($rootScope, $location){
// logging helper
function getPath(route) {
if (!!route && typeof(route.originalPath) === "string")
return "'" + route.originalPath + "'";
return "[unknown route, using otherwise]";
}
$rootScope.$on("$routeChangeStart", function(evt, to, from){
console.log("Route change start from", getPath(from), "to", getPath(to));
if (to.authorize === true)
{
to.resolve = to.resolve || {};
if (!to.resolve.authorizationResolver)
{
to.resolve.authorizationResolver = function(authService) {
console.log("Resolving authorization.");
return authService.authorize();
};
}
}
});
$rootScope.$on("$routeChangeError", function(evt, to, from, error){
console.log("Route change ERROR from", getPath(from), "to", getPath(to), error);
if (error instanceof AuthorizationError)
{
console.log("Redirecting to login");
$location.path("/login").search("returnTo", to.originalPath);
}
});
// NOT needed in authorization / logging purposes only
$rootScope.$on("$routeChangeSuccess", function(evt, to, from){
console.log("Route change success from", getPath(from), "to", getPath(to));
});
})
.controller("LoginController", function($location, authService){
console.log("Instantiating login controller");
this.login = login(true, $location.search().returnTo);
this.logout = login(false, "/");
// DRY helper
function login(doWhat, whereTo){
return function() {
console.log("Authentication set to", doWhat, "Redirecting to", whereTo);
authService.authenticated = doWhat;
$location.path(whereTo && whereTo || "/");
};
}
})
.controller("GeneralController", function($scope){
console.log("Instantiating general controller");
$scope.console = console;
})
.service("authService", function($q, $timeout){
console.log("Instantiating auth service");
var self = this;
this.authenticated = false;
this.authorize = function() {
return this
.getInfo()
.then(function(info){
console.log("Checking authentication status", info);
if (info.authenticated === true)
return true;
// anonymous
throw new AuthorizationError();
});
};
this.getInfo = function() {
console.log("Acquiring authentication info");
return $timeout(function(){
console.log("Authorization info acquired");
return self;
}, 1000);
};
});
// Custom error type
function AuthorizationError(description) {
this.message = "Forbidden";
this.description = description || "User authentication required.";
}
AuthorizationError.prototype = Object.create(Error.prototype);
AuthorizationError.prototype.constructor = AuthorizationError;
})(angular);
<!DOCTYPE html>
<html ng-app="Test">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js@1.4.x" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.6/angular.min.js" data-semver="1.4.6"></script>
<script data-require="angular-route@*" data-semver="1.4.3" src="https://code.angularjs.org/1.4.3/angular-route.js"></script>
<script src="app.js"></script>
</head>
<body>
<h3>Angular routing authorization implementation</h3>
<p>
Home and login are public views and load immediately because they don't have
any promises to resolve (set by <code>route.resolve</code>).
Authorized view is only accessible after user is authenticated.
Authorization resolves in 1 second.
</p>
<p>
Anonymous users are <strong>auto-redirected to login</strong> when trying to
access Authorized view. After login they're auto-redirected back to where they
were before redirection to login view. If users manually accesses login view
(clicking Login) they're redirected to home after login/logout.
.
</p>
<p class="important">Open <strong>development console</strong> to observe execution log.</p>
<a href="#/home">Home</a>
|
<a href="#/private">Authorized</a>
|
<a href="#/login">Login</a>
<section>
<ng-view></ng-view>
</section>
<script type="text/ng-template" id="login">
<h1>Login view</h1>
<p>This is a publicly accessible login view</p>
<p>
<a href="" ng-click="context.login()">click here</a> to auto-authenticate
</p>
<p>
<a href="" ng-click="context.logout()">logout</a> to prevent authorized view access.
</p>
</script>
<script type="text/ng-template" id="public">
<h1>Public view</h1>
<p>This is a publicly accessible view</p>
</script>
<script type="text/ng-template" id="private">
<h1>Authorized view</h1>
<p>This is an authorized view</p>
{{console.log("Rendering private view")}}
</script>
</body>
</html>
/* Put your css in here */
body {
font-family: Segoe UI;
}
section {
margin-top: 2em;
border: 1px solid #ccc;
padding: 0 2em 1em;
}
.important {
color: #e00;
}