Code Monkey home page Code Monkey logo

Comments (11)

aderbas avatar aderbas commented on September 24, 2024

me too, +1

from angular-digest-auth.

psenough avatar psenough commented on September 24, 2024

+1
i'm picking up legacy code (don't usually use angular), need to get digest auth working, all angular stackoverflows point to this lib but can't figure out how the hell we go from including the module to having a dgAuthService available for the controllers to use.
working example code would be really useful.

from angular-digest-auth.

firrib avatar firrib commented on September 24, 2024

I got this working (for the most part), and wanted to share my solution in case it helps others. It took a bit of understanding Digest Authorization and making a few changes to the angular-digest-auth.js file.

First off, I looked at the RESPONSE header from our server, which was already set up for digest authentication:
Authenticate: Digest realm="concordUI", nonce="66fe7765"
Connection: keep-alive
Content-Length: 0

This told me two things:

  1. To pass Authenticate as the header to dgAuthServiceProvider.setHeader
  2. That our server is using RFC 2069, which is basically 'Level 1' Digest Authentication, according to the Wiki ([https://en.wikipedia.org/wiki/Digest_access_authentication]).

Looking at tafax's angularJS digest code, it is set up for 'Level 2' Digest auth (RFC 2617), so it will include the nonceCount and qop in the HASH response. This isn't compatible with RFC 2069, so I had to edited out those parts in his code (generateResponse and generateHeader functions). For the generateResponse function, my 'return' code followed the RFC 2069 standard:
return md5.createHash(ha1 + ":" + authServer.info.nonce + ":" + ha2);

In the generateHeader function, I removed the algorith, opaque, qop, and nc lines. I may have been able to keep them in, but I took them out just to be safe. The modfied generateResponse method ignores the nc and cnonce values. I left them in just in case we end up doing 'Level 2'/RFC 2617 in the future.

In my app, I configured the dgAuthServiceProvider like this:

app.config(['dgAuthServiceProvider', function(dgAuthServiceProvider) {
    dgAuthServiceProvider.setHeader('authenticate');

    dgAuthServiceProvider.setLimit('inf');

    dgAuthServiceProvider.setConfig({
        login: {
            method: 'GET',
               url: '/con/data/userList' //Some URL which requires authentication
        },
        logout: {
            method: 'GET',
            url: '/con/data/userList' //Some URL which requires authentication
        }
    });

    dgAuthServiceProvider.callbacks.login.push(['AuthenticationSvc', function(AuthenticationSvc)
    {
        return {
            successful: function(response)
            {
                AuthenticationSvc.loginSuccessful(response);
            },
            error: function(response)
            {
                AuthenticationSvc.loginError(response);
            },
            required: function(response)
            {
                AuthenticationSvc.loginRequired(response);
            },
            limit: function(response)
            {
                AuthenticationSvc.loginLimitReached(response);
            }
        };
    }]);

    dgAuthServiceProvider.callbacks.logout.push(['AuthenticationSvc', function(AuthenticationSvc)
    {
        return {
            successful: function(response)
            {
                AuthenticationSvc.logoutSuccessful(response);
            },
            error: function(response)
            {
                AuthenticationSvc.logoutError(response);
            }
        };
    }]);

}]);

I created my own service, 'AuthenticationSvc', to handle all the responses from the digest service, including login and logout functions:

(function() {
    'use strict';

    angular
        .module('myApp')
        .factory('AuthenticationSvc', AuthenticationSvc);

    AuthenticationSvc.$inject = ['$q', '$timeout', 'dgAuthService'];
    function AuthenticationSvc(   $q,   $timeout,   dgAuthService) {
        var service = {
            //Functions
            start: start,
            login: login,
            logout: logout,
            loginSuccessful: loginSuccessful,
            loginError: loginError,
            loginRequired: loginRequired,
            loginLimitReached: loginLimitReached,
            logoutSuccessful: logoutSuccessful,
            logoutError: logoutError,

            //Variables
            isLoggedIn: false
        };

        var qLogin = null;
        var qStart = null;
        var qLogout = null

        //These help differentiate between starting (and automatically logging in from previous creds) and manually logging in
        var bStarting = false;
        var bManualLogIn = false;

        return service;

        // implementations //

        function start() { //Used when user first hits the web site - Starts the auth service
            qStart = $q.defer();
            $timeout(function() {
                //Start auth service if it hasn't already started
                if (dgAuthService.hasStarted()) { //Already started, so just resolve the qStart
                    qStart.resolve();
                } else {
                    bStarting = true;
                    dgAuthService.start();  
                }
            });
            return qStart.promise;
        }

        function loginSuccessful(response) {
            service.isLoggedIn = true;
            if (bStarting) {
                bStarting = false;
                qStart.resolve();
            }
            if (bManualLogIn) {
                bManualLogIn = false;
                qLogin.resolve();
            }
        }

        function loginError(response) {
            response.errorType = 'loginError';

            if (bStarting) {
                bStarting = false;
                qStart.reject(response);
            } 

            if (bManualLogIn) {
                bManualLogIn = false;
                qLogin.reject(response);
            }
        }

        function loginRequired(response) {
            response.errorType = 'loginRequired';
            if (bStarting) {
                bStarting = false;
                qStart.reject(response);
            }

            //No bManualLogIn check here because if we get to this state we've started but not logged in
            //from previous session, which is fine. We want to keep 'bManualLogIn' value as it is
        }

        function loginLimitReached(response) {
            response.errorType = 'loginLimitReached';
            if (bStarting) {
                bStarting = false;
                qStart.reject(response);
            }

            if (bManualLogIn) {
                bManualLogIn = false;
                qLogin.reject(response);
            }
        }

        function logoutSuccessful(response) {
            service.isLoggedIn = false;
            qLogout.resolve();
        }

        function logoutError(response) {
            qLogout.reject(response);
        }

        /**
         * Do user login
         * @param credentials (object) {username (string) - username, password (string) - password}
         */
        function login(credentials) {
            qLogin = $q.defer();
            bManualLogIn = true;
            $timeout(function() {
                service.start().finally(function() { //Start the service in case it hasn't started yet (user browsed directly to login page)
                    if (service.isLoggedIn) { //We're logged in so we need to logout and then log back in
                        service.logout().finally(function() {
                            dgAuthService.setCredentials(credentials.username, credentials.password);
                            dgAuthService.signin();
                        });
                    } else { //Not currently logged in, so log in
                        dgAuthService.setCredentials(credentials.username, credentials.password);
                        dgAuthService.signin();
                    }
                });
            })

            return qLogin.promise;
        } //End of function login 

        /**
         * Do user logout
         */
        function logout() {
            qLogout = $q.defer();
            $timeout(function() {
                dgAuthService.signout();
            });
            return qLogout.promise;
        } //End of function logout
    } //End of AuthenticationSvc
})();

In my router code, I use a resolve which uses the above AuthenticationSvc methods to start the service and routes to a login page if the user isn't already logged in. The login page calls the AuthenticationSvc's login function when the user presses the Login button.

The login/logout functions use promises, so I use AuthenticationSvc.login(creds).then.... in my Login page.

You might need to add this before the $httpProvider.interceptors.push([ in
the angular-digest-auth.js file:
$httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
This prevents the browser from displaying it's own username/password box.

Also, if you add that line, you might actually need to NOT add it (and the auth headers) when browsing off-site to a third party (like google). You'd handle that case in the 'request': function(request) property function of $httpProvider.interceptors.push.

I hope this helps someone, and I'm not too off base. But that's what I had to do to get it
working on the server we have.

BIG PS: A big thanks to tafax for writing the library. Digest Authentication is non-trivial, and he did a great job on the code! So thank you!!

from angular-digest-auth.

develmts avatar develmts commented on September 24, 2024

I know this is a very late comment , but

Where the dgAuthService.hasStarted() gets defined? Looking through the source code isn't defined anywhere. Where it comes from?

from angular-digest-auth.

firrib avatar firrib commented on September 24, 2024

@develmts : Sorry. I added it directly to the DgAuthService function in the angular-digest-auth.js file:

function DgAuthService($q, authIdentity, authRequests, stateMachine)
    {
        /**
         * Specifies if the service is started.
         *
         * @type {boolean}
         * @private
         */
        var _started = false;

        this.hasStarted = function() {
            return _started;
        }
       .......
  }

I actually modified the file a lot to customize what I needed, but hopefully that's the only missing piece for this example.

Hope this helps!

-Scott

from angular-digest-auth.

develmts avatar develmts commented on September 24, 2024

Thanks !

from angular-digest-auth.

Bhimisetty avatar Bhimisetty commented on September 24, 2024

I just started working with this, but not able to do work, getting 401 unauthorized every time. @firrib Can you share your customised angular-digest-auth.js so that it will be helpful to me to resolve the issue

from angular-digest-auth.

firrib avatar firrib commented on September 24, 2024

@Bhimisetty : Sorry it's for a company I'm working for. I'm not comfortable doing that. Plus, I've made a lot of changes that are very specific to what I'm doing. I wish you luck, though. Perhaps it's on the server end?

from angular-digest-auth.

kimyearho avatar kimyearho commented on September 24, 2024

@firrib Thank you for providing us with the samples !!

from angular-digest-auth.

firrib avatar firrib commented on September 24, 2024

@kimyearho You're most welcome!

from angular-digest-auth.

kimyearho avatar kimyearho commented on September 24, 2024

Can not hide username / password display box Is there any other alternative?

from angular-digest-auth.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.