18

I am implementing an auth system in my angular js app.

What I am planning it like below:

  1. Get user info(name and pass from login form)
  2. Check whether user exists or not
  3. if exists server respond with a session cookie and frontend will redirect to a certain page.
  4. then user will do some task which will generate API request
  5. API request should have cookie information that was sent on step 3
  6. server check whether the cookie was generated or not and if cookie was found then respond with the API request results. And in my service I am doing something like
    MyApp.service('myAuth', function($http, $q) {
        this.authHeader = null;
        this.checkAuth = function(){
        //do api call and if success sets this.authHeader = response
        }
        this.isAuthenticaed = function(){
            this.authHeader ? return this.authHeder  : return false;
       }

After submitting the login form I will call checkAuth and get my session cookie back from my server, how I can add the cookie information while doing the next REST call and also when user will navigate throughout the application after log in I do want to check each time isAuthenticaed true or false, in Angularjs when it will navigate to another page does it resets after setting it true from the first call? And is my approach 1-6 good or do you have any specific suggestions? Btw I checked previous so entries but those are not what I want to know.

Peter Albert
  • 16,917
  • 5
  • 64
  • 88
arnold
  • 1,682
  • 8
  • 24
  • 31
  • Check this question [about auth](http://stackoverflow.com/questions/17982868/angularjs-best-practice-for-ensure-user-is-logged-in-or-out-using-cookiestore/17983610#17983610) – Deividi Cavarzan Aug 17 '13 at 15:42
  • for check if user is authenticated... your server side need to return and 401 for the request and your redirect to the login page. To check this, you cant put an interceptor on $http to listen to this HTTP 401 response. – Deividi Cavarzan Aug 17 '13 at 15:44

4 Answers4

35

I am not sure about your backend, but this is how I would do it

  • Create a separate login page (dedicated url not angular sub view or modal dialog).
  • If the user is not authenticated redirect to this login page. This is done by server redirects. This page may or may not use angular framework, as it just involves sending a user\password to server.
  • Make a POST (not AJAX request) from the login page, and verify on server.
  • On the server set the auth cookie. (Different frameworks do it differently. ASP.Net sets form authentication cookie.)
  • Once the user is authenticated redirect user to the actual angular app and load all its components.

This saves any code require to manage authentication on client side in Angular. If the user lands on this page he is authenticated and has the cookie.

Also default browser behavior is to send all cookies associated with a domain with each request, so you don't have to worry if angular is sending some cookie or not.

T J
  • 42,762
  • 13
  • 83
  • 138
Chandermani
  • 42,589
  • 12
  • 85
  • 88
  • 2
    This is a very good suggestion, not only it simplifies the process but opens up new chances to get better performances. If anyone is interested in the above answer approach check this [ng-conf video](http://youtu.be/62RvRQuMVyg?t=2m41s) about this very topic. – Aurelio Oct 17 '14 at 15:36
  • In Angular I may load many views without refreshing the page. With Angular how can I check the session with out reloading the page? – Jared Christensen May 29 '15 at 19:18
  • 2
    Problem happens when session expires while you are in angular app. And you don't want to lose state of app by leaving to login url. – hex Jun 12 '15 at 23:35
7

I use the http-auth-interceptor. http://ngmodules.org/modules/http-auth-interceptor

In my backend (asp.net mvc) I build a simple Authentication Service and return an http error 401 if the user is not authenticated. Then I handle the error with a login-view in the SPA site.

T J
  • 42,762
  • 13
  • 83
  • 138
N0rdl1cht
  • 565
  • 5
  • 17
  • I am using interceptor to add the cookie after authentication to call the REST request, one thing I am not clear. How the log out should work. I am saving session data(cookie) into a file and when user sends any new request I check with the saved cookie. Do I need to delete the cookie when user will send logout request? – arnold Aug 18 '13 at 15:06
  • I am using ASP.NET MVC on server side, with an modified Forms Authentication. At Logout my clients send a logout request to the server and the server deletes the auth cookie. – N0rdl1cht Aug 20 '13 at 09:03
  • 2
    This module is the best option and the original blog posting explains how having a /login or /logout route in your single-page-app doesn't really make sense anymore (tho it does on the backend). Just started using http-auth-interceptor and am seeing what it does/doesn't do--so this answer seems best from the standpoint of overall approach but doesn't cover all aspects of the login flow (from spa/frontend to backend and back, for both failure and success). http://frederiknakstad.com/authentication-in-single-page-applications-with-angular-js/ provides much of the missing detail. – jimmont Jan 02 '14 at 21:29
4

The ideas put forth by the previous answers will work, but I think they're overkill. You don't need anything this complex.

how I can add the cookie information while doing the next REST call

Turn on withCredentials by default inside $httpProvider like so:

app.config(['$httpProvider', function($httpProvider) {
    $httpProvider.defaults.withCredentials = true;
}]);

Then remove the wildcard (if you had one) from the CORS-related headers, and set allow-credentials, on the server side. In my case, using Python + Flask + Flask-Restful, it's super easy and looks like this:

import Flask
from flask_restful import Api
app = Flask(__name__)
api = Api(app)
api.decorators = [cors.crossdomain(origin='http://localhost:8100', credentials=True)]

Now cookies will be set and returned automatically and transparently by the browser. See these threads for more info:

when user will navigate throughout the application after log in I do want to check each time isAuthenticaed true or false

As suggested above, have the server return 401 if the auth session expires or is deleted, and use $httpInterceptor in Angular to catch this like so:

app.config(function($httpProvider) {
    var interceptor =
        function($q, $rootScope) {
            return {
                'response': function(response) {
                    return response;
                 },
                'responseError': function(rejection) {
                    if (rejection.status==401) {
                        // Modify this part to suit your needs.
                        // In my case I broadcast a message which is
                        // picked up elsewhere to show the login screen.
                        if (!rejection.config.url.endsWith('/login'))
                        {
                            $rootScope.$broadcast('auth:loginRequired');
                        }
                    }

                    return $q.reject(rejection)
                 }
            }
        };

    $httpProvider.interceptors.push(interceptor);
});
Community
  • 1
  • 1
Lane Rettig
  • 6,640
  • 5
  • 42
  • 51
1

(Disclosure: I'm one of the developers of UserApp)

You could use the third-party service UserApp for this, together with the AngularJS module.

Check out the getting started guide, or take the course on Codecademy. Here's some examples of how it works:

  • Login form with error handling:

    <form ua-login ua-error="error-msg">
        <input name="login" placeholder="Username"><br>
        <input name="password" placeholder="Password" type="password"><br>
        <button type="submit">Log in</button>
        <p id="error-msg"></p>
    </form>
    

    User info is accessed using the user service: user.current.email

    Or in the template: <span>{{ user.email }}</span>

  • Signup form with error handling:

    <form ua-signup ua-error="error-msg">
        <input name="first_name" placeholder="Your name"><br>
        <input name="login" ua-is-email placeholder="Email"><br>
        <input name="password" placeholder="Password" type="password"><br>
        <button type="submit">Create account</button>
        <p id="error-msg"></p>
    </form>
    

    ua-is-email means that the username is the same as the email.

  • How to specify which routes that should be public, and which route that is the login form:

    $routeProvider.when('/login', {templateUrl: 'partials/login.html', public: true, login: true});
    $routeProvider.when('/signup', {templateUrl: 'partials/signup.html', public: true});
    

    The .otherwise() route should be set to where you want your users to be redirected after login. Example:

    $routeProvider.otherwise({redirectTo: '/home'});

  • Log out link:

    <a href="#" ua-logout>Log Out</a>

  • Hide elements that should only be visible when logged in:

    <div ng-show="user.authorized">Welcome {{ user.first_name }}!</div>

And to authenticate to your back-end services, just use user.token() to get the session token and send it with the AJAX request. At the back-end, use the UserApp API to check if the token is valid or not.

If you need any help, just let me know :)