From fcc980ddc696cddaa6472c7de8cef4ae6cc651f2 Mon Sep 17 00:00:00 2001 From: Julien Le Coupanec Date: Tue, 13 Feb 2018 22:43:45 +0100 Subject: [PATCH] Feathers: Authentication methods (Server, Client, Local, OAuth1, OAuth2) --- backend/feathers.js | 235 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 233 insertions(+), 2 deletions(-) diff --git a/backend/feathers.js b/backend/feathers.js index 5e59096..7f2aa57 100644 --- a/backend/feathers.js +++ b/backend/feathers.js @@ -23,6 +23,9 @@ npm install @feathersjs/feathers --save npm install @feathersjs/errors --save ``` +// --> APPLICATION <-- +// https://docs.feathersjs.com/api/application.html + const feathers = require('@feathersjs/feathers'); const app = feathers(); // application initialization @@ -42,6 +45,9 @@ app.on(eventname, listener) // registers a listener method for app.emit(eventname, data) // emits the event eventname to all event listeners app.removeListener(eventname, [ listener ]) // removes all or the given listener for eventname +// --> SERVICES <-- +// https://docs.feathersjs.com/api/services.html + // "Services" are the heart of every Feathers application. // Services are JavaScript objects (or instances of ES6 classes) that implement certain methods. // Feathers itself will also add some additional methods and functionality to its services. @@ -97,6 +103,9 @@ myService.once(eventname, listener) // registers a listener metho myService.emit(eventname, data) // emits the event eventname to all event listeners myService.removeListener(eventname, [ listener ]) // removes all listeners (or the given listener) for eventname +// --> HOOKS <-- +// https://docs.feathersjs.com/api/hooks.html + // Hooks are pluggable middleware functions that can be registered // before, after or on errors of a service method. You can register a // single hook function or create a chain of them to create complex work-flows. @@ -147,6 +156,9 @@ context.error // [writable] contains the error object that was thrown in a f context.result // [writable] contains the result of the successful service method call (only available in after hooks) context.dispatch // [writable and optional] contains a "safe" version of the data that should be sent to any client +// --> CHANNELS <-- +// https://docs.feathersjs.com/api/channels.html + // A channel is an object that contains a number of connections. // It can be created via app.channel and allows a connection to join or leave it. app.channel(name) // when given a single name, returns an existing or new named channel @@ -185,6 +197,7 @@ npm install @feathersjs/primus --save ``` // --> EXPRESS <-- +// https://docs.feathersjs.com/api/express.html const feathers = require('@feathersjs/feathers'); const express = require('@feathersjs/express'); @@ -208,6 +221,7 @@ express.errorHandler() // middleware that formats any error response t app.use(express.errorHandler()) // set up the error handler with the default configuration // --> SOCKET.IO <-- +// https://docs.feathersjs.com/api/client/socketio.html const feathers = require('@feathersjs/feathers'); const socketio = require('@feathersjs/socketio'); @@ -227,6 +241,7 @@ app.configure(socketio({ })); // --> PRIMUS <-- +// https://docs.feathersjs.com/api/client/primus.html app.configure(primus(options)) // sets up the Primus transport with the given Primus options app.configure(primus(options, callback)) // sets up the Primus transport with the given Primus options and calls the callback with the Primus server instance @@ -256,6 +271,7 @@ npm install @feathersjs/authentication-client --save ``` // --> REST CLIENT <-- +// https://docs.feathersjs.com/api/client/rest.html rest([baseUrl]) // Initialize a client object with a base URL @@ -266,11 +282,13 @@ app.configure(restClient.axios(axios)); // connect to a service using app.configure(restClient.fetch(window.fetch)); // connect to a service using Fetch // --> SOCKET.IO <-- +// https://docs.feathersjs.com/api/client/socketio.html socketio(socket) // initialize the Socket.io client using a given socket and the default options socketio(socket, options) // initialize the Socket.io client using a given socket and the given options // --> PRISMUS <-- +// https://docs.feathersjs.com/api/client/primus.html primus(socket) // initialize the Primus client using a given socket and the default options primus(socket, options) // initialize the Primus client using a given socket and the given options @@ -298,7 +316,8 @@ npm install @feathersjs/authentication-oauth1 --save npm install @feathersjs/authentication-oauth2 --save ``` -app.configure(auth(options)) // configure the authentication plugin with the given options +// --> SERVER <-- +// https://docs.feathersjs.com/api/authentication/server.html options = { path: '/authentication', // the authentication service path @@ -323,7 +342,219 @@ options = { } } -app.service('authentication') // +app.configure(auth(options)) // configure the authentication plugin with the given options + +service = app.service('authentication') // service for creating JWT (implements only the create and remove methods) + +service.create(data) // creates a JWT based on the jwt options configured on the plugin +service.remove(data) // removes the JWT token (mostly exists for the logout process) +service.hooks({ before }) // registers hooks for this service +service.hooks({ after }) // registers hooks for this service + +app.passport.createJWT(payload, options) // used by the authentication service to generate JSON Web Tokens +app.passport.verifyJWT(token, options) // verifies the signature and payload of the passed in JWT token using the options + +auth.hooks.authenticate(strategies) // registers an array of authentication strategies on a service method + +app.on('login', callback)) // emits an event whenever a client successfully authenticates +app.on('logout', callback)) // emits an event whenever a client successfully logout + +// --> CLIENT <-- +// https://docs.feathersjs.com/api/authentication/client.html + +options = { + path: '/authentication', // the server-side authentication service path + header: 'Authorization', // the default authorization header for REST + jwtStrategy: 'jwt', // the name of the JWT authentication strategy + entity: 'user', // the entity you are authenticating (ie. a users) + service: 'users', // the service to look up the entity + cookie: 'feathers-jwt', // the name of the cookie to parse the JWT from when cookies are enabled server side + storageKey: 'feathers-jwt', // the key to store the accessToken in localstorage or AsyncStorage on React Native + storage: undefined // Passing a WebStorage-compatible object to enable automatic storage on the client. +} + +app.configure(auth(options)) // configure the authentication plugin with the given options + +app.authenticate() // authenticates using the JWT from the storage +app.authenticate(options) // authenticate with a Feathers server by passing a strategy and other properties as credentials +app.logout() // removes the JWT accessToken from storage on the client + +app.passport.getJWT() // pulls the JWT from storage or the cookie +app.passport.verifyJWT(token) // verifies that a JWT is not expired and decode it to get the payload +app.passport.payloadIsValid(token) // synchronously verify that a token has not expired + +app.on('authenticated', callback)) // emits an event whenever the client successfully authenticates +app.on('logout', callback)) // emits an event whenever the client successfully authenticates +app.on('reauthentication-error', errorHandler) // will automatically handle attempting to re-authenticate the socket when the client regains connectivity with the server + +// --> LOCAL (EMAIL & PASSWORD) <-- +// https://docs.feathersjs.com/api/authentication/local.html + +const feathers = require('@feathersjs/feathers'); +const authentication = require('@feathersjs/authentication'); +const local = require('@feathersjs/authentication-local'); +const app = feathers(); + +options = { + name: 'local', // the name to use when invoking the authentication Strategy + entity: 'user', // the entity that you're comparing username/password against + service: 'users', // the service to look up the entity + usernameField: 'email', // key name of username field + passwordField: 'password', // key name of password field + passReqToCallback: true, // whether the request object should be passed to `verify` + session: false, // whether to use sessions + Verifier: Verifier // A Verifier class. Defaults to the built-in one but can be a custom one +} + +app.configure(authentication(options)); +app.configure(local()); + +// Setup a hook to only allow valid JWTs or successful +// local auth to authenticate and get new JWT access tokens +app.service('authentication').hooks({ + before: { + create: [ + authentication.hooks.authenticate(['local', 'jwt']) + ] + } +}); + +local.hooks.hashPassword() // hashes plain text passwords before they are saved to the database +local.hooks.protect('password') // makes sure that protected fields don't get sent to a client + +// --> JWT <-- +// https://docs.feathersjs.com/api/authentication/jwt.html + +const feathers = require('@feathersjs/feathers'); +const authentication = require('@feathersjs/authentication'); +const jwt = require('@feathersjs/authentication-jwt'); +const app = feathers(); + +options = { + name: 'jwt', // the name to use when invoking the authentication Strategy + entity: 'user', // the entity that you pull from if an 'id' is present in the payload + service: 'users', // the service to look up the entity + passReqToCallback: true, // whether the request object should be passed to `verify` + jwtFromRequest: [ // a passport-jwt option determining where to parse the JWT + ExtractJwt.fromHeader, // From "Authorization" header + ExtractJwt.fromAuthHeaderWithScheme('Bearer'), // Allowing "Bearer" prefix + ExtractJwt.fromBodyField('body') // from request body + ], + secretOrKey: auth.secret, // Your main secret provided to passport-jwt + session: false, // whether to use sessions, + Verifier: Verifier // A Verifier class. Defaults to the built-in one but can be a custom one. See below for details. +} + +app.configure(authentication(options)); +app.configure(jwt()); + +// Setup a hook to only allow valid JWTs to authenticate +// and get new JWT access tokens +app.service('authentication').hooks({ + before: { + create: [ + authentication.hooks.authenticate(['jwt']) + ] + } +}); + +// --> OAUTH1 <-- +// https://docs.feathersjs.com/api/authentication/oauth1.html + +const feathers = require('@feathersjs/feathers'); +const authentication = require('@feathersjs/authentication'); +const jwt = require('@feathersjs/authentication-jwt'); +const oauth1 = require('@feathersjs/authentication-oauth1'); + +const session = require('express-session'); +const TwitterStrategy = require('passport-twitter').Strategy; +const app = feathers(); + +// Setup in memory session +app.use(session({ + secret: 'super secret', + resave: true, + saveUninitialized: true +})); + +options = { + idField: 'Id', // The field to look up the entity by when logging in with the provider. Defaults to 'Id' (ie. 'twitterId'). + path: '/auth/', // The route to register the middleware + callbackURL: 'http(s)://hostame[:port]/auth//callback', // The callback url. Will automatically take into account your host and port and whether you are in production based on your app environment to construct the url. (ie. in development http://localhost:3030/auth/twitter/callback) + entity: 'user', // the entity that you are looking up + service: 'users', // the service to look up the entity + passReqToCallback: true, // whether the request object should be passed to `verify` + session: true, // whether to use sessions, + handler: fn, // Express middleware for handling the oauth callback. Defaults to the built in middleware. + formatter: fn, // The response formatter. Defaults the the built in feathers-rest formatter, which returns JSON. + Verifier: Verifier // A Verifier class. Defaults to the built-in one but can be a custom one. See below for details. +} + +// Setup authentication +app.configure(authentication(options)); +app.configure(jwt()); +app.configure(oauth1({ + name: 'twitter', + Strategy: TwitterStrategy, + consumerKey: '', + consumerSecret: '' +})); + +// Setup a hook to only allow valid JWTs to authenticate +// and get new JWT access tokens +app.service('authentication').hooks({ + before: { + create: [ + authentication.hooks.authenticate(['jwt']) + ] + } +}); + +// --> OAUTH 2 <-- +// https://docs.feathersjs.com/api/authentication/oauth2.html + +const feathers = require('@feathersjs/feathers'); +const authentication = require('@feathersjs/authentication'); +const jwt = require('@feathersjs/authentication-jwt'); +const oauth2 = require('@feathersjs/authentication-oauth2'); +const FacebookStrategy = require('passport-facebook').Strategy; +const app = feathers(); + +options = { + idField: 'Id', // The field to look up the entity by when logging in with the provider. Defaults to 'Id' (ie. 'facebookId'). + path: '/auth/', // The route to register the middleware + callbackURL: 'http(s)://hostname[:port]/auth//callback', // The callback url. Will automatically take into account your host and port and whether you are in production based on your app environment to construct the url. (ie. in development http://localhost:3030/auth/facebook/callback) + successRedirect: undefined, + failureRedirect: undefined, + entity: 'user', // the entity that you are looking up + service: 'users', // the service to look up the entity + passReqToCallback: true, // whether the request object should be passed to `verify` + session: false, // whether to use sessions, + handler: fn, // Express middleware for handling the oauth callback. Defaults to the built in middleware. + formatter: fn, // The response formatter. Defaults the the built in feathers-rest formatter, which returns JSON. + Verifier: Verifier // A Verifier class. Defaults to the built-in one but can be a custom one. See below for details. +} + +// Setup authentication +app.configure(authentication({ secret: 'super secret' })); +app.configure(jwt()); +app.configure(oauth2({ + name: 'facebook', + Strategy: FacebookStrategy, + clientID: '', + clientSecret: '', + scope: ['public_profile', 'email'] +})); + +// Setup a hook to only allow valid JWTs to authenticate +// and get new JWT access tokens +app.service('authentication').hooks({ + before: { + create: [ + authentication.hooks.authenticate(['jwt']) + ] + } +}); /* *******************************************************************************************