38786 (7) [Avatar] Offline
#1
Once I had Auth0 configured I ran into a couple problems getting the authentication to work. I tried getting this working with Google, other providers may work differently.

1) The first issue involved an error message in the console about Access-Control-Allow-Origin. If you search Auth0 they punt and say its a google issue and give you a link to stackoverflow. https://stackoverflow.com/questions/28547288/no-access-control-allow-origin-header-is-present-on-the-requested-resource-err
Fixed this by editing my host file to have a domain name instead of localhost or 127.0.0.1 in my URL. Be sure you change you update Auth0 configuration for your client / default app to allow your url plus port number the allowed callback URLs and Allowed Web Origins fields otherwise it will continue to fail.

2) Once the above was fixed I tried logging in and I'd get the Auth0 login UI say there was an error and an alert telling me the same thing. When I looked in the logs in Auth0, they were claiming that the login was successful. I messed around in the code provided for the user-controller to print out some extra information where the error was happening. I was able to get a stack trace with something in lock9 - I tried to debug a bit in there, but it was pretty clear that by the time I hit a debug point it was deep into some error handling code and not too happy about it and basically had very little useful information.

I searched for Auth0 lock 9 and found the documentation on it https://auth0.com/docs/libraries/lock/v9. It immediately tells you that v9 is deprecated and you should switch to v11 immediately. In index.html I changed the lock script refernce to https://cdn.auth0.com/js/lock/11.3.1/lock.min.js and was able to log in via the Auth0 UI widget. I also confirmed in the logs on Auth0.com that it was a successful login.

This sounds great, but its not all roses - there is a pretty significant change to the way the code in lock 10 and 11 works vs v9. Even though I was able to log in successfully, none of the updates to the UI were being triggered. The login button isn't hidden, the logout button isn't shown, name and profile pic aren't shown etc. Essentially it looks like most of user-controller.js needs to be rewritten to use lock 11. The upside is that there seems to be some good documentation for this here https://auth0.com/docs/libraries/lock/v11

I haven't done this step yet and haven't moved past 5.2.6 yet so I don't know if this is a viable solution for future work. If I get a working user-controller.js that seems viable for lock 11 and the exercises moving forward I'll post it here.

I am really not enjoying using Auth0 and if it continues to be an out of date PITA I am going to be pretty frustrated and annoyed - I'm talking to you Manning and Sbarski. I wanted to do some learning on AWS Serverless and I'm doing more stuff with other technologies and their struggles than with what I wanted - that is crap and doesn't make me excited to buy another Manning book or another Sbarski book.</rant>
38786 (7) [Avatar] Offline
#2
Here is the user-controller I created to use for the rest of the book. You'll need to set an "audience" on line 34 to get JWT tokens back from Auth0. I never was able to get the Delegation Tokens in section 5.4 working, I think it might have been an issue with the API / client setup in Auth 0. Auth 0 was returning RS256 JWT tokens when I needed HS256 signed tokens to make things work with the code in the book and on AWS. After many hours of trying to debug that functionality I decided it wasn't worth the continued waste of time and moved on without that functionality.

There are log statements that are turned on in the controller that should give a bit more information about what is going on in case you are having trouble with the authentication.

Hopefully the controller is helpful if you are blocked here.
MacFlecknoe (17) [Avatar] Offline
#3
This is a show stopper for me. Can the author please supply updated source code??

I appreciate the previous user posting a partial solution but i cannot access his file.

Edit: I think i solved most of my issues. Here is the output of user-controller.js:

var userController = {
    data: {
        auth0Lock: null,
        config: null
    },
    uiElements: {
        loginButton: null,
        logoutButton: null,
        profileButton: null,
        profileNameLabel: null,
        profileImage: null
    },
    init: function (config) {

        var that = this;

        this.uiElements.loginButton = $('#auth0-login');
        this.uiElements.logoutButton = $('#auth0-logout');
        this.uiElements.profileButton = $('#user-profile');
        this.uiElements.profileNameLabel = $('#profilename');
        this.uiElements.profileImage = $('#profilepicture');

        this.data.config = config;

        this.data.auth0Lock = new Auth0Lock(config.auth0.clientId, config.auth0.domain, {
            auth: {
				responseType: 'id_token token',
                params: {
                    scope: config.auth0.scope,
					audience: config.auth0.audience,
					redirectUrl: "",
					responseType: "token"
                }
            }
        }); // params set in config.js
        this.data.auth0Lock.on("authenticated", function (authResult) {
            console.log("authenticated: ", authResult);
            that.retrieveProfileData(authResult.accessToken);
            localStorage.setItem('userToken', authResult.accessToken);
        });

        var idToken = localStorage.getItem('userToken');

        if (idToken) {
            this.retrieveProfileData(idToken);
        }
        this.wireEvents();
    },
    retrieveProfileData: function (accessToken) {
		
		console.log("retrieveProfileData: ", accessToken);
		
        var that = this;

        console.log("retrieving profile");
        this.configureAuthenticatedRequests();
        this.data.auth0Lock.getUserInfo(accessToken, function (err, profile) {
            if (err) {
                return alert('There was an error getting the profile: ' + err.message);
            }
            that.showUserAuthenticationDetails(profile);
        });
    },
    configureAuthenticatedRequests: function () {
        $.ajaxSetup({
            'beforeSend': function (xhr) {
				
				var token = localStorage.getItem('userToken');
				console.log("sending request with token: " + token);
                xhr.setRequestHeader('Authorization', 'Bearer ' + token);
            }
        });
    },
    showUserAuthenticationDetails: function (profile) {

		console.log("showUserAuthenticationDetails: ", profile);
		
        var showAuthenticationElements = !!profile; //coerce into a boolean (!!1 evalutes to true, !!0 evalutes to false)

        if (showAuthenticationElements) {
            this.uiElements.profileNameLabel.text(profile.nickname);
            this.uiElements.profileImage.attr('src', profile.picture);
        }
        this.uiElements.loginButton.toggle(!showAuthenticationElements);
        this.uiElements.logoutButton.toggle(showAuthenticationElements);
        this.uiElements.profileButton.toggle(showAuthenticationElements);
    },
    wireEvents: function () {

        var that = this;

        this.uiElements.loginButton.click(function (e) {
            console.log("show");
            that.data.auth0Lock.show();
        });
        this.uiElements.logoutButton.click(function (e) {

            localStorage.removeItem('userToken');

            that.uiElements.logoutButton.hide();
            that.uiElements.profileButton.hide();
            that.uiElements.loginButton.show();
        });
        this.uiElements.profileButton.click(function (e) {

            var url = that.data.config.apiBaseUrl + '/user-profile';

            $.get(url, function (data, status) {
                $('#user-profile-raw-json').text(JSON.stringify(data, null, 2));
                $('#user-profile-modal').modal();
            })
        });
    }
}
MacFlecknoe (17) [Avatar] Offline
#4
This might also help as other modifications needed to be made to other files. Here is the output from my UserProfileHandler.js class. I am not a Node.js developer so please forgive the errors that I am sure exist in the file. I uploaded my public key cert from Auth0 to an s3 bucket and used that to verify incoming tokens:

'use strict';

var AWS = require('aws-sdk');
var jwt = require('jsonwebtoken');
var request = require('request');
var s3 = new AWS.S3();

var verifyToken = function (token, secretOrPublicKey, verifyOptions, callback) {

    jwt.verify(token, secretOrPublicKey, verifyOptions, function (err, decoded) {

        if (err) {
            console.log('Failed jwt validation: ', err, 'auth: ', token);
            callback('Authorization Failed');
        } else {
            var headers = {
                'Authorization': "Bearer " + token
            };
            var options = {
                url: 'https://' + process.env.DOMAIN + '/userinfo',
                method: 'GET',
                json: true,
                headers: headers
            };
            request(options, function (error, response, body) {
                if (!error && response.statusCode === 200) {
                    callback(null, body);
                } else {
                    console.log("tokeninfoerror: ", error);
                    console.log("response.statusCode: ", response.statusCode);
                    callback(error);
                }
            });
        }
    });
};

exports.handler = function (event, context, callback) {

    if (!event.authToken) {
        callback('Could not find authToken');
        return;
    }
    var token = event.authToken.split(' ')[1]; // contains the word Bearer before the token

    if (!process.env.CLIENT_SECRET) {

        console.log("grabbing public key from s3");

        var params = {
            Bucket: process.env.PUBLIC_KEY_BUCKET_NAME,
            Key: process.env.PUBLIC_KEY_BUCKET_KEY
        };
        s3.getObject(params, function (s3err, data) {

            if (s3err) {
                console.log(s3err, s3err.stack);
                callback(s3err);

            } else {
                verifyToken(token, new Buffer(data.Body, 'binary'), { algorithm: 'RS256' }, callback);
            }
        });

    } else {
        console.log("using client secret");
        verifyToken(token, process.env.CLIENT_SECRET, { algorithm: 'HS256' }, callback);
    }
};