krakenjs / kraken-example-with-passport

An example integrating kraken with passport authentication
53 stars 33 forks source link

Attempt to add OAuth2 strategy and fails #12

Open mikesparr opened 9 years ago

mikesparr commented 9 years ago

Is there a way to extend this example to show a common OAuth2 strategy? I upgraded to the latest KrakenJS and rebuilt the controllers from pre 1.x version app, and changed auth.js and other /lib/ files like spec.js, crypto.js, user.js.

Now if I wanted to use an OAuth strategy I assumed all I would have to do was:

Every step of the way the app failed, complaining about one thing after another. I finally had to comment out all the spec.js and user.js mentions and create a new oauth.js lib and import in index.js main server file. It still failed complaining about not finding the strategy name.

I then imported passport in index.js and declared it to .use(new strategy export) and it got further and now I get the following errors:

Internal server error

The URL /auth/callback?code=zrmmdTI1j2QSP2Ht had the following error failed to obtain access token (status: 403 data: {"error":"invalid_grant","error_description":"Invalid authorization code"})

I then added express-session and cookie-parser thinking for some reason during redirects it was losing the code or token, but still same error. Is there any way to extend this app and show a working example of a popular OAuth strategy instead of just the Mongo-based local strategy so others can see what might be wrong or what missing pieces or NPM dependencies Kraken needs?

Thanks so much!

aredridel commented 9 years ago

Ooof. OAuth2 is a big bag of potential complication. what service are you testing against? What flow?

mikesparr commented 9 years ago

Sorry I didn't see you replied.

Using Auth0 (http://www.auth0.com) and their passport strategy: https://github.com/auth0/passport-auth0 and replaced the login form with a JS widget that authenticates against their service, and callback returns to KrakenJS-powered app. To test I just tweaked this example app and get same error.

If you had a working OAuth2 example and not just a local strategy, that would be helpful. FB or Google would be fine I reckon.

Flow:

I'm unsure what passport is trying to do when calling authenticate() to cause this 500 error.

I replaced the sample login form with Auth0 JS widget:

{>"layouts/master" /}

{<body}
    <div>
        <!-- <form id="loginForm" method="post"> -->
            <fieldset>
                <h2>Login</h2>
                {?messages}
                    <ul>
                        {#messages}
                            <li>{.}</li>
                        {/messages}

                    </ul>
                {/messages}
                <!--
                <table>
                    <tr>
                        <td><label for="username">Login: </label></td>
                        <td><input id="username" name="username" type="text"/></td>
                    </tr>
                    <tr>
                        <td><label for="password">Password: </label></td>
                        <td><input id="password" name="password" type="password"/></td>
                    </tr>
                    <tr>
                        <td>
                            <input type="submit" value="Login"/>
                            <input type="hidden" name="_csrf" value="{_csrf}"/>
                        </td>
                        <td></td>
                    </tr>
                </table>
                -->

                <div id="root" style="width: 280px; margin: 40px auto; padding: 10px; border-style: dashed; border-width: 1px;">
                    Custom Login
                </div>
                <script src="https://cdn.auth0.com/js/lock-7.1.min.js"></script>
                <script>
                  var lock = new Auth0Lock('**{{my app ID}}**', '**{{my app domain}}**');

                  lock.show({
                      container: 'root'
                    , callbackURL: 'http://localhost:8000/auth/callback'
                    , responseType: 'code'
                    , authParams: {
                      scope: 'openid profile'
                    }
                  });
                </script>

            </fieldset>
        <!-- </form> -->
    </div>
{/body}

I edited the auth.js in lib to export new strategy:

/**
 * Module that will handle our authentication tasks
 */
'use strict';

var User = require('../models/user'),
    LocalStrategy = require('passport-local').Strategy,
    Auth0Strategy = require('passport-auth0');

...

/**
 * A helper method to retrieve a user from a local DB and ensure that the provided password matches.
 * @param req
 * @param res
 * @param next
 */
exports.auth0Strategy = function() {

    return new Auth0Strategy({
        domain:       process.env['AUTH0_DOMAIN'],
        clientID:     process.env['AUTH0_CLIENT_ID'],
        clientSecret: process.env['AUTH0_CLIENT_SECRET'],
        callbackURL:  process.env['AUTH0_CALLBACK_URL'] || 'http://localhost:8000/auth/callback'
    }, function(accessToken, refreshToken, profile, done) {
        //Some tracing info
        console.log('profile is', profile);
        //save the profile
        return done(null, profile);
    });
};

I edited the spec.js file in lib to use the new strategy:

'use strict';

var express = require('express'),
    passport = require('passport'),
    auth = require('../lib/auth');
    //userLib = require('./user')(),
    //db = require('../lib/database'),
    //crypto = require('../lib/crypto');

module.exports = function spec(app) {
    app.on('middleware:after:session', function configPassport(eventargs) {
        //Tell passport to use our newly created local strategy for authentication
        passport.use(auth.auth0Strategy());
        //Give passport a way to serialize and deserialize a user. In this case, by the user's id.
        //passport.serializeUser(userLib.serialize);
        //passport.deserializeUser(userLib.deserialize);

        // you can use this section to keep a smaller payload
        passport.serializeUser(function(user, done) {
          done(null, user);
        });

        passport.deserializeUser(function(user, done) {
          done(null, user);
        });

        app.use(passport.initialize());
        app.use(passport.session());
    });
    return {
        onconfig: function(config, next) {

            //var dbConfig = config.get('databaseConfig'),
            //    cryptConfig = config.get('bcrypt');

            //crypto.setCryptLevel(cryptConfig.difficulty);
            //db.config(dbConfig);
            //userLib.addUsers();
            next(null, config);
        }
    };

};

I edited controllers/index.js to add the callback route, trying similar syntax to your local strategy example to no avail, then trying the middleware inline in route like Auth0 example, also to no avail.

    /**
     * Receive the login credentials and authenticate.
     * Successful authentications will go to /profile or if the user was trying to access a secured resource, the URL
     * that was originally requested.
     *
     * Failed authentications will go back to the login page with a helpful error message to be displayed.
     */
    router.get('/auth/callback', passport.authenticate('auth0'), function (req, res) {

        console.log(req);

        /* fails with unknown function
        passport.authenticate('auth0', {
            successRedirect: req.session.goingTo || '/profile',
            failureRedirect: '/login',
            failureFlash: true
        })(req, res);*/

        /* this with passport middleware fails with invalid auth grant */
        res.redirect("/profile");

    });
aredridel commented 9 years ago

And what error do you get in the console when the Internal Server Error happens?