automatically redirect page in angularjs - ruby-on-rails

In a angularjs project, I want when user doesn't sign in, page is automatically redirect to login page. For this, I when user want see a page, if doesn't sign in before, I send 401 from rails sever by below code:
def failure
render :status => 401, :json => { :success => false, :info => "Login Credentials Failed" }
end
and I recieve this in chrome browser:
GET http://localhost:3000/api/record.json 401 (Unauthorized) angular.min.js?body=1:81
(anonymous function) angular.min.js?body=1:81
t angular.min.js?body=1:76
f angular.min.js?body=1:74
I angular.min.js?body=1:102
I angular.min.js?body=1:102
(anonymous function) angular.min.js?body=1:103
h.$eval angular.min.js?body=1:114
h.$digest angular.min.js?body=1:111
h.$apply angular.min.js?body=1:115
t angular.min.js?body=1:75
y angular.min.js?body=1:79
x.onreadystatechange
And I have below code in angularjs controller to redirect page:
'use strict';
angular.module('app',['ngRoute', 'ngResource', 'sessionService', 'recordService'])
.config(['$httpProvider', function($httpProvider){
$httpProvider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content');
var interceptor = ['$location', '$rootScope', '$q', function($location, $rootScope, $q) {
function success(response) {
return response
};
function error(response) {
if (response.status == 401) {
$rootScope.$broadcast('event:unauthorized');
$location.path('/users/login');
return response;
};
return $q.reject(response);
};
return function(promise) {
return promise.then(success, error);
};
}];
// $httpProvider.responseInterceptors.push(interceptor);
$httpProvider.interceptors.push(interceptor);
}])
.config(['$routeProvider',function($routeProvider, $locationProvider){
$routeProvider
.when('/', {controller: 'HomeCtrl', templateUrl: '<%= asset_path('templates/index.html') %>'})
.when('/record', {controller: 'RecordCtrl', templateUrl: '<%= asset_path('templates/record/index.html') %>'})
.when('/users/login', {controller: 'UsersCtrl', templateUrl: '<%= asset_path('templates/users/login.html') %>'})
.when('/users/register', {controller: 'UsersCtrl', templateUrl: '<%= asset_path('templates/users/register.html') %>'})
.otherwise( {redirectTo: '/'});
}]);
Now when I run project and I want accept localhost:3000/#/record, I see the record page without data and page doesn't redirect to login page. How can I fix this probelm and solve it?

You can do like:
angular.module('myapp')
.factory('httpInterceptor', ['$q', '$location',function ($q, $location) {
var canceller = $q.defer();
return {
'request': function(config) {
// promise that should abort the request when resolved.
config.timeout = canceller.promise;
return config;
},
'response': function(response) {
return response;
},
'responseError': function(rejection) {
if (rejection.status === 401) {
canceller.resolve('Unauthorized');
$location.url('/user/signin');
}
if (rejection.status === 403) {
canceller.resolve('Forbidden');
$location.url('/');
}
return $q.reject(rejection);
}
};
}
])
//Http Intercpetor to check auth failures for xhr requests
.config(['$httpProvider',function($httpProvider) {
$httpProvider.interceptors.push('httpInterceptor');
}]);
than in the route
.state('user_actions', {
abstract: true,
templateUrl: 'users/views/actions.html',
resolve: {
hasaccess: function(Sessions){
return Sessions.hasAccess('users');
}
},
controller:'UserParentActionsController'
})
.state('user_actions.create', {
url: '/user/create',
templateUrl: 'users/views/create.html',
resolve: {
groups: function(Groups){
return Groups.getList();
}
},
controller:'UserCreateController'
})

Related

Service Worker w offline.html Backup Page

I can't get the offline.html page to display. I keep getting the The FetchEvent for "https://my-domain.com" resulted in a network error response: a redirected response was used for a request whose redirect mode is not "follow".
Here's the snippet of my service-worker.js which should return the offline.html when the network is unavailable.
self.addEventListener('fetch', function(event) {
if (event.request.mode === 'navigate' || (event.request.method === 'GET' && event.request.headers.get('accept').includes('text/html'))) {
if(event.request.url.includes("my-domain.com")){
console.log(event.request);
event.respondWith(
caches.match(event.request).then(function(resp) {
return resp || fetch(event.request).then(function(response) {
let responseClone = response.clone();
caches.open(CACHE_NAME).then(function(cache) {
cache.put(event.request, responseClone);
});
return response;
});
}).catch(function() {
return caches.match("/offline.html");
})
);
}
}
});
Below is the console.log of my network request (page refresh when offline)
Request {method: "GET", url: "https://my-domain.com", headers: Headers, destination: "unknown", referrer: "", …}
bodyUsed:false
cache:"no-cache"
credentials:"include"
destination:"unknown"
headers:Headers {}
integrity:""
keepalive:false
method:"GET"
mode:"navigate"
redirect:"manual"
referrer:""
referrerPolicy:"no-referrer-when-downgrade"
signal:AbortSignal {aborted: false, onabort: null}
url:"https://my-domain.com"
__proto__:Request
I got this working / found the fix. It was related to a redirected response security issue in the browser. From the Chromium Bugs Blog, Response.redirected and a new security restriction.
Solution: To avoid this failure, you have 2 options.
You can either change the install event handler to store the response generated from res.body:
self.oninstall = evt => {
evt.waitUntil(
caches.open('cache_name')
.then(cache => {
return fetch('/')
.then(response => cache.put('/', new Response(response.body));
}));
};
Or change both handlers to store the non-redirected response by setting redirect mode to ‘manual’:
self.oninstall = function (evt) {
evt.waitUntil(caches.open('cache_name').then(function (cache) {
return Promise.all(['/', '/index.html'].map(function (url) {
return fetch(new Request(url, { redirect: 'manual' })).then(function (res) {
return cache.put(url, res);
});
}));
}));
};
self.onfetch = function (evt) {
var url = new URL(evt.request.url);
if (url.pathname != '/' && url.pathname != '/index.html') return;
evt.respondWith(caches.match(evt.request, { cacheName: 'cache_name' }));
};

How to call update_password after request_reset in ng-token-auth and devise-token-auth?

I am using rails api devise-token-auth and Angular Front with ng-token-auth. I am getting problems in the Forgot Password?.
I want that after successful forgot password request, the user is allowed to login and redirected to my update_password path to change password.
The result is user gets login but the redirect url does not go to update_password path.
My redirect url becomes from: http://localhost:9000/#/auth/update_password to http://localhost:9000/?client_id=7gcm9TRnCY0BQh_q83ayPQ&config=default&expiry=&reset_password=true&token=e7Pf34gAC45GPemR6umXwQ&uid=email3%40email.com#/auth/update_password
please note that #/auth/update_password goes to end of link.
My ng-route is like below:
mainApp.config(function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'views/pages/Home.html',
controller: 'HomeCtrl'
})
.when('/auth/signup', {
templateUrl: 'views/auth/signup.html',
controller: 'authCtrl'
})
.when('/auth/signin', {
templateUrl: 'views/auth/signin.html',
controller: 'authCtrl'
})
.when('/auth/request_reset',{
templateUrl: 'views/auth/requestResetPassword.html',
controller: 'authCtrl'
})
.when('/auth/update_password',{
templateUrl: 'view/auth/updatePassword.html',
controller: 'authCtrl'
})
.otherwise({
redirectTo: '/'
});
});
My ng-token-auth settings are:
mainApp.constant('railsServer', 'http://localhost:3000/api/v1');
mainApp.constant('angServer', 'http://localhost:9000/#');
mainApp.config(function ($authProvider, angServer, railsServer) {
$authProvider.configure({
apiUrl: railsServer,
storage: 'localStorage',
confirmationSuccessUrl: 'http://localhost:9000/#/auth/update_password',
passwordResetSuccessUrl: 'http://localhost:9000/#/auth/update_password'
});
});
My authCtrl is:
var authApp = angular.module('authModule', []);
authApp.controller('authCtrl', ['$scope', '$auth', '$http', '$location', 'railsServer', '$log', function ($scope, $auth, $http, $location, railsServer, $log) {
$scope.go = function (path) {
$location.path(path);
};
var rails_server_path = railsServer;
$scope.userSignUp = function (userSignUpForm) {
$auth.submitRegistration(userSignUpForm)
.then(function (resp) {
$log.info("Welcome " + resp.data.data.email + " !");
})
.catch(function (resp) {
$log.error(resp.data.errors.full_messages[0]);
});
};
$scope.userSignIn = function (userSignInForm) {
$auth.submitLogin(userSignInForm)
.then(function (resp) {
$log.info("Welcome " + resp.email + " !");
$location.path('/#/')
})
.catch(function (resp) {
$log.error(resp.errors[0]);
});
};
$scope.userRequestReset = function(userRequestResetForm){
$auth.requestPasswordReset(userRequestResetForm)
.then(function(resp){
$log.info(resp);
})
.catch(function(resp){
$log.error(resp);
});
};
$scope.userUpdatePassword = function(userUpdatePasswordForm){
$auth.updatePassword(userUpdatePasswordForm)
.then(function(resp){
$log.info(resp);
})
.catch(function(resp){
$log.error(resp.data.errors[0]);
});
};
}]);
My devise-token-auth setting are default settings.

Devise Angular Authentication

I am trying to authenticate into devise via angular. On loading localhost, I see a POST 401 error. This issue seems to be with some devise setting, as my X-CSRF-TOKEN is being passed, but my POST requests aren't accepted.
I am able to register a user successfully, and immediately log in to that user (register also logs user in). I am then able to log out.
Here is the issue: I cannot log in to that user again.
My angular app is inside rails, so the two aren't disconnected.
What do I do? Here are relevant files:
app.js
var myApp = angular.module('myApp', ['templates','ui.router','ui.bootstrap', 'Devise']);
myApp.config([
'$httpProvider', function($httpProvider) {
return $httpProvider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content');
}
]);
myApp.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
/**
* Routes and States
*/
$stateProvider
.state('home', {
url: '/',
templateUrl: 'home.html',
controller: 'HomeCtrl'
})
.state('login', {
url: '/login',
templateUrl: 'login.html',
controller: 'AuthCtrl',
onEnter: ['$state', 'Auth', function($state, Auth) {
Auth.currentUser().then(function (){
$state.go('home');
})
}]
})
.state('register', {
url: '/register',
templateUrl: 'register.html',
controller: 'AuthCtrl',
onEnter: ['$state', 'Auth', function($state, Auth) {
Auth.currentUser().then(function (){
$state.go('home');
})
}]
});
// default fall back route
$urlRouterProvider.otherwise('/');
// enable HTML5 Mode for SEO
//$locationProvider.html5Mode(true);
});
myApp.run(function() {
return console.log('angular app running');
});
authCtrl.js
angular.module('myApp')
.controller('AuthCtrl', [
'$scope',
'$state',
'Auth',
function($scope, $state, Auth){
$scope.login = function() {
Auth.login($scope.user).then(function(){
$state.go('home');
});
};
$scope.register = function() {
Auth.register($scope.user).then(function(){
$state.go('home');
});
};
}]);
navCtrl.js
angular.module('myApp')
.controller('NavCtrl', [
'$scope',
'Auth',
function($scope, Auth){
$scope.signedIn = Auth.isAuthenticated;
$scope.logout = Auth.logout;
Auth.currentUser().then(function (user){
$scope.user = user;
});
$scope.$on('devise:new-registration', function (e, user){
$scope.user = user;
});
$scope.$on('devise:login', function (e, user){
$scope.user = user;
});
$scope.$on('devise:logout', function (e, user){
$scope.user = {};
});
}]);
applicaton_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
respond_to :json
def index
end
end
I have Devise gem installed, and using angular, angular-ui, angular-devise in Bowerfile
It looks like changing :exception to :null_session worked.. Why is this?

AngularJS Devise authorization

I'm trying to add authorization functionality to my app, sow for example when I want to go to /admin/posts/create it will redirect me to /login if I'm not already logged in.
My angular module
angular.module('flapperNews', ['ui.router', 'templates', 'ngResource', 'formly', 'formlyBootstrap', 'ngMessages', 'Devise'])
I'm using this service
angular.module('flapperNews')
.factory('authorization', [
'$state',
'Auth',
'$location',
function($state, Auth, $location) {
return {
authorize: function() {
var isAuthenticated = Auth.isAuthenticated();
if (isAuthenticated === false) {
$state.go('login');
}
}
};
}
]);
These are my routes
$stateProvider.state('home', {
url: '/',
controller: 'PostsCtrl',
templateUrl: 'posts/_posts.html'
});
$stateProvider.state('admin', {
abstract: true,
resolve: {
authorize: ['authorization', function(authorization) {
return authorization.authorize();
}]
},
template: '<div ui-view />'
});
$stateProvider.state('postsCreate', {
parent: 'admin',
url: '/admin/posts/create',
controller: 'PostsCtrl as vm',
templateUrl: 'posts/create.html'
});
Through this link I'm trying to access route where I shouldn't be able to get
<a ui-sref="postsCreate" class="btn btn-primary">Create a post</a>
I don't get any errors and I end up in section which I should be able to access.

Controller actions being executed as soon as the page loads

I'm using rails with devise, and angular with ng-file-upload.
I have two problems. first as soon as I go to the root of the website a POST request is executed to try to sign in the user with results in a 500 status code response.
It says it can't find the users_url, which makes sense because I don't have in my routes, what is weird is that request being fired.
Also using the ng-file-upload, when I load a dialog to upload a file, the dialog loads and it sends the request to upload the file directly. With no wait in between.
Why is this happening?
THis are the differents files being excuted in all this actions:
app.coffee
app = angular.module("inkorporated", ['ui.router', 'templates', 'rails', 'Devise', 'ngFileUpload', 'ngDialog'])
.config(['$stateProvider','$urlRouterProvider',
($stateProvider, $urlRouterProvider)->
$stateProvider
.state('home', {
url: '/home'
templateUrl: 'home/_home.html'
controller: 'SearchCtrl'
})
.state('login', {
url: '/login'
templateUrl: 'auth/_login.html'
controller: 'AuthCtrl'
onEnter: ['$state', 'Auth', ($state, Auth)->
Auth.currentUser().then ->
$state.go('home')
return
return
]
})
.state('register', {
url: '/register'
templateUrl: 'auth/_register.html'
controller: 'AuthCtrl'
onEnter: ['$state', 'Auth', ($state, Auth)->
Auth.currentUser().then ->
$state.go('home')
return
return
]
})
.state('user', {
url: '/user/{id}'
templateUrl: 'users/_user.html'
controller: 'UserCtrl'
})
$urlRouterProvider.otherwise('home')
return
])
navCtrl.coffee
angular.module("inkorporated").controller('NavCtrl', ['$scope', '$state', 'Auth',
($scope, $state, Auth)->
$scope.config = {
headers: {
'X-CSRF-TOKEN': $('meta[name=csrf-token]').attr("content")
}
}
$scope.signedIn = Auth.isAuthenticated
$scope.logout = Auth.logout
Auth.currentUser().then(
(user)->
if !!user.email
$scope.user = user
else
$scope.user = {}
return
)
$scope.$on('devise:new-registration',
(e, user)->
$scope.user = user
return
)
$scope.$on('devise:login',
(e, user)->
if !!user.email
$scope.user = user
else
$scope.user = {}
return
)
$scope.$on('devise:logout',
(e, user)->
$scope.user = {}
return
)
return
])
avatarCtrl.coffee
angular.module("inkorporated").controller("AvatarCtrl", ["$scope", "$stateParams", "Upload", "ngDialog"
($scope, $stateParams, Upload, ngDialog)->
$scope.$watch('avatar',
(avatar)->
$scope.upload($scope.avatar)
return
)
$scope.upload = (avatar)->
Upload.upload({
url: "users/avatar",
fields: { 'id': $stateParams.id }
file: avatar
})
.success(
(data, status, headers, config)->
$scope.closeThisDialog()
return
)
return
return
])
application.html.haml
!!!
%html{ "ng-app" => "inkorporated" }
%head
%meta{:content => "text/html; charset=UTF-8", "http-equiv" => "Content-Type"}/
%meta{ name: "viewport", content: "width=device-width, initial-scale=1" }/
%title Inkorporated
= stylesheet_link_tag 'application', media: 'all'
= javascript_include_tag 'application'
= csrf_meta_tags
%body
.container-fluid
%header{ "ng-include" => "'nav/_nav.html'" }
#page-content.container
%ui-view
From what I can see here, I think there's a very good chance that the $watch is being triggered by a model change on avatar when your dialog opens. You can pretty easily remove that watch and make that function more event driven. Looking at the ng-file-uplpad docs, it looks like there's a directive attribute to specify calling a custom function like ngf-change="myFunction()"
$scope.$watch('avatar', function(avatar) {
$scope.upload($scope.avatar);
});
For the second part of your question, I'm curious to see what code is in the SearchCtrl being called on the landing page, that POST request could easily be related to something in there

Resources