kraken-example-with-passport icon indicating copy to clipboard operation
kraken-example-with-passport copied to clipboard

Attempt to add OAuth2 strategy and fails

Open mikesparr opened this issue 9 years ago • 3 comments

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:

  • npm install the additional strategy module
  • add the strategy export to auth.js
  • change the spec.js to use the new strategy export from auth.js
  • add callback route with passport.authorize({some strategy name}, ...) that redirects to logged in view

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!

mikesparr avatar Jun 16 '15 01:06 mikesparr

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

aredridel avatar Jun 16 '15 01:06 aredridel

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:

  • user visits this demo app and clicks Login
  • login view embeds Auth0 JS widget for login
  • user inputs login credentials or selects FB or Google and Auth0 "wraps" provider, authenticates, and then hits callback
  • using Auth0 passport strategy .authenticate() as route middleware per their example and it appears to set user in session for app, but error appears on screen.

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");

    });

mikesparr avatar Jun 25 '15 19:06 mikesparr

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

aredridel avatar Jul 14 '15 12:07 aredridel