bacbkone router redirect if not authenticated - backbone.js

I am trying to implement a simple app that needs a login and user authentication. As I am new to backbone and marionette, I have been trying to follow the example for this tutorial: https://github.com/davidsulc/marionette-gentle-introduction
Generally I have set up a new app:
var App = new Marionette.Application({});
App.addRegions({
headerRegion : "#nav-region",
mainRegion : "#main-region"
});
App.navigate = function(route, options){
options || (options = {});
Backbone.history.navigate(route, options);
};
App.getCurrentRoute = function(){
return Backbone.history.fragment
};
App.on("start", function(){
if(Backbone.history){
Backbone.history.start();
}
});
And routers are defined in modules, e.g.:
App.module("ContentManagementApp", function(ContentManagementApp, App, Backbone, Marionette, $, _){
ContentManagementApp.Router = Marionette.AppRouter.extend({
appRoutes : {
"contentmanagement/:dsid(/:dspageclassid)": "showContentMananagement",
}
});
var API = {
showContentMananagement : function(dsid, dspageclassid){
// If not set, set to frontpage
ContentManagementApp.Show.Controller.showDSPage(dsid, dspageclassid);
App.execute("set:active:header", "contentmanagement");
},
};
App.on("contentmanagement:show", function(dsid, dspageclassid){
App.navigate("contentmanagement/" + dsid + "/" + dspageclassid);
API.showContentMananagement(dsid, dspageclassid);
});
App.addInitializer(function(){
new ContentManagementApp.Router({
controller : API
});
});
});
I would like to test if the user is logged and redirect to the login page when the app starts, but it seems like App.addInitializer is called before. Does it mean I have to do the check in each module, or can I get by it somehow?

How do you determine if the user is logged or not?
If it's a call to an API that could fail (due to the user being unauthenticated), it will probably return an HTTP error code 403. I usually do this using a global jQuery ajax.error() handler, I check if it's a 403 (Forbidden) for any of my normal API calls (model fetching and so on) and if it is, I redirect to a login url.
Otherwise, if you want to check for a cookie or similar, you should do it before calling Backbone.history.start(). Only start the app if the user is logged. :)

I just set this up in my app - in your backend when the user is logged in create a cookie/destroy it when they sign-out. Then I use the jquery-cookie-rails gem to access the cookie as $.cookie('cookie_name') and if it isn't there I route them to the signin path.
I would note - I also check to see if the user is signed on the backend when hitting different controller actions and route them appropriately. I just like the extra protection :).

Related

integrating linkedin login with angularjs application

I am new to angular js .I would like to know the procedure to get api key and integrating linked in sign in ,sign up with angularjs application.
You can use LinkedIn SDK to handle authorisation and sing up or sing off users.
Documentation: https://developer.linkedin.com/docs/getting-started-js-sdk
You have to initial SDK. You will need to create an app in your LinkedIn Developer panel. Then you get an API Key there.
Then in your app you have to create a service that will call LinkedIn API.
Something like this:
export class LinkedIn {
constructor($q, $window) {
'ngInject';
this.$q = $q;
this.$window = $window;
};
get() {
let doc = this.$window.document;
let script = doc.createElement('script');
let deferred = this.$q.defer();
script.src = 'http://platform.linkedin.com/in.js';
script.innerHTML = [
'api_key: ' + YOUR_API_KEY,
'authorize: ' + 'true',
'lang: ' + 'en-US',
'onLoad: onLinkedInApiLoad',
'scope: ' + YOUR_APP_SCOPE
].join('\n');
this.$window.onLinkedInApiLoad = () => {
deferred.resolve(this.$window.IN);
};
doc.body.appendChild(script);
return deferred.promise;
};
}
Next you need to decide where and when you want to initial this call. You can do that in .run block or made some middleware to handle it. After that you will receive LinkedIn API object.
When you have got your LinkedIn API object you can request authorisation, check if user has been already logged in and etc. Options are describe in documentation. You can authorise user calling IN.User.authorize(handler || angular.noop) or logout IN.User.logout(handler || angular.noop)
There is also options to do a callback on Event where user log in or log out for example:
IN.Event.on(IN, eventName, callback, callbackScope, extraData);
IN.Event.onOnce(IN, eventName, callback, callbackScope, extraData);
You can use angular module for linkedIn authentication. angular-social-login

Node API - How to link Facebook login to Angular front end?

Rewriting this question to be clearer.
I've used passport-facebook to handle login with facebook on my site.
My front end is in Angular so I know now need to understand whats the correct way of calling that api route. I already have several calls using Angular's $http service - however as this login with facebook actually re-routes the facebook page can i still use the usual:
self.loginFacebook = function )() {
var deferred = $q.defer();
var theReq = {
method: 'GET',
url: API + '/login/facebook'
};
$http(theReq)
.then(function(data){
deferred.resolve(data);
})
return deferred.promise;
}
or is it perfectly ok/secure/correct procedure to directly hit that URL in a window location:
self.loginFacebook = function (){
$window.location.href = API + '/login/facebook';
}
Furthermore, from this how do I then send a token back from the API? I can't seem to modify the callback function to do that?
router.get('/login/facebook/callback',
passport.authenticate('facebook', {
successRedirect : 'http://localhost:3000/#/',
failureRedirect : 'http://localhost:3000/#/login'
})
);
Thanks.
I was stacked on the same problem.
First part:
I allow in backend using cors and in frontend i use $httpProvider, like this:
angular.module('core', [
'ui.router',
'user'
]).config(config);
function config($httpProvider) {
$httpProvider.defaults.useXDomain = true;
$httpProvider.defaults.headers.common['X-Requested-With'];
$httpProvider.defaults.headers.common["X-Requested-With"] = 'XMLHttpRequest';
};
The second part:
<span class="fa fa-facebook"></span> Login with facebook
This call my auth/facebook route that use passport to redirect to facebook page allowing a user to be authenticated.
If the user grant access, the callback /api/auth/facebook/callback is called and the facebook.strategy save the user with the profile data.
After saving the user, i create a special token with facebook token, id and email. This info is used to validate every time the user access to private states in the front.
My routes are something like this:
router.get('/facebook', passport.authenticate('facebook',
{ session: false, scope : 'email' }));
// handle the callback after facebook has authenticated the user
router.get('/facebook/callback',
passport.authenticate('facebook',
{session: false, failureRedirect: '/error' }),
function(req, res, next) {
var token = jwt.encode(req.user.facebook, config.secret);
res.redirect("/fb/"+token);
});
In frontend i catch the /fb/:token using a state and assign the token to my local storage, then every time the user go to a private section, the token is sent to backend and validate, if the validation pass, then the validate function return the token with the decoded data.
The only bad thing is that i don't know how to redirect to the previous state that was when the user click on login with facebook.
Also, i don't know how you are using the callback, but you need to have domain name to allow the redirect from facebook. I have created a server droplet in digitalocean to test this facebook strategy.
In the strategy you have to put the real domain in the callback function, like this:
callbackURL: "http://yourdomain.com/api/auth/facebook/callback"
In the same object where you put the secretId and clientSecret. Then, in your application in facebook developers you have to allow this domain.
Sorry for my english, i hope this info help you.
Depending on your front-end, you will need some logic that actually makes that call to your node/express API. Your HTML element could look like
<a class='btn' href='login/facebook'>Login</a>
Clicking on this element will make a call to your Express router using the endpoint of /login/facebook. Simple at that.

Sending authenticated requests to GitHub API from authorized Auth0 app user

I am having a very difficult time getting authenticated API requests to GitHub to work. I have created an authorized application in GitHub and connected it to my Auth0 account. I have no problems getting a user signed in using their GitHub account but once they are signed in I cannot make authenticated requests to the GitHub API (I am trying to set a GitHub webhook in one of the user's GitHub repos). All my requests are rejected for having incorrect credentials.
I have the JWT issued by Auth0 being sent along in each request to the GitHub API endpoint but it appears as though this is not sufficient. The Auth0 profile that comes back from my user seems to have an access_token in it, but sending this along does not work either.
Here is what my Auth0 login code looks like (using the Angular API):
angular.module('myApp').controller('LoginCtrl', ['$scope', '$http', 'auth', 'store', '$location',
function ($scope, $http, auth, store, $location) {
$scope.login = function () {
auth.signin({
authParams: {
responseType: 'token' // I think this is the default but just in case
}
}, function (profile, token) {
// Success callback
store.set('profile', profile);
store.set('token', token);
$location.path('/');
}, function () {
// Error callback
console.debug("error logging in");
});
};
}]);
This works fine. They authorize the GitHub application tied to my organization's Auth0 account with its requested permissions without issue and land back in my application and I then have access to an Auth0 profile tied to their GitHub account, but then if I try and make an authenticated request to the GitHub API on their behalf:
var username = auth.nickname;
var repo = "some_user.github.io"; // todo: get repo from setup process
var url = "https://api.github.com/repos/" + username + "/" + repo + "/hooks/";
var conf = {
name: "web",
active: true,
config: {
"url": "https://webtask.it.auth0.com/api/run/wt-my-container_com-0/echo?webtask_no_cache=1",
"content_type": "json"
}
};
$http.post(url, conf).success(function(data, status) {
console.log("post successful:");
console.log(status);
console.log(data);
});
... GitHub rejects the request, either saying the request resource doesn't exist (to prevent private data leakage) or that I supplied bad credentials, depending on different variables (if I try supplying the "access_token" field provided in their Auth0 profile as a query param or supply my Auth0 application's client secret, etc).
I have scoured the documentation of both Auth0 and GitHub trying to figure out what the correct procedure is (for example, do I need to implement the whole OAuth2 token flow myself? it seems like Auth0 should be doing that for me) but nothing I have tried so far works, and nothing on Google has pointed me in the right direction. I have tried a number of other methods of doing this without success but I don't want to make this post too much longer. Any help would be greatly appreciated.
I figured it out. There were two problems: one, a trailing slash had crept in on the end of my API call to the GitHub endpoint, which evidently breaks something and causes GitHub to reject the request, and second, I had set things up to send along the Authorization header with every request as per the Auth0 guide here: https://auth0.com/docs/client-platforms/angularjs, specifically this part:
myApp.config(function (authProvider, $routeProvider, $httpProvider, jwtInterceptorProvider) {
// ...
// We're annotating this function so that the `store` is injected correctly when this file is minified
jwtInterceptorProvider.tokenGetter = ['store', function(store) {
// Return the saved token
return store.get('token');
}];
$httpProvider.interceptors.push('jwtInterceptor');
// ...
});
But GitHub does not like that since it does not contain the token it is expecting and will reject the request if it sees it. Once I removed the trailing slash and removed the above code, everything started working as expected.
Look at this gitHub page. It is something like this with angular:
//'common' will add the headder to every request.
$httpProvider.defaults.headers.common["Authorization"] = token YOUR_TOKEN;

refreshing user.is_authenticated with angular and django rest framework

I'm using django rest framework on the backend and angularjs on the frontend. The problem is the login, in angular I do:
function ($scope, $http, User) {
$scope.login = function (user) {
$http.post('/login/', user)
.then(function (response, status) { // success callback
console.log("success");
console.log(response);
}, function(response) { // error callback
console.log("error");
console.log(response);
})
}
}
then in my views.py I do:
def login_view(request):
user = authenticate(username=request.data['username'], password=request.data['password'])
if user is not None:
login(request, user)
return HttpResponse(status=200)
return HttpResponse(status=400)
But in my home template which is the only django template that I use, the rest is pure html since I use ui-router with state view, so the {% if user.is_authenticated %} won't get updated and I have to refresh the page manually. Is there any solution to make the 'if' statement in the template to be trigger without refreshing all the page, or any better solution to make a login system in my website?
Thank you.
You should not use Django's templatescripts in AngularJS.
AngularJS is a SPA. Django's templatescripts does not work well with SPAs.
You need to create your client logic in javascript only.
This can be done by creating a server endpoint that returns true if a given user is authenticated.

Marionette JS multiple route ,controller and session management

So my Marionette application has folowing 2 routes and controllers
AuthRoute and AuthController for user login and logout
UserRoute and UserControler for user listing, new user and edit user
In AuthController when user login I need to save the user session somewhere so that it can be acceable to both these routes, So next time when user hits user listing page I can check for user session and then load the user listing page.
How can I achieve this?
Also, what will be the best way to check if user session is valid or not? My plan is to create a service which will return user details if the session is valid or else will return 401, but till the time service returns the response I can't load the route specific views.
Can someone help me on these?
Thanks in Advance
MSK
I usually save session data in a singleton Beckbone.Model referenced by the Marionette.Application.
Session Model:
var SessionModel = Backbone.Model.extend({
initialize: function () {
this.listenTo(this, 'change', function (model) {
// Manage cookies storage
// ex. using js.cookie
Cookies.set('session', model.toJSON(), {expires: 1});
});
},
checkAuth: function () {
...
},
login: function (user, password, remember) {
...
},
logout: function () {
...
}
});
Application:
var app = new Marionette.Application({
session: new SessionModel(),
...
})
app.reqres.setHandlers({
session: function () {
return app.session;
}
});
Then you can access session model from any point through "global channel":
Backbone.Wreqr.radio.channel('global').reqres.request('session').checkAuth()
So you can implement all your session procedures in the session model class that can be accessed from the whole application. Using cookies you will also save the user session in the browser.
If you use backend API for Backbone then:
How can I achieve this?
The best way for saving a user session is cookies. Use it for storing and retrieving the user authentication_token.
Also, what will be the best way to check if user session is valid or
not?
You have to store authentication_token on the backend side. For login, logout, signup you should iterate with your API.

Resources