passport.authenticate() is not being executed via $http.get - javascript

I have the following angular controller
angular.module('app').controller('mvNavBarLoginCtrl', function($scope, $http){
$scope.login = function(){
$http.get('/auth/twitter').then(function(response){
if(response.data.success)
{
alert(response);
}
});
}
});
My routes.js has the following:
app.get('/auth/twitter', passport.authenticate('twitter'));
I can verify that I am getting here, however it doesn't seem the passport.authenticate is being executed.
More over if I try to hit the same route from html markup using (href="/auth/twitter" target="_self") everything works great.
Any idea what I am missing?

just looking at your code without checking passport api. I think passport. authenticate return 302 redirect as response. 302 redirect has empty body. your callback function will not follow 302. when you do this in a link, the browser do follow 302.

Related

Moving Passport Google strategy authenticate method from route to controller breaks

I am using the 'passport-google-oauth' strategy from Passport with Nodejs based Express app to provide authentication. This worked great when I had the logic inside the route file as follows:
router.route('/auth/google')
.get(passport.authenticate('google', {
scope: ['https://www.googleapis.com/auth/userinfo.profile']
});
);
When I moved everything into a controller to make the app more manageable, it stopped working. Specifically, no error is being thrown. The GET request succeeds and the debug method outputs the string, but then it just loads forever and I'm not being redirected to the Google page to select which account to log in with. Any ideas what is going on here and what I need to change to make it flow like fine wine?
// route file
router.route('/auth/google')
.get(backendController.googleAuthenticate);
// controller file
exports.googleAuthenticate = () => {
debug('attempting Google authentication');
passport.authenticate('google', {
scope: ['https://www.googleapis.com/auth/userinfo.profile'],
failWithError: true
});
}

Initialize Current User Service on Application Start in AngularJS

I’m developing a Single Page Application with AngularJS.
When a user successfully logs in, a security token is stored in a cookie. Now, when he refreshes the page, the token will be sent to the backend, which returns a JSON object "currentUser" containing all the relevant information about the current user (as name, access-groups, profile picture, etc.).
The problem is, this is an asynchronous process of course, so when the controller starts another operation, say, just alerting the user’s name, this value will be undefined at that time.
Of course, I could set a timeout but is there a better solution?
I thought about a "currentUserService", which initializes first (sending the cookie and filling the user information with the backend response) and can only be processed after this initialization is completed.
But how can this be done? Or are there any other possibilities?
edit:
Hi guys,
thanks for the input!
Both of your suggestions seem to be very promising for asynchronous requests in general, but I think they might not fit perfectly for my concern:
The information about the current user only have to be requested once, so I would like to store them for the whole application (e.g. in the rootScope or a service) accessible from any controller without having to request them again in every controller (as in the callback or resolve-solution) but make sure that there won’t be any „timeout“ problems. Do you have any ideas?
You can resolve the user's data before the view loads either with ng-route or ui-router:
This example is written for ui-router:
.state('profile', {
url: '/profile',
controller: 'profileCtrl as vm',
resolve: {
user: function(AuthService) {
//Return a promise or an object to be resolved.
return AuthService.getUserFromToken(); //Say this is asynchronous and returns a promise
}
}
});
//In controller:
.controller('profileCtrl', function(... , user) {
//User data available here
this.user = user;
});
Please note if any errors arise during the resolve stage the state will not be loaded so you'll have to take care of the errors!
If a user refreshes you have to initialize everything. I assume the token is stored in localstorage or something and I assume this is angular 1.*. To do this I think you should call user-related functions from your http call-callback:
$scope.user = {};
$scope.getUser = function(){
$http({
method: 'GET',
url: '/someUrl'
}).then(function (res) {
$scope.user = res.data; //or whatever the response is
$scope.handleUserRelatedThings();
}).catch(function(err) {
//handle error
})
}
$scope.handleUserRelatedThings = function(){
//do things with $scope.user
alert($scope.user.name);
}
//on init
$scope.getUser();

How to avoid $compile:tpload errors on 401 status code response

We are developing a Single Page Application with AngularJS and ASP.NET MVC Json Rest API.
When an unauthenticated client tries to navigate to a private route (Ex: /Foo/Home/Template) to get a template, it gets a 401 response from the Web API and our AngularJS app automatically redirects it to the login page.
We are handling the 401 with $http interceptor with something like this:
if (response.status === 401) {
$location.path(routeToLogin);
return $q.reject(response);
}
Entering the correct credentials allows the client to get the template.
Everything is working perfectly except for one detail; the Javascript console reports this error:
Error: [$compile:tpload] http://errors.angularjs.org/1.3.0/$compile/tpload?p0=%Foo%2FHome%2FTemplate%2F
AngularJs documentation states this:
Description
This error occurs when $compile attempts to fetch a template from some
URL, and the request fails.
In our AngularJs app the request fails but it is by design because the resource is there but it cannot be accessed (401).
Should I move on and accept this kind of error on console or is it possible to mute or shield it in some way?
EDIT:
I have debugged the angular source a little bit and I found what part of the code is raising the exception.
Since we are using TemplateUrl to declare our templates, we are indirectly using the function compileTemplateUrl that makes this call:
$templateRequest($sce.getTrustedResourceUrl(templateUrl))
this leaves the second parameter (ignoreRequestError) of templateRequest undefined.
ignoreRequestError(optional)boolean
Whether or not to ignore the exception when the request fails or the
template is empty
When our http interceptor, handling the 401 status code, rejects the promise, the $http.get inside the $TemplateRequestProvider fails and calls this function:
function handleError() {
self.totalPendingRequests--;
if (!ignoreRequestError) {
throw $compileMinErr('tpload', 'Failed to load template: {0}', tpl);
}
return $q.reject();
}
I believe we can't do anything to prevent the error on console as TemplateUrl does not allow to set the ignoreRequestError flag to false.
I've tried to bypass the reject in case of 401 status code; this fixes the error on console but sadly it has a side effect: an empty template is wrongly cached into the TemplateCache causing othe problems.
After some thinking I remembered about decorating in Angular, it solved this problem perfectly:
app.config(['$provide', function($provide) {
$provide.decorator('$templateRequest', ['$delegate', function($delegate) {
var fn = $delegate;
$delegate = function(tpl) {
for (var key in fn) {
$delegate[key] = fn[key];
}
return fn.apply(this, [tpl, true]);
};
return $delegate;
}]);
}]);
You should be able to intercept the call for the template by status and url.
Plunker
app.config(function($httpProvider) {
var interceptor = function($location, $log, $q) {
function success(response) {
// The response if complete
$log.info(response);
return response;
}
function error(response) {
// The request if errors
$log.error(response);
return $q.reject(response);
}
return function(promise) {
return promise.then(success, error);
}
}
$httpProvider.responseInterceptors.push(interceptor);
});
As I see it, you have two options:
Option A)
go with the interceptors. However, to eliminate the compile you need to return success status code inside response error (BAD) OR redirect to the login page inside the interceptor (Good):
app.factory('authInterceptorService', function () {
var interceptor = {};
interceptor.responseError = function (rejection) {
if (rejection.status === 401 && rejection.config.url === "home template url") {
//BAD IDEA
//console.log("faking home template");
//rejection.status = 200;
//rejection.data = "<h1>should log in to the application first</h1>";
//GOOD IDEA
window.location = "/login.html";
}
return rejection;
}
return interceptor;
});
and on app config:
app.config(['$httpProvider', function ($httpProvider) {
$httpProvider.interceptors.push('authInterceptorService');
}
Option b)
make the home template public. After all it should be just html mark-up, without any sensible information.
this solution is clean...and perhaps is also possible.

AngularJS unwanted Behavior when reloading page

I'm developing an Angular application with the MEAN stack, so suppose you got an express route that make a query to the databse, the results are sent in the response:
app.get('/api', function(req, res){
Todo.find({}, "", function(err,todos){
if (err)
res.send(err);
res.json(todos);
});
});
In the client-side:
Controller :
...
Todo.get().success(function(data){ //i got the service with the $http call
$scope.todos = data;
});
When I go to localhost:8080/#/api, I can see my partial and the data I requested.
The problem that I'm having is that if I omit the hashtag, i don't see the partial, I only see the response data in JSON format.
I also tried to use html5 mode, but if I reload I got the same behavior.
Any ideas on how can I avoid this behavior??
Anything after the # isn't sent to the server. So when you go to localhost:8080/#/api, expressjs just sees a request to / and returns the AngularJS template. AngularJS then routes the browser page using the /#/api, which called the Todo.get() and (I assume) makes a call to localhost:8080/api, returning the data from the DB.
That's why you only get the data when you omit the hash and when you use html5 mode.
I would suggest changing your API call to:
/api/todos - return data from the db
And change your AngularJS route to just use:
/todos - show the partial and data requested

Calling Angular service from outside JS via .scope().call() hangs request

I'm working on adding a Google+ signin button to my Angular app and most of it is working, except for the handling of the callback result. The callback from the G+ signin is an outside JS function called signinCallback with looks like so:
//Handling the Google+ Signin right here
function signinCallback(authResult) {
angular.element($("#btnGooglePlus")).scope().handleGoogleSignin(authResult);
}
The only way I could figure out how to pass the authResult back into the controller was to call a controller method via element.scope(). handleGoogleSignin is called fine, and inside that function there is a http.get service call that looks like:
User.getSocialKey(key).then(function(data) {
console.log(data);
});
User is a service, and getSocialKey looks like:
getSocialKey: function(etag) {
console.log("Hit the social key service with the etag: " + etag);
return $http({
url: '/api/user/social',
method: 'post',
data: {etag:etag}
}).then(function(result) {
console.log("Returning promise from social service");
return result.data;
});
},
The first log statement gets hit fine, then nothing. Request is never sent. Now, if I go and click something on the page that has an ng-model attribute (example, a checkbox), the request is then sent and received just fine. So my question: Why is my Angular service call being suspended until I click on something? Why isn't it going through right away?
I've tried replacing getSocialKey with working service calls, same thing. I believe the issue comes down to calling the function with angular.element($("#btnGooglePlus")).scope().handleGoogleSignin(authResult); but I'm not sure. Anyone seen this before?
Sorry I can't test but I think you should call .$apply() since the action is triggered outside the AngularJS's scope.
function signinCallback(authResult) {
angular.element($("#btnGooglePlus")).scope().handleGoogleSignin(authResult);
angular.element($("#btnGooglePlus")).scope().$apply();
}

Categories

Resources