feathers-authentication
Advanced tools
Comparing version 0.8.0-beta-1 to 0.8.0-beta-2
@@ -25,3 +25,3 @@ 'use strict'; | ||
function populateHeader() { | ||
var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; | ||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
@@ -28,0 +28,0 @@ return function (hook) { |
@@ -8,5 +8,5 @@ 'use strict'; | ||
exports.default = function () { | ||
var opts = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; | ||
var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
var config = Object.assign({}, defaults, opts); | ||
var options = Object.assign({}, defaults, opts); | ||
@@ -16,82 +16,6 @@ return function () { | ||
if (!app.get('storage')) { | ||
var storage = (0, _utils.getStorage)(config.storage); | ||
app.set('storage', storage); | ||
} | ||
app.authentication = new _authentication2.default(app, options); | ||
app.authenticate = app.authentication.authenticate.bind(app.authentication); | ||
app.logout = app.authentication.logout.bind(app.authentication); | ||
// load any pre-existing JWT from localStorage | ||
(0, _utils.getJWT)(config.tokenKey, config.cookie, app.get('storage')).then(function (token) { | ||
app.set('token', token); | ||
app.get('storage').setItem(config.tokenKey, token); | ||
}); | ||
app.authenticate = function () { | ||
var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; | ||
var getOptions = Promise.resolve(options); | ||
// If no type was given let's try to authenticate with a stored JWT | ||
if (!options.type) { | ||
getOptions = (0, _utils.getJWT)(config.tokenKey, config.cookie, app.get('storage')).then(function (token) { | ||
if (!token) { | ||
return Promise.reject(new _feathersErrors2.default.NotAuthenticated('Could not find stored JWT and no authentication type was given')); | ||
} | ||
return { type: 'token', token: token }; | ||
}); | ||
} | ||
var handleResponse = function handleResponse(response) { | ||
app.set('token', response.token); | ||
app.set('user', response.user); | ||
return Promise.resolve(app.get('storage').setItem(config.tokenKey, response.token)).then(function () { | ||
return response; | ||
}); | ||
}; | ||
return getOptions.then(function (options) { | ||
var endPoint = void 0; | ||
if (options.type === 'local') { | ||
endPoint = config.localEndpoint; | ||
} else if (options.type === 'token') { | ||
endPoint = config.tokenEndpoint; | ||
} else { | ||
throw new Error('Unsupported authentication \'type\': ' + options.type); | ||
} | ||
return (0, _utils.connected)(app).then(function (socket) { | ||
// TODO (EK): Handle OAuth logins | ||
// If we are using a REST client | ||
if (app.rest) { | ||
return app.service(endPoint).create(options).then(handleResponse); | ||
} | ||
var method = app.io ? 'emit' : 'send'; | ||
return (0, _utils.authenticateSocket)(options, socket, method).then(handleResponse); | ||
}); | ||
}); | ||
}; | ||
// Set our logout method with the correct socket context | ||
app.logout = function () { | ||
app.set('user', null); | ||
app.set('token', null); | ||
(0, _utils.clearCookie)(config.cookie); | ||
// remove the token from localStorage | ||
return Promise.resolve(app.get('storage').removeItem(config.tokenKey)).then(function () { | ||
// If using sockets de-authenticate the socket | ||
if (app.io || app.primus) { | ||
var method = app.io ? 'emit' : 'send'; | ||
var socket = app.io ? app.io : app.primus; | ||
return (0, _utils.logoutSocket)(socket, method); | ||
} | ||
}); | ||
}; | ||
// Set up hook that adds token and user to params so that | ||
@@ -110,3 +34,3 @@ // it they can be accessed by client side hooks and services | ||
app.mixins.push(function (service) { | ||
service.before(hooks.populateHeader(config)); | ||
service.before(hooks.populateHeader(options)); | ||
}); | ||
@@ -117,6 +41,2 @@ } | ||
var _feathersErrors = require('feathers-errors'); | ||
var _feathersErrors2 = _interopRequireDefault(_feathersErrors); | ||
var _hooks = require('./hooks'); | ||
@@ -126,8 +46,10 @@ | ||
var _utils = require('./utils'); | ||
var _authentication = require('./authentication'); | ||
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } | ||
var _authentication2 = _interopRequireDefault(_authentication); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } | ||
var defaults = { | ||
@@ -134,0 +56,0 @@ cookie: 'feathers-jwt', |
@@ -11,4 +11,13 @@ 'use strict'; | ||
exports.clearCookie = clearCookie; | ||
exports.getJWT = getJWT; | ||
exports.verifyJWT = verifyJWT; | ||
exports.payloadIsValid = payloadIsValid; | ||
exports.retrieveJWT = retrieveJWT; | ||
exports.getStorage = getStorage; | ||
var _jwtDecode = require('jwt-decode'); | ||
var _jwtDecode2 = _interopRequireDefault(_jwtDecode); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
// Returns a promise that resolves when the socket is connected | ||
@@ -90,12 +99,36 @@ function connected(app) { | ||
// Tries the JWT from the given key either from a storage or the cookie | ||
function getJWT(tokenKey, cookieKey, storage) { | ||
// Pass a jwt token, get back a payload if it's valid. | ||
function verifyJWT(data) { | ||
return new Promise(function (resolve, reject) { | ||
var token = typeof data === 'string' ? data : data.token; | ||
if (token) { | ||
try { | ||
var payload = (0, _jwtDecode2.default)(token); | ||
if (payloadIsValid(payload)) { | ||
resolve(payload); | ||
} else { | ||
reject(new Error('Invalid token: expired')); | ||
} | ||
} catch (error) { | ||
reject(new Error('Cannot decode malformed token.')); | ||
} | ||
} else { | ||
reject(new Error('No token provided to verifyJWT')); | ||
} | ||
}); | ||
} | ||
// Pass a decoded payload and it will return a boolean based on if it hasn't expired. | ||
function payloadIsValid(payload) { | ||
return payload && payload.exp * 1000 > new Date().getTime(); | ||
} | ||
// Tries the JWT from the given key either from a storage or the cookie. | ||
function retrieveJWT(tokenKey, cookieKey, storage) { | ||
return Promise.resolve(storage.getItem(tokenKey)).then(function (jwt) { | ||
var cookieToken = getCookie(cookieKey); | ||
if (cookieToken) { | ||
return cookieToken; | ||
var token = jwt || getCookie(cookieKey); | ||
if (token && token !== 'null' && !payloadIsValid((0, _jwtDecode2.default)(token))) { | ||
token = undefined; | ||
} | ||
return jwt; | ||
return token; | ||
}); | ||
@@ -102,0 +135,0 @@ } |
@@ -27,3 +27,3 @@ 'use strict'; | ||
function associateAuthenticated() { | ||
var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; | ||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
@@ -30,0 +30,0 @@ return function (hook) { |
@@ -21,3 +21,3 @@ 'use strict'; | ||
function hashPassword() { | ||
var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; | ||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
@@ -24,0 +24,0 @@ return function (hook) { |
@@ -27,6 +27,2 @@ 'use strict'; | ||
var _isPermitted = require('./is-permitted'); | ||
var _isPermitted2 = _interopRequireDefault(_isPermitted); | ||
var _parseToken = require('./parse-token'); | ||
@@ -36,6 +32,2 @@ | ||
var _checkPermissions = require('./check-permissions'); | ||
var _checkPermissions2 = _interopRequireDefault(_checkPermissions); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
@@ -49,5 +41,3 @@ | ||
isAuthenticated: _isAuthenticated2.default, | ||
isPermitted: _isPermitted2.default, | ||
parseToken: _parseToken2.default, | ||
checkPermissions: _checkPermissions2.default | ||
parseToken: _parseToken2.default | ||
}; | ||
@@ -54,0 +44,0 @@ |
@@ -25,3 +25,3 @@ 'use strict'; | ||
function loadAuthenticated() { | ||
var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; | ||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
@@ -28,0 +28,0 @@ return function (hook) { |
@@ -21,3 +21,3 @@ 'use strict'; | ||
function parseToken() { | ||
var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; | ||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
@@ -24,0 +24,0 @@ return function (hook) { |
@@ -21,3 +21,3 @@ 'use strict'; | ||
function queryWithAuthenticated() { | ||
var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; | ||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
@@ -24,0 +24,0 @@ return function (hook) { |
101
lib/index.js
@@ -6,3 +6,3 @@ 'use strict'; | ||
}); | ||
exports.default = auth; | ||
exports.default = init; | ||
@@ -17,6 +17,2 @@ var _debug = require('debug'); | ||
var _lodash = require('lodash.merge'); | ||
var _lodash2 = _interopRequireDefault(_lodash); | ||
var _hooks = require('./hooks'); | ||
@@ -42,2 +38,10 @@ | ||
var _options = require('./options'); | ||
var _options2 = _interopRequireDefault(_options); | ||
var _base = require('./base'); | ||
var _base2 = _interopRequireDefault(_base); | ||
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } | ||
@@ -49,57 +53,11 @@ | ||
// Options that apply to any provider | ||
// Exposed modules | ||
function init() { | ||
var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
var middleware = []; | ||
// Exposed modules | ||
var defaults = { | ||
header: 'Authorization', | ||
setupMiddleware: true, // optional - to setup middleware yourself set to false. | ||
cookie: { // Used for redirects, server side rendering and OAuth | ||
enabled: false, // Set to true to enable all cookies | ||
name: 'feathers-jwt', | ||
httpOnly: true, | ||
maxAge: '1d', | ||
secure: true | ||
}, | ||
token: { | ||
name: 'token', // optional | ||
service: '/auth/token', // optional string or Service | ||
subject: 'auth', // optional | ||
issuer: 'feathers', // optional | ||
algorithm: 'HS256', // optional | ||
expiresIn: '1d', // optional | ||
secret: null, // required | ||
successRedirect: null, // optional - no default. If set the default success handler will redirect to location | ||
failureRedirect: null, // optional - no default. If set the default success handler will redirect to location | ||
successHandler: null // optional - a middleware to handle things once authentication succeeds | ||
}, | ||
local: { | ||
service: '/auth/local', // optional string or Service | ||
successRedirect: null, // optional - no default. If set the default success handler will redirect to location | ||
failureRedirect: null, // optional - no default. If set the default success handler will redirect to location | ||
successHandler: null, // optional - a middleware to handle things once authentication succeeds | ||
passReqToCallback: true, // optional - whether request should be passed to callback | ||
session: false // optional - whether we should use a session | ||
}, | ||
user: { | ||
service: '/users', // optional string or Service | ||
idField: '_id', // optional | ||
usernameField: 'email', // optional | ||
passwordField: 'password' // optional | ||
}, | ||
oauth2: { | ||
// service: '/auth/facebook', // required - the service path or initialized service | ||
passReqToCallback: true, // optional - whether request should be passed to callback | ||
// callbackUrl: 'callback', // optional - the callback url, by default this gets set to /<service>/callback | ||
permissions: { | ||
state: true, | ||
session: false | ||
} | ||
} | ||
}; | ||
function authentication() { | ||
var _app$authentication; | ||
function auth() { | ||
var config = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; | ||
return function () { | ||
var app = this; | ||
@@ -109,3 +67,3 @@ var _super = app.setup; | ||
// Merge and flatten options | ||
var authOptions = (0, _lodash2.default)({}, defaults, app.get('auth'), config); | ||
var authOptions = (0, _options2.default)(app.get('auth'), config); | ||
@@ -115,4 +73,4 @@ // NOTE (EK): Currently we require token based auth so | ||
// provider then we'll set up a sane default for them. | ||
if (!authOptions.token.secret) { | ||
throw new Error('You must provide a token secret in your config via \'auth.token.secret\'.'); | ||
if (!authOptions.secret && !authOptions.token.secret) { | ||
throw new Error('You must provide a \'secret\' in your authentication configuration'); | ||
} | ||
@@ -142,3 +100,3 @@ | ||
app.use(mw.tokenParser(authOptions)); | ||
// Verify and decode a JWT if it is present | ||
// Verify and decode a JWT if it is present | ||
app.use(mw.verifyToken(authOptions)); | ||
@@ -177,11 +135,22 @@ // Make the Passport user available for REST services. | ||
}; | ||
app.authentication = new _base2.default(app, authOptions); | ||
(_app$authentication = app.authentication).use.apply(_app$authentication, middleware); | ||
} | ||
authentication.use = function () { | ||
middleware.push.apply(middleware, arguments); | ||
return authentication; | ||
}; | ||
return authentication; | ||
} | ||
// Exposed Modules | ||
auth.hooks = _hooks2.default; | ||
auth.middleware = mw; | ||
auth.LocalService = _local2.default; | ||
auth.TokenService = _token2.default; | ||
auth.OAuth2Service = _oauth2.default; | ||
init.hooks = _hooks2.default; | ||
init.middleware = mw; | ||
init.LocalService = _local2.default; | ||
init.TokenService = _token2.default; | ||
init.OAuth2Service = _oauth2.default; | ||
module.exports = exports['default']; |
@@ -45,10 +45,2 @@ 'use strict'; | ||
var _isPermitted = require('./rest/is-permitted'); | ||
var _isPermitted2 = _interopRequireDefault(_isPermitted); | ||
var _checkPermissions = require('./rest/check-permissions'); | ||
var _checkPermissions2 = _interopRequireDefault(_checkPermissions); | ||
var _logout = require('./rest/logout'); | ||
@@ -71,4 +63,2 @@ | ||
isAuthenticated: _isAuthenticated2.default, | ||
isPermitted: _isPermitted2.default, | ||
checkPermissions: _checkPermissions2.default, | ||
logout: _logout2.default, | ||
@@ -75,0 +65,0 @@ setupSocketIOAuthentication: _sockets.setupSocketIOAuthentication, |
@@ -17,3 +17,3 @@ 'use strict'; | ||
function logout() { | ||
var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; | ||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
@@ -20,0 +20,0 @@ debug('Registering logout middleware'); |
@@ -17,3 +17,3 @@ 'use strict'; | ||
function notAuthenticated() { | ||
var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; | ||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
@@ -20,0 +20,0 @@ debug('Registering notAuthenticated middleware'); |
@@ -23,3 +23,3 @@ 'use strict'; | ||
function populateUser() { | ||
var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; | ||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
@@ -26,0 +26,0 @@ debug('Registering populateUser middleware'); |
@@ -21,6 +21,10 @@ 'use strict'; | ||
function setCookie() { | ||
var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; | ||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
debug('Registering setCookie middleware'); | ||
function makeExpiry(timeframe) { | ||
return new Date(Date.now() + (0, _ms2.default)(timeframe)); | ||
} | ||
return function (req, res, next) { | ||
@@ -63,6 +67,10 @@ var app = req.app; | ||
if (options.expires === undefined && options.maxAge) { | ||
var expiry = new Date(Date.now() + (0, _ms2.default)(options.maxAge)); | ||
options.expires = expiry; | ||
options.expires = makeExpiry(options.maxAge); | ||
} | ||
// By default, the cookie will expire with the token. | ||
if (options.expires === undefined && options.maxAge === undefined) { | ||
options.expires = makeExpiry(options.token.expiresIn); | ||
} | ||
if (options.expires && !(options.expires instanceof Date)) { | ||
@@ -69,0 +77,0 @@ throw new Error('cookie.expires must be a valid Date object'); |
@@ -17,3 +17,3 @@ 'use strict'; | ||
function successRedirect() { | ||
var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; | ||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
@@ -20,0 +20,0 @@ debug('Registering successRedirect middleware'); |
@@ -25,3 +25,3 @@ 'use strict'; | ||
function tokenParser() { | ||
var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; | ||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
@@ -28,0 +28,0 @@ debug('Registering tokenParser middleware'); |
@@ -20,3 +20,3 @@ 'use strict'; | ||
module.exports = function verifyToken() { | ||
var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; | ||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
@@ -23,0 +23,0 @@ debug('Registering verifyToken middleware'); |
@@ -89,3 +89,3 @@ 'use strict'; | ||
socket.on('logout', function () { | ||
var callback = arguments.length <= 0 || arguments[0] === undefined ? function () {} : arguments[0]; | ||
var callback = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : function () {}; | ||
@@ -119,3 +119,3 @@ // TODO (EK): Blacklist token | ||
function setupSocketIOAuthentication(app) { | ||
var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; | ||
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
@@ -130,3 +130,3 @@ debug('Setting up Socket.io authentication middleware with options:', options); | ||
function setupPrimusAuthentication(app) { | ||
var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; | ||
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
@@ -133,0 +133,0 @@ debug('Setting up Primus authentication middleware with options:', options); |
@@ -46,3 +46,3 @@ 'use strict'; | ||
function LocalService() { | ||
var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; | ||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
@@ -49,0 +49,0 @@ _classCallCheck(this, LocalService); |
@@ -10,2 +10,3 @@ 'use strict'; | ||
exports.normalizeCallbackURL = normalizeCallbackURL; | ||
exports.default = init; | ||
@@ -31,2 +32,8 @@ | ||
var _url = require('url'); | ||
var _url2 = _interopRequireDefault(_url); | ||
var _feathersCommons = require('feathers-commons'); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
@@ -42,3 +49,3 @@ | ||
function OAuth2Service() { | ||
var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; | ||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
@@ -78,2 +85,3 @@ _classCallCheck(this, OAuth2Service); | ||
var id = user[idField]; | ||
var userService = this.getUserService(); | ||
@@ -86,3 +94,3 @@ debug('Updating user: ' + id); | ||
// TODO (EK): Handle paginated services? | ||
return this._userService.patch(id, data, { oauth: true }); | ||
return userService.patch(id, data, { oauth: true }); | ||
} | ||
@@ -94,5 +102,6 @@ }, { | ||
var id = data[provider + 'Id']; | ||
var userService = this.getUserService(); | ||
debug('Creating new user with ' + provider + 'Id: ' + id); | ||
return this._userService.create(data, { oauth: true }); | ||
return userService.create(data, { oauth: true }); | ||
} | ||
@@ -107,5 +116,6 @@ }, { | ||
var query = _defineProperty({}, options.provider + 'Id', profile.id); | ||
var userService = this.getUserService(); | ||
// Find or create the user since they could have signed up via facebook. | ||
this._userService.find({ query: query }).then(this.getFirstUser).then(function (user) { | ||
userService.find({ query: query }).then(this.getFirstUser).then(function (user) { | ||
var _Object$assign; | ||
@@ -132,2 +142,12 @@ | ||
} | ||
}, { | ||
key: 'getUserService', | ||
value: function getUserService() { | ||
return typeof this._userService === 'string' ? this.app.service(this._userService) : this._userService; | ||
} | ||
}, { | ||
key: 'getTokenService', | ||
value: function getTokenService() { | ||
return typeof this._tokenService === 'string' ? this.app.service(this._tokenService) : this._tokenService; | ||
} | ||
@@ -148,5 +168,8 @@ // GET /auth/facebook | ||
value: function get(id, params) { | ||
// Make sure the provider plugin name doesn't overwrite the OAuth provider name. | ||
delete params.provider; | ||
var tokenService = this.getTokenService(); | ||
var options = Object.assign({}, this.options, params); | ||
if (options.service + '/' + id !== options.callbackUrl) { | ||
if ('/' + (0, _feathersCommons.stripSlashes)(options.service) + '/' + id !== options.callbackURL) { | ||
return Promise.reject(new _feathersErrors2.default.NotFound()); | ||
@@ -170,3 +193,3 @@ } | ||
// Get a new JWT and the associated user from the Auth token service and send it back to the client. | ||
return this._tokenService.create(tokenPayload, { user: user }).then(resolve).catch(reject); | ||
return tokenService.create(tokenPayload, { user: user }).then(resolve).catch(reject); | ||
}); | ||
@@ -185,2 +208,3 @@ | ||
var options = this.options; | ||
var tokenService = this.getTokenService(); | ||
@@ -206,4 +230,4 @@ if (!options.tokenStrategy) { | ||
// Get a new JWT and the associated user from the Auth token service and send it back to the client. | ||
return this._tokenService.create(tokenPayload, { user: user }).then(resolve).catch(reject); | ||
}); | ||
return tokenService.create(tokenPayload, { user: user }).then(resolve).catch(reject); | ||
}).bind(this); | ||
@@ -220,8 +244,5 @@ middleware(params.req, params.res); | ||
var tokenService = this.options.token.service; | ||
var userService = this.options.user.service; | ||
this._tokenService = this.options.token.service; | ||
this._userService = this.options.user.service; | ||
this._tokenService = typeof tokenService === 'string' ? app.service(tokenService) : tokenService; | ||
this._userService = typeof userService === 'string' ? app.service(userService) : userService; | ||
// Register our Passport auth strategy and get it to use our passport callback function | ||
@@ -251,2 +272,19 @@ var Strategy = this.options.strategy; | ||
/* | ||
* Make sure the callbackURL is an absolute URL or relative to the root. | ||
*/ | ||
function normalizeCallbackURL(callbackURL, servicePath) { | ||
if (callbackURL) { | ||
var parsed = _url2.default.parse(callbackURL); | ||
if (!parsed.protocol) { | ||
callbackURL = '/' + (0, _feathersCommons.stripSlashes)(callbackURL); | ||
} | ||
} else { | ||
callbackURL = callbackURL || '/' + (0, _feathersCommons.stripSlashes)(servicePath) + '/callback'; | ||
} | ||
return callbackURL; | ||
} | ||
function init(options) { | ||
@@ -258,5 +296,9 @@ if (!options.provider) { | ||
if (!options.service) { | ||
throw new Error('You need to provide an \'service\' for your ' + options.provider + ' provider. This can either be a string or an initialized service.'); | ||
throw new Error('You need to provide an \'service\' for your ' + options.provider + ' OAuth provider. This can either be a string or an initialized service.'); | ||
} | ||
if (!options.successHandler && !options.successRedirect) { | ||
throw new Error('You need to provide a \'successRedirect\' URL for your ' + options.provider + ' OAuth provider when using the default successHandler.'); | ||
} | ||
if (!options.strategy) { | ||
@@ -277,3 +319,3 @@ throw new Error('You need to provide a Passport \'strategy\' for your ' + options.provider + ' provider'); | ||
options = (0, _lodash2.default)({ user: {} }, app.get('auth'), app.get('auth').oauth2, options); | ||
options.callbackURL = options.callbackURL || options.service + '/callback'; | ||
options.callbackURL = normalizeCallbackURL(options.callbackURL, options.service); | ||
@@ -280,0 +322,0 @@ if (options.token === undefined) { |
@@ -45,3 +45,3 @@ 'use strict'; | ||
var _verifyToken = function _verifyToken() { | ||
var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; | ||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
@@ -79,3 +79,3 @@ var secret = options.token.secret; | ||
function TokenService() { | ||
var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; | ||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
@@ -82,0 +82,0 @@ _classCallCheck(this, TokenService); |
@@ -56,3 +56,3 @@ # Migrating from 0.7 to 0.8 | ||
auth.isAuthenticated(), | ||
auth.checkPermissions({entity: 'user', has: 'users', field: 'permissions'}), | ||
auth.checkPermissions({namespace: 'users', on: 'user', field: 'permissions'}), | ||
auth.isPermitted() | ||
@@ -71,4 +71,3 @@ ] | ||
mw.checkPermissions({ | ||
entity: 'user', | ||
has: 'admin', | ||
on: 'user', | ||
field: 'permissions', | ||
@@ -91,3 +90,3 @@ permissions | ||
auth.isAuthenticated(), | ||
auth.hasPermissions('admin', { field: 'role'}) | ||
auth.checkPermissions({namespace: 'admin', on: 'user', field: 'role'}) | ||
] | ||
@@ -207,4 +206,23 @@ }); | ||
TODO | ||
TODO (EK): We currently set them up automatically by default but may enforce middleware to be registered explicitly. | ||
### All cookies are httpOnly | ||
There is no longer a JS accessible cookie. in most cases this is an implementation detail and not something you were likely aware of. However, if you were relying on accessing the JWT access token inside the JS accessible cookie then it is no more. | ||
We've found that all authentication can happen using an `httpOnly` cookie instead of having the short lived JS accessible cookie. This is not only more secure as you are no longer susceptible to XSS attacks but are also more flexible. | ||
Scenarios where you need to use a cookie: | ||
- OAuth | ||
- Universal app server side rendering | ||
- Old school server side template rendering | ||
- Redirects to other domains or apps after authentication | ||
To support all of these use cases we needed to encode the auth token a cookie. The other option is to put the token in the query string (which is insecure). | ||
Previously we had the feathers client parse the token from a short lived JS accessible cookie. However, we've since realized that if you make an ajax call the cookie will be sent along in the header and we can verify it on the server side. If you make a socket call the initial socket connection will also have the cookie and we can verify it. So in all cases we can simply use an `httpOnly` cookie and parse the token out of it server side. | ||
If you are doing your authentication over AJAX or sockets in the first place then there is no need for a cookie at all and the client will just store the token in localstorage and use it until logged out or expired. | ||
### You no longer register some hooks | ||
@@ -243,5 +261,5 @@ | ||
### Response to `app.uthenticate()` returns `user` | ||
### Response to `app.authenticate()` does not return `user` | ||
The authenticate call is not a typical service call. It doesn't support batch calls as you can't authenticate multiple users and it returns both the token and the authenticated user. Assigning the user object to `response.data` doesn't makes it harder for you to add your own custom data to the authentication response. For those reasons and to make it easier for people to find the logged in `user` in the response, `response.data` is now `response.user` when authenticating. | ||
We previously made the assumption that you are always authenticating a user. This may not always be the case, or your app may not care about the current user as you have their id or can encode some details in the JWT. Therefore, if you need to get the current user you need to request it explicitly after authentication. | ||
@@ -252,5 +270,4 @@ ### Removed Configuration Options | ||
We've changed up some of the possible authentication options. | ||
We've changed up some of the possible authentication options. You can view all the available options in the main [`index.js`](./src/index.js) file. | ||
- `cookie` | ||
- | ||
@@ -264,3 +281,3 @@ | ||
The following hooks have been remove: | ||
The following hooks have been removed: | ||
@@ -274,2 +291,2 @@ - `restrictToOwner` | ||
You should now use single `checkPermissions` and `isPermitted` hook (or middleware) as shown above. | ||
You should now use single `checkPermissions` and `isPermitted` hook (or middleware) as shown above. |
{ | ||
"name": "feathers-authentication", | ||
"description": "Add Authentication to your FeathersJS app.", | ||
"version": "0.8.0-beta-1", | ||
"version": "0.8.0-beta-2", | ||
"homepage": "https://github.com/feathersjs/feathers-authentication", | ||
@@ -39,4 +39,5 @@ "main": "lib/", | ||
"jshint": "jshint src/. test/. --config", | ||
"mocha": "mocha --recursive test/ --compilers js:babel-core/register", | ||
"test": "npm run compile && npm run jshint && npm run mocha && nsp check" | ||
"mocha": "mocha --opts mocha.opts", | ||
"coverage": "istanbul cover _mocha -- --opts mocha.opts", | ||
"test": "npm run compile && npm run jshint && npm run coverage && nsp check" | ||
}, | ||
@@ -53,4 +54,6 @@ "directories": { | ||
"debug": "^2.2.0", | ||
"feathers-commons": "^0.7.5", | ||
"feathers-errors": "^2.4.0", | ||
"jsonwebtoken": "^7.1.9", | ||
"jwt-decode": "^2.1.0", | ||
"lodash.intersection": "^4.4.0", | ||
@@ -77,2 +80,3 @@ "lodash.isplainobject": "^4.0.6", | ||
"feathers-socketio": "^1.3.2", | ||
"istanbul": "^1.1.0-alpha.1", | ||
"jshint": "^2.9.3", | ||
@@ -79,0 +83,0 @@ "localstorage-memory": "^1.0.2", |
# feathers-authentication | ||
[![Build Status](https://travis-ci.org/feathersjs/feathers-authentication.png?branch=master)](https://travis-ci.org/feathersjs/feathers-authentication) | ||
[![Code Climate](https://codeclimate.com/github/feathersjs/feathers-authentication/badges/gpa.svg)](https://codeclimate.com/github/feathersjs/feathers-authentication) | ||
[![Test Coverage](https://codeclimate.com/github/feathersjs/feathers-authentication/badges/coverage.svg)](https://codeclimate.com/github/feathersjs/feathers-authentication/coverage) | ||
[![Issue Count](https://codeclimate.com/github/feathersjs/feathers-authentication/badges/issue_count.svg)](https://codeclimate.com/github/feathersjs/feathers-authentication) | ||
@@ -5,0 +8,0 @@ > Add Authentication to your FeathersJS app. |
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
152410
44
2312
109
0
14
24
+ Addedfeathers-commons@^0.7.5
+ Addedjwt-decode@^2.1.0
+ Addedfeathers-commons@0.7.8(transitive)
+ Addedjwt-decode@2.2.0(transitive)