How to globally implement Authentication with AngularJS, Devise and UI Router? - javascript

I'm quite new to Angular so this might be a newbie question.
I'm trying to implement a simple task manager (just an exercise) with Rails as backend and Angular as frontend. So far I followed a tutorial and everything worked fine.
Now I want to globally implement Authentication. That means: When a user is not registered and logged in, she should see a splash page or the login page.
I don't want to do that in every single Angular controller, because DRY. So I figured the UI Router might be a good place. And I have a slight suspicion that maybe $httpProvider.interceptors might be useful.
This is how far I got. I can check if a user is authenticated and prevent the page from loading. But nothing more. How would I go from here? Are there any good tutorials out there I missed?
This question on Stackoverflow goes in a similar direction but doesn't solve my problem since they are not using Devise.
Thanks!
// app.js
var app = angular.module("TaskManager", ['ui.router', 'templates', 'Devise'])
.config([
'$stateProvider',
'$urlRouterProvider',
function($stateProvider, $urlRouterProvider){
$stateProvider
.state('home', {
url: '/home',
templateUrl: 'home/_home.html',
controller: 'MainCtrl',
resolve: {
projectPromise: ['projects', function(projects) {
console.log($rootScope);
return projects.getAll();
}]
}
})
.state('projects', {
url: '/projects/{id}',
templateUrl: 'projects/_projects.html',
controller: 'ProjectsCtrl',
resolve: {
project: ['$stateParams', 'projects', function($stateParams, projects) {
return projects.get($stateParams.id);
}]
}
})
.state('login', {
url: '/login',
templateUrl: 'auth/_login.html',
controller: 'AuthCtrl',
onEnter: ['$state', 'Auth', function($state, Auth) {
Auth.currentUser().then(function() {
$state.go('home');
})
}]
})
.state('register', {
url: '/register',
templateUrl: 'auth/_register.html',
controller: 'AuthCtrl',
onEnter: ['$state', 'Auth', function($state, Auth) {
Auth.currentUser().then(function() {
$state.go('home');
})
}]
});
$urlRouterProvider.otherwise('home');
}
]);
// run blocks
app.run(function($rootScope, Auth) {
// you can inject any instance here
$rootScope.$on('$stateChangeStart',
function(event, toState, toParams, fromState, fromParams){
if(!Auth.isAuthenticated()) {
// So the magic should probably happen here. But how do I implement this?
// And how do I allow users to access the /login and /register page?
event.preventDefault();
}
})
});

I wrote an article that basically answers this question (at a high level at least) called How to Set Up Authentication with AngularJS and Ruby on Rails.
If you want to check whether the user is authenticated at any particular route, you can (in Angular's standard router, although I don't know about ui-router) use resolve. Here are a couple routes from an older project of mine:
.when('/', {
templateUrl: 'views/main.html',
controller: 'MainCtrl',
isPublic: true
})
.when('/today', {
templateUrl: 'views/announcements.html',
controller: 'AnnouncementsCtrl',
resolve: {
auth: ['$auth', function($auth) {
return $auth.validateUser();
}]
}
})
The root route is public but /today requires authentication. Maybe that gives you enough to go on for ui-router as well. Side note: I should really update my article to account for ui-router (or Angular's new router) since I don't think many people use the regular old Angular router.
Edit: I remembered this thing from a different project of mine. This is CoffeeScript. Irrelevant code omitted.
"use strict"
angular.module("fooApp", [
"ngAnimate"
"ngCookies"
"ngResource"
"ngRoute"
"ngSanitize"
"ngTouch"
"ui.router"
"ng-token-auth"
])
.run ($rootScope, $auth, $location) ->
$rootScope.$on "auth:login-success", -> $location.path "/"
$rootScope.$on "auth:logout-success", -> $location.path "/sign-in"
$rootScope.$on "$stateChangeStart", (event, next) ->
$auth.validateUser().then (->
if $location.path() == "/"
$location.path "/file-uploads"
), ->
if !next.isPublic
$location.path "/sign-in"
Hopefully it's kind of self-evident what's going on there. This project DOES use ui-router, as you can tell from the $stateChangeStart.

Related

Why is $routeParams not working? Undefined

I'm triyng to build simple applications on Angular and Django rest framework.
I have a next root app:
app.js
angular
.module('sharePhotoApp', [
'ngRoute',
'sharePhotoApp.controllers',
'sharePhotoApp.services'
])
.config(['$routeProvider', function($routeProvider) {
$routeProvider
.when('/', {
templateUrl: '/',
controller: 'PostListController'
})
.when('/:username', {
templateUrl: '/user/',
controller: 'UserDetailController'
})
.otherwise({
redirectTo: '/'
});
}]);
And the next controller:
angular
.module('sharePhotoApp.controllers')
.controller('UserDetailController', UserDetailController)
UserDetailController.$inject = ['$scope','$routeParams','userService'];
function UserDetailController($scope, $routeParams, userService){
alert($routeParams.username);
$scope.user = userService.get({ username: $routeParams.username });
}
I tried to follow the next URL in browser: http://127.0.0.1:8000/user/#/admin
I suppose in this case should triggered route with username parameter.
But I've gotten undefined.
Could anyone explain where I done mistake?
"Note that the $routeParams are only updated after a route change
completes successfully. This means that you cannot rely on
$routeParams being correct in route resolve functions. Instead you can
use $route.current.params to access the new route's parameters."
https://docs.angularjs.org/api/ngRoute/service/$routeParams
Have you also installed ngRoute module?
As the description says, the route is not finished in your controller, so the params are also undefined.
$scope.$on('$routeChangeSuccess', function() {
// $routeParams should be populated here
});
should do the work.

ASP.NET 5 Routing with AngularJS Routing

I'm developing an application with ASP.NET 5 with AngularJS. I'm using basic client side HTML routing support provided by Angular. I've successfully implemented Angular Routing, the default routing in Startup class is
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
Here is my Angular Routing
app.config(['$routeProvider', '$locationProvider', function ($routeProvider, $locationProvider) {
$routeProvider.
when('/', {
templateUrl: 'static/home.html',
controller: 'AppController'
}).
when('/about', {
templateUrl: 'static/about.html',
controller: 'AppController'
}).
when('/contact', {
templateUrl: 'static/contact.html',
controller: 'AppController'
}).
when('/tutorial', {
templateUrl: 'static/tutorial.html',
controller: 'AppController'
}).
otherwise({
redirectTo: '/'
});
$locationProvider.html5Mode(true);
}]);
It all works fine, but when I create another action in the same controller and wants to redirect to that action through URL, it redirect it to the same action i.e. Index
Here is my code for HomeController
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
public IActionResult Web()
{
return View();
}
}
Routing happens in Index, but can't navigate to Web Action. What is the problem that I'm confronting. Thanks.
I think yaht the problem is that your application does not know anything about your Web method. To solve this you can:
1) use attribute routing, and
2) write direct route in your route configuration method.
Also check your web.config file for redirection in rewrite node of system.webServer section or any other redirection configurations
P.S. Be noted that if you redirects to another page your angular application will reconfigured and restarted. It takes some time. You should not use redirections ang all other page reloading methods if you want your app will be really cool. Try think in "angular" way. If you want to change part of the main content - angular will do it with routing. If you want to load some additional content and you want it looks like http://host/controller/method/submethod1/submethod2 url in browser - angular also can do it for you. You just need to place get() method inside your angular controller as the first step. In this case when angular will be resolving this controller it calls your get() method and loads data from server. So, you will have fast apllication with actual content always.
P.P.S. Try to use angular-ui-routing instead of default ng-route. ng-route is good, but it can not support subrouting well and also it has some other issues. angular-ui-routing solves all these messy things
Try this one to register your route,
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
Try This:
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
Then also try this below your config:
.controller('RootController', ['$rootScope', '$scope', '$route', '$routeParams', '$location', '$http',
function ($rootScope, $scope, $route, $routeParams, $location, $http) {
$scope.$on('$routeChangeSuccess', function (e, current, previous) {
$scope.activeViewPath = $location.path();
});
}]);
Hope this will help. =)

How to change/set the main view using Yeoman with Angular Fullstack

I created a project using Yeoman (angular-fullstack). And now I would like to know how the change/set the main view to login.html. So normally when you start the application you first get the main view where you can chose to login or register. What I want is when the application start the page starts direct on the login.html
in your app.js file located at client\app\app.js, in the angular config add the following:
$stateProvider
.run(function ($state) {
$state.go('login');
});
So it should look like:
.config(function ($stateProvider, $urlRouterProvider, $locationProvider, $httpProvider) {
$urlRouterProvider
.otherwise('/');
$locationProvider.html5Mode(true);
$httpProvider.interceptors.push('authInterceptor');
$stateProvider
.run(function ($state) {
$state.go('login');
});
})
I realize this has been out here for a while and you likely already have a good solution, but I was recently looking at this myself and see a couple options.
One, inside app.js you could add the following code snippet under $urlRouterProvider:
.when('/', '/login')
Making your full method be something like:
.config(function ($stateProvider, $urlRouterProvider, $locationProvider, $httpProvider) {
$urlRouterProvider
.when('/', '/login')
$locationProvider.html5Mode(true);
$httpProvider.interceptors.push('authInterceptor');
})
This would force anyone going to your base page url directly to login, unless they provide the full route to another page. However, if you intend to still use the main.html, you will then need to go into client/app/main/main.js and change the default route to:
.state('main', {
url: '/main',
templateUrl: 'app/main/main.html',
controller: 'MainCtrl'
});
So main is reachable by appending /main to the url.
Which brings me to option 2: Go into the main.js file and switch it's url to '/main' and go into login.js and switch it's url to '/'. Then anyone navigating to your base page automatically goes to the login screen but the url is viewed as just your domain without any sub page.
So client/app/main/main.js becomes:
.state('main', {
url: '/main',
templateUrl: 'app/main/main.html',
controller: 'MainCtrl'
});
And client/app/account/account.js now contains:
.state('login', {
url: '/',
templateUrl: 'app/account/login/login.html',
controller: 'LoginCtrl'
})

Redirect to route not working in Angular

I'm having a problem redirecting to a route which may be a result of a lack of understanding about how it's supposed to work.
Here is my routeProvider:
app.config(['$routeProvider', '$httpProvider', function ($routeProvider, $httpProvider) {
$routeProvider.
when('/', {
templateUrl: '/Content/templates/landingPage.html',
controller: 'HomeController'
})
...
A user in my app can log in at any time and when they do I want to redirect them to my home route. Eg:
function loginSuccessful() {
$location.path('/');
}
I'm trying to test this on a registration confirmation page I have which is on the following URL:
http://localhost:55841/account/register?token=da924359-130a-4a5c-9b8e-4f44267b4d6e#/
When loginSuccessful() is called, rather than redirect, it simply appends #/ to the URL. I'm expecting it to redirect to the app root (http://localhost:55841/).
Thanks in advance.

AngularJS - How to refer to a submodule controller from ui-router?

I'm struggling a bit with having submodules in an Angular 1.3.9 app. I've got a (non-working, sorry) preview at http://plnkr.co/edit/XBfTPAGRRe0CWJgjGJzc?p=preview and I think it's freaking out, in part, because I'm using Restangular.
I have the following:
angular
.module('estimate', ['ui.router', 'restangular', 'estimate.project'])
;
angular
.module('estimate.project', ['ui.router'])
.config(['$stateProvider', '$urlRouterProvider', '$locationProvider'
, function($stateProvider, $urlRouterProvider, $locationProvider) {
$stateProvider
.state('project', {
url: "/project/{id:int}",
abstract: true,
templateUrl: '/app/templates/project.html',
controller: "ProjectController as project",
resolve: { // stuff }
})
.state('project.overview', {
url: "",
templateUrl: "/app/templates/overview.html"
})
// ...
;
}])
.controller('ProjectController', ['$scope', 'ProjectService', 'myProject'
, function($scope, ProjectService, myProject) {
console.log('i made it!');
}]);
And in my template, which is served from the estimate module, I have:
<li><a ui-sref="project.overview({ id: 1 })">One</a></li>
The URL resolves correctly on the page, but clicking on it does nothing. It doesn't even throw any console errors - it just sits there. My gut tells me it has to do with how I'm referring to the controllers and/or the routes, and if they need to be prefixed or modified to work with a submodule. Any ideas on how to get it to load properly?
If this post is too scatterbrained, let me know and I'll try to clean it up.
I updated your plunker here and would say, that the most important change is - referencing sub-module in the main module:
Instead of this:
angular
.module('estimate', ['ui.router', 'restangular'])
...
angular
.module('estimate.project', ['ui.router'])
...
We have to use this, i.e. reference sub module in a parent module
angular
.module('estimate', ['ui.router', 'restangular', 'estimate.project'])
...
angular
.module('estimate.project', ['ui.router'])
...
With some few other small adjustments, that should be the way. Check it working here

Categories

Resources