6

I have a web app that's already written in Django, and it is working quite well. I want to add a few views for angular as the plan is to move into that direction. One issue i'm facing is that some of my controllers require login, with django we normally use the @login_required decorator and everything is fine.

But with angular.js calling the controllers (for api's), the redirect is not happening. I'm assuming I'll have to somehow check if my django user is authenticated directly from angular side. Is there any explanation on how to confirm this on angular? I have been struggling with this and have read the following:

https://docs.angularjs.org/api/ng/service/$http

https://medium.com/@vince_prignano/angular-js-with-django-bde834dbd61e

$routeProvider not triggered after login redirect

http://www.django-rest-framework.org/api-guide/authentication/

Basically, I want to confirm, via Angular, that my user is logged in and if not, redirect them to the login page.

EDIT

I'm implementing a request interceptor as shown here:

Interceptor not working

However, in django @login_required it's returning the html of the redirecting page. Is there a way to get the URL and forward the user there?

Community
  • 1
  • 1
KVISH
  • 12,923
  • 17
  • 86
  • 162

5 Answers5

1

Add resolve in your $routeProvider as:

    $routeProvider 
      .when('/', { 
         templateUrl: '/views/main.html' }) 
      .when('/admin', {
         templateUrl: 'views/admin.html', 
         controller: 'AdminCtrl', 
         resolve: { loggedin: checkLoggedin } }) 
      .when('/login', { 
         templateUrl: 'views/login.html', 
         controller: 'LoginCtrl' }) 
      .otherwise({ redirectTo: '/' }); -

See more at: https://vickev.com/#!/article/authentication-in-single-page-applications-node-js-passportjs-angularjs

gettingLearned
  • 126
  • 1
  • 8
1

As mentioned in the previous answer, it would be best if your Django back-end can issue a 401 response when login is required. Right now it sounds like it's sending a 302, which you can still observe in the browser if you're making an XHR request. As you've found, using $http interceptors in Angular are a common way of looking for a 401 and sending the user to a login page.

I've taken a different approach: implement a service that abstracts this a bit, via a method called $user.get() - it makes a request to a known endpoint (in my case, /api/users/current) and rejects the returned promise if it sees a 401. In your case you could implement a rejection handler that uses window.location.href to send the user to your dedicated login page

Disclaimer: I work at Stormpath and we’ve spent a log of time thinking about this :) In my comments above I’m referring to our Stormpath Angular SDK - you can look at this library to see how I’ve solved this problem.

robertjd
  • 4,723
  • 1
  • 25
  • 29
1

When defining my app i'm doing this:

$httpProvider.interceptors.push('errorInterceptor');

and The code for that is below:

app.factory('errorInterceptor', ['$q', '$rootScope', '$location',
    function ($q, $rootScope, $location) {
        return {
            request: function (config) {
                return config || $q.when(config);
            },
            requestError: function(request){
                return $q.reject(request);
            },
            response: function (response) {
                return response || $q.when(response);
            },
            responseError: function (response) {
                if (response && response.status == 302 && response.data.url) {
                    window.location = response.data.url;
                    return;
                }
                return $q.reject(response);
            }
        };
}]);

Basically, we can't use login_required. We have to create a new decorator and provide a 302 status with a url.

KVISH
  • 12,923
  • 17
  • 86
  • 162
1

If you're making APIs calls via AJAX back to the server, most likely you don't want the response to be redirected to the login page.

I have the same use case and made my own decorator to return a 403 when the user is not logged in. You may also use 401 instead if you like (I left it commented out).

I use 403 because 401 seems to imply WWW authentication.

from django.http import HttpResponse, HttpResponseForbidden


def logged_in_or_deny(func):
    def check_request(request, *args, **kwargs):
        if (request.user.is_authenticated()):
            return func(request, *args, **kwargs)
        else:
            return HttpResponseForbidden('You must be logged in')       # 403 Response
            #return HttpResponse('You must be logged in', status=401)    # 401 Response
    return check_request

Then you would protect your view like this:

@logged_in_or_deny
def your_view(request):
    # ... your code ...
    return HttpResponse('Your normal response :)')

From Angular, it seems like you already know how to use an interceptor to check the response code and redirect the user accordingly. If you use the 403 or 401, you should check the against the response body just in case you respond with similar errors in the future.

While what you already have would work, 302 responses can be used for other reasons. It's better to have an explicit 4xx response rather than a 3xx redirection response since it'll be immediately obvious that it's a client side error (missing authentication).

Community
  • 1
  • 1
user193130
  • 8,009
  • 4
  • 36
  • 64
0

You can use Http-Auth-Interceptor. Suppose A view requires login and a user makes a request to the view without login then @login_required decorator returns the response with response code 401. In auth interceptor intercept the status code if status code is 401 then redirect user to the login page.

example site: http://witoldsz.github.io/angular-http-auth/

Abdul Gaffar
  • 340
  • 3
  • 5