简体   繁体   中英

Hapi.js API Authentication

I am trying to use hapi and the passport-local strategy. First I am trying to get 'static' users to work then I plan on pushing user info into a database. What I have below (as well as at https://github.com/RyanHirsch/hapi-auth-poc ) will authenticate a user hitting the website as expected, but I can't figure out how to properly authenticate API requests. For testing I am simply trying to use cURL to send the GET along with username/password and not getting a successful login.

Where am I going wrong? How do I allow credential handling on API requests using hapi and passport?

var Hapi = require('hapi');
var LocalStrategy = require('passport-local').Strategy;

var config = {
    hostname: 'localhost',
    port: 8000,
    urls: {
        failureRedirect: '/login'
    },
    excludePaths: ['/public/']
};
var plugins = {
    yar: {
        cookieOptions: {
            password: "worldofwalmart",
            isSecure: false
        }
    },
    travelogue: config // use '../../' instead of travelogue if testing this repo locally
}

var server = new Hapi.Server(config.hostname, config.port);
server.pack.require(plugins, function (err) {
    if (err) {
        throw err;
    }
});

server.auth.strategy('passport', 'passport');

var USERS = {
    "van": "walmart"
};

var Passport = server.plugins.travelogue.passport;
Passport.use(new LocalStrategy(function (username, password, done) {

    // Find or create user here...
    // In production, use password hashing like bcrypt
    if (USERS.hasOwnProperty(username) && USERS[username] == password) {
        return done(null, { username: username });
    }

    return done(null, false, { 'message': 'invalid credentials' });
}));
Passport.serializeUser(function (user, done) {

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

    done(null, obj);
});

// routes
server.route({
    method: 'GET',
    path: '/',
    config: { auth: 'passport' }, // replaces ensureAuthenticated
    handler: function (request, reply) {

        // If logged in already, redirect to /home
        // else to /login
        reply().redirect('/home');
    }
});

server.route({
    method: 'GET',
    path: '/login',
    config: {
        handler: function (request, reply) {
            if (request.session._isAuthenticated()) {
                reply().redirect('/home');
            } else {
                var form = '<form action="/login" method="post"> <div> <label>Username:</label> <input type="text" name="username"/> </div> <div> <label>Password:</label> <input type="password" name="password"/> </div> <div> <input type="submit" value="Log In"/> </div> </form>';
                reply(form);
            }
        }
    }
});


server.route({
    method: 'GET',
    path: '/home',
    config: { auth: 'passport' },
    handler: function (request, reply) {
        // If logged in already, redirect to /home
        // else to /login
        reply("ACCESS GRANTED<br/><br/><a href='/logout'>Logout</a>");
    }
});

server.route({
    method: 'GET',
    path: '/api/home',
    config: {
        validate: {
            payload: {
                username: Hapi.types.String(),
                password: Hapi.types.String()
            }
        },
        auth: false,
        handler: function (request, reply) {
            // If logged in already, redirect to /home
            // else to /login
            Passport.authenticate('local')(request, function (err) {
                console.log("successful authentication?");
                if (err && err.isBoom) {}
                if(request.session._isAuthenticated()) {
                    reply({message: "logged in"});
                }
            });
            // reply({ "working" : "success" });
        }
    }
});

server.start(function () {
    console.log('server started on port: ', server.info.port);
});

You try to authenticated your request using username and password that are provided via the payload but have configured your action to use GET. What happens is that Hapi ignores your parameters and therefore your request fails.

Just use POST instead.

As a side note, using this method will make your api depended on cookies and sessions, I'd prefer to use "token" validation and create a stateless service. I think passport-bearer would be a good option for you.

Simpler Solution: hapi-login ( plugin )

We found the Passport Local Strategy to be a bit convoluted so we created a simpler option : https://github.com/dwyl/ hapi-login
It's a Hapi Plugin which creates a /login endpoint where you can POST a username ( or email ) and password and authenticate people to your app/website.

We created a step-by-step example for how to use the plugin:
https://github.com/dwyl/hapi-login- example with end-to-end tests
and a demo UI: https://hapi-login.herokuapp.com/ with JOI validation.

You will be up-and-running in 5 minutes .

There is also a complimentary ( but separate ) registration plugin :
https://github.com/dwyl/hapi-register

We use these in our Hapi apps and maintain them so you don't have to!
建立状态 codecov.io 代码气候 依赖状态 devDependency状态

If you have any questions or feedback, get in touch !

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM