Angular UI Router and server side redirecting - javascript

I'm building a small Meteor app and I've stumbled upon a minor setback.
I was using Iron:Router and Angular UI Router which led to some difficulties. I had to remove the Iron:Router to resolve them and by doing that I lost the benefit of redirecting to an URL on the server side. How I used to redirect and process using the Iron:Router:
Router.route('/payment/:invoice_no/:amount/:userId', {
where: 'server',
action: function() {
var amount = parseInt(this.params.amount);
var url = generate_URL_for_payment_authorization(this.params.invoice_no,this.params.amount,this.params.userId);
if (url == null) {
this.response.end("error");
}
this.response.writeHead(301, { 'Location': url});
this.response.end();
}
});
How I rewrote the previous code using the Angular UI Router:
.state('premiumPayment', {
url: '/payment/:invoice_no/:amount/:userId',
controller: function($scope, $stateParams, $http) {
var invoice_no = $stateParams.invoice_no;
var amount = $stateParams.amount;
var userId = $stateParams.userId;
Meteor.call('testingFunction', invoice_no, amount, userId, (error) => {
if (error) {
alert(error);
}
else {
console.log('Going to PayPal screen!');
}
});
}
})
And the testingFunction. I would like to know how do I redirect once I got the URL?
testingFunction: function (invoice_no, amount, userId) {
console.log(invoice_no);
console.log(amount);
console.log(userId);
var url = "";
if (Meteor.isServer) {
url = generate_URL_for_payment_authorization(invoice_no,amount,userId);
console.log("Going to this URL now: " + url);
//HOW DO I REDIRECT TO THE URL HERE???
}
}
So basically what I'm asking, how do I navigate to that URL which I get in the testingFunction function? I can't use Iron:Router because I'll get some unwanted behaviour back into my app.

Related

About how the value is returned using app.set() and app.get()

I am releasing access to pages using connect-roles and loopback but I have a pertinent question about how I can collect the customer's role and through the connect-roles to read the session and respond to a route.
Example, when the client logs in I load a string containing the client's role and access it in a function that controls access to pages.
I have this doubt because I'm finalizing a large scale service that usually there are multiple client sessions that are accessed instantly using a same storage and check function.
It would be efficient to store the customer's role using app.set() and app.get()?
app.get('/session-details', function (req, res) {
var AccessToken = app.models.AccessToken;
AccessToken.findForRequest(req, {}, function (aux, accesstoken) {
// console.log(aux, accesstoken);
if (accesstoken == undefined) {
res.status(401);
res.send({
'Error': 'Unauthorized',
'Message': 'You need to be authenticated to access this endpoint'
});
} else {
var UserModel = app.models.user;
UserModel.findById(accesstoken.userId, function (err, user) {
// console.log(user);
res.status(200);
res.json(user);
// storage employee role
app.set('employeeRole', user.accessLevel);
});
}
});
});
Until that moment everything happens as desired I collect the string loaded with the role of the client and soon after I create a connect-roles function to validate all this.
var dsConfig = require('../datasources.json');
var path = require('path');
module.exports = function (app) {
var User = app.models.user;
var ConnectRoles = require('connect-roles');
const employeeFunction = 'Developer';
var user = new ConnectRoles({
failureHandler: function (req, res, action) {
// optional function to customise code that runs when
// user fails authorisation
var accept = req.headers.accept || '';
res.status(403);
if (~accept.indexOf('ejs')) {
res.send('Access Denied - You don\'t have permission to: ' + action);
} else {
res.render('access-denied', {action: action});
// here
console.log(app.get('employeeRole'));
}
}
});
user.use('authorize access private page', function (req) {
if (employeeFunction === 'Manager') {
return true;
}
});
app.get('/private/page', user.can('authorize access private page'), function (req, res) {
res.render('channel-new');
});
app.use(user.middleware());
};
Look especially at this moment, when I use the
console.log(app.get('employeeRole')); will not I have problems with simultaneous connections?
app.get('/private/page', user.can('authorize access private page'), function (req, res) {
res.render('channel-new');
});
Example client x and y connect at the same time and use the same function to store data about your session?
Being more specific when I print the string in the console.log(app.get('employeeRole')); if correct my doubt, that I have no problem with simultaneous connections I will load a new variable var employeeFunction = app.get('employeeRole'); so yes my function can use the object containing the role of my client in if (employeeFunction === 'Any Role') if the role that is loaded in the string contain the required role the route it frees the page otherwise it uses the callback of failureHandler.
My test environment is limited to this type of test so I hope you help me on this xD
Instead of using app.set you can create a session map(like hashmaps). I have integrated the same in one of my projects and it is working flawlessly. Below is the code for it and how you can access it:
hashmap.js
var hashmapSession = {};
exports.auth = auth = {
set : function(key, value){
hashmapSession[key] = value;
},
get : function(key){
return hashmapSession[key];
},
delete : function(key){
delete hashmapSession[key];
},
all : function(){
return hashmapSession;
}
};
app.js
var hashmap = require('./hashmap');
var testObj = { id : 1, name : "john doe" };
hashmap.auth.set('employeeRole', testObj);
hashmap.auth.get('employeeRole');
hashmap.auth.all();
hashmap.auth.delete('employeeRole');

Angularjs login authentication issues

I am new using Angularjs and I'm building a login-page using AngularJS through REST API. I'm facing an issue when I am trying to submit my form. I browsed through so many web-site and links, but I din't got proper answer. Please don't tell me to google it, because I already have so many blue links. If you know anything , please correct me and if you have any working example share it .
AngularJS :
var app = angular.module('logapp',['toastr','ngRoute']);
app.factory('Auth', function($http){
var service = {};
service.login = function(username,password) {
$http.post('http://localhost:3000/loginfo',
{
username : username,
password : password
})
.then(
function successCallback(response){
console.log(response.data);
});
};
service.isAuthenticated = function() {
return {
isAuthenticated : false,
}
};
return service;
});
app.controller('credientials', function($scope,$http,Auth) {
$scope.isAuthenticated = false;
$scope.userCred = {
username: '',
password: ''
}
/*-----Form Submition-----*/
$scope.log = function(userCred){
Auth.login(userCred, function(result) {
console.log(Auth);
if (result === true) {
console.log('success');
} else {
$scope.Error = response.message;
}
});
};
First things first, this part
Auth.login(userCred, function(result) {
is Wrong. Your service.login takes 2 parameters. And they are username and password. It does not take any callback.
Right way to do it is like this
Auth.login(userCred.username, userCred.password)
.then(function(result){
console.log(result);
})
.catch(function(err){
console.error(err);
});
Which goes without saying, you refactor your service.login as follows
service.login = function(username,password) {
return $http.post('http://localhost:3000/loginfo',{
username : username,
password : password
})
}

AngularJS Firebase logout user not working

My logout function, linked to a logout button is:
$scope.logoutUser = function() {
var ref = new Firebase("https://buzzmovieionic.firebaseio.com");
ref.unauth();
console.log(ref.getAuth);
$state.transitionTo('login');
}
When I click logout, it prints this to the console:
function (){x("Firebase.getAuth",0,0,arguments.length);return this.k.P.we()}
I am checking for authData in my other controller with:
CONTROLLER:
.controller('SearchCtrl',
function ($scope, $http, Movie, $state, UsersRef, AuthData, $timeout) {
$scope.$on('$ionicView.enter', function () {
if (!AuthData) {
console.log("Auth data null!");
swal("Unauthorized", "You are not logged in", "error");
$state.transitionTo('login');
} else {
console.log("Auth data found: " + AuthData);
//do stuff
}
});
})
FACTORY:
.factory("AuthData", [
function () {
var ref = new Firebase("https://buzzmovieionic.firebaseio.com");
var data = null;
ref.onAuth(function (authData) {
if (authData) {
data = authData;
}
});
return data;
}
])
If I logout, then go back to the page linked to SearchCtrl by changing the URL, it still says it found the authData.
However, if I try and go to the search page the FIRST time I open the app, before anybody has logged in, it gives me the right error message and exits out to the login page.
How can I ensure the user can't go back into the app after logging out?
Welcome to async programming 101.
Firebase's onAuth methods listens for changes on auth state. When the auth state changes, the callback method you provide is invoked. But while it's waiting for auth state changes, your other code continues to run.
It most easy to see this if you add some log statements to your code:
.factory("AuthData", [
function () {
var ref = new Firebase("https://buzzmovieionic.firebaseio.com");
var data = null;
console.log('before onAuth');
ref.onAuth(function (authData) {
console.log('in callback');
if (authData) {
data = authData;
}
});
console.log('after onAuth');
return data;
}
])
The output is going to be:
before onAuth
after onAuth
in callback
Which is likely not what you expected when you wrote this code.
The simplest way to fix this in your code is to use the synchronous ref.getAuth() method:
.factory("AuthData", [
function () {
var ref = new Firebase("https://buzzmovieionic.firebaseio.com");
return ref.getAuth();
}
])
But you're going to run into this asynchronicity problem quite often. I highly recommend using and studying AngularFire instead of reinventing the wheel.
You are never cleaning data inside AuthData so it will always have data after the first guy logs in. I'm not familiar with Firebase but you need something like this in your AuthData factory:
.factory("AuthData", [
function () {
var ref = new Firebase("https://buzzmovieionic.firebaseio.com");
var data = null;
ref.onAuth(function (authData) {
if (authData) {
data = authData;
}
else
data = null;
});
return data;
}
])

Not setting additional header parameter in angularjs XHR request without mannual refresh

I am trying to send extra header in XHR request (init with $resource).Following is my config
var app = angular.module('app',['angularMoment']).
run(function ($rootScope,$location,$route, $timeout, $http) {
var token = localStorage.getItem("userToken");
$http.defaults.headers.common.token = token;
}
I am changing hash params (eg. after login process) to navigate in app. So when I am sending any XHR request after login process (wihout mannual reload), it's sending token (request header) as NULL. But when I reload my page manually it's working fine (i.e sending token as header). Also I tried with $route.reload() but it's not working.
Please suggest how can I get rid of this issue.
Thanks
EDIT :
After trying with follwing code :
app.factory('tokenInterceptorService', ['$q', '$location', function ($q, $location) {
var tokenInterceptor = {};
var request = function (config) {
config.headers = config.headers || {};
var token = localStorage.getItem("userToken");
config.headers.token = token;
return config;
}
// if response errors with 401 redirect to lgoin
var response = function (rejection) {
if (rejection.status === 401) {
$location.path('/');
}
return $q.reject(rejection);
}
tokenInterceptor.request = request;
tokenInterceptor.response = response;
return tokenInterceptor;
}]);
app.config(function ($httpProvider) {
$httpProvider.interceptors.push('tokenInterceptorService');
});
app.run(function ($rootScope, $location,$route, $timeout, $http) {
$rootScope.config = {};
$rootScope.config.app_url = $location.url();
$rootScope.config.app_path = $location.path();
$rootScope.layout = {};
$rootScope.layout.loading = false;
$rootScope.$on('$routeChangeStart', function () {
//need to validate
console.log($rootScope.isValidated + "app");
//show loading
$timeout(function(){
$rootScope.layout.loading = true;
});
});
$rootScope.$on('$routeChangeSuccess', function () {
//hide loading
$timeout(function(){
$rootScope.layout.loading = false;
}, 200);
});
$rootScope.$on('$routeChangeError', function () {
alert('Something went wrong. Please refresh.');
$rootScope.layout.loading = false;
});
})
It stop rendring the views in application with ".run" and trapping in $rootScope.$on('$routeChangeError', and giving the error Error: [$rootScope:inprog] $digest already in progress.
Since if I understand correctly your user token is always taken from localstorage, you can setup a watch on that localStorage key in your run function (Demo plunker for working with Localstorage in angular: http://plnkr.co/edit/7hP13JAjPybxkRuMZLZ0?p=preview )
angular.module('app',[]).run(['$rootScope', '$http', function($root, $http) {
$root.$watch(function() {
return localStorage.getItem('userToken');
}, function(userToken) {
$http.defaults.headers.common.token = userToken;
});
});
This should solve your problems without any interceptors etc.
However I'd actually recommend using http interceptor as calls to localStorage are slow, or setting the defaults where you actually set the user token after login or logout (save it also on a scope variable, and initialize it in the run part like you do now).
You need to set up an interceptor that alters every request sent to the server. You can find out more form the docs here, but essentially you need to set up a factory service on your app to add the token header like so:
app.factory('tokenInterceptorService', ['$q', '$location', 'localStorage', function ($q, $location, localStorage) {
var tokenInterceptor = {};
var request = function (config) {
config.headers = config.headers || {};
var token = localStorage.getItem("userToken");
if (token) {
config.headers.token = token;
}
return config;
}
// if response errors with 401 redirect to lgoin
var response = function (rejection) {
if (rejection.status === 401) {
$location.path('/login');
}
return $q.reject(rejection);
}
tokenInterceptor.request = request;
tokenInterceptor.response = response;
return tokenInterceptor;
}]);
and then register it during the config stage with:
app.config(function ($httpProvider) {
$httpProvider.interceptors.push('tokenInterceptorService');
});
module.run executes well before anything else in the app (but after module.config). Would the localStorage have been set by then? I think that is happening later, which is why you see this value after reloading the page.
An interceptor would be the way to go.
How are you setting the value in localStorage?
Fiddle

tell protractor to block on asynchronous call

I'm adding a fresh, angular client-tier to a legacy app. Upon login the legacy up redirects to a 'home' url. The url contains a session id which I need to grab and use (in the url) for any subsequent gets/posts. After login I call:
browser.getCurrentUrl()
and then use a regex to extract the session id. I store the session id away and use it for later gets/posts.
The problem is though that browser.getCurrentUrl() returns a promise and all my tests run before I can get the session id back. How can I make protractor wait for the browser.getCurrentUrl() to resolve.
Specifically below where I have the code:
var sessionId = loginPage.login('testuser#example.com', 'testuser');
homePage = new HomePage(sessionId);
I really need all code to block on loginPage.login() so I'll have a defined session id. My home page tests and any other page tests will need the session id to run properly.
How can I achieve this in protractor?
Thanks!
The relevant parts of my code looks like this...
home.spec.js:
describe('home page tests', function() {
var loginPage = new LoginPage();
var homePage;
// get sessionId from login and create a new HomePage object from it
beforeEach(function() {
var sessionId = loginPage.login('testuser#example.com', 'testuser');
homePage = new HomePage(sessionId);
homePage.get();
});
describe('main elements of home page test', function() {
it('page has correct username as part of user menu', function() {
expect(homePage.getUsername()).toEqual('testuser#example.com');
});
});
});
login.po.js:
function LoginPage {
// ...snip...
this.login = function(username, password) {
return this.get()
.then(function() {
this.username.sendKeys(username);
this.password.sendKeys(password);
this.loginButton.click();
})
.then(function() {
return browser.getCurrentUrl().then(function(url) {
var groups = sessionIdRegex.exec(url);
// return the extracted session id or null if there is none
if (groups !== null) {
return sessionIdRegex.exec(url)[2];
} else {
return null;
}
});
});
};
}
home.po.js:
function HomePage(sessionId) {
this.username = element(by.binding('username'));
this.getUsername = function() {
return this.username.getText();
}
this.get = function() {
return browser.get(browser.baseUrl + sessionId + '#/home');
};
};
module.exports = HomePage;
The simplest could be to use expect:
Jasmine expectations are also adapted to understand promises. That's why the line
`expect(name.getText()).toEqual('Jane Doe');
works - this code actually adds an expectation task to the control flow, which will run after the other tasks.
login.po.js:
function LoginPage {
this.login = function(username, password) {
return this.get()
.then(function() {
this.username.sendKeys(username);
this.password.sendKeys(password);
this.loginButton.click();
})
.then(function() {
return browser.getCurrentUrl().then(function(url) {
var groups = sessionIdRegex.exec(url);
// return the extracted session id or null if there is none
if (groups !== null) {
return sessionIdRegex.exec(url)[2];
} else {
return null;
}
});
});
};
expect(this.login).not.toBeUndefined();
}

Categories