mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-05-24 01:13:00 -04:00
Fixes for passport local and allow empty password
This commit is contained in:
parent
812395b21b
commit
7010a13648
@ -124,7 +124,7 @@ export default {
|
||||
|
||||
location.reload()
|
||||
},
|
||||
setUser({ user, userDefaultLibraryId, serverSettings, Source, feeds }) {
|
||||
setUser({ user, userDefaultLibraryId, serverSettings, Source }) {
|
||||
this.$store.commit('setServerSettings', serverSettings)
|
||||
this.$store.commit('setSource', Source)
|
||||
this.$setServerLanguageCode(serverSettings.language)
|
||||
@ -143,17 +143,18 @@ export default {
|
||||
this.error = null
|
||||
this.processing = true
|
||||
|
||||
var payload = {
|
||||
const payload = {
|
||||
username: this.username,
|
||||
password: this.password || ''
|
||||
}
|
||||
var authRes = await this.$axios.$post('/login', payload).catch((error) => {
|
||||
const authRes = await this.$axios.$post('/login', payload).catch((error) => {
|
||||
console.error('Failed', error.response)
|
||||
if (error.response) this.error = error.response.data
|
||||
else this.error = 'Unknown Error'
|
||||
return false
|
||||
})
|
||||
if (authRes && authRes.error) {
|
||||
console.log('Auth res', authRes)
|
||||
if (authRes?.error) {
|
||||
this.error = authRes.error
|
||||
} else if (authRes) {
|
||||
this.setUser(authRes)
|
||||
|
20
package-lock.json
generated
20
package-lock.json
generated
@ -18,7 +18,6 @@
|
||||
"passport": "^0.6.0",
|
||||
"passport-google-oauth20": "^2.0.0",
|
||||
"passport-jwt": "^4.0.1",
|
||||
"passport-local": "^1.0.0",
|
||||
"passport-openidconnect": "^0.1.1",
|
||||
"socket.io": "^4.5.4",
|
||||
"xml2js": "^0.4.23"
|
||||
@ -1109,17 +1108,6 @@
|
||||
"passport-strategy": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/passport-local": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz",
|
||||
"integrity": "sha512-9wCE6qKznvf9mQYYbgJ3sVOHmCWoUNMVFoZzNoznmISbhnNNPhN9xfY3sLmScHMetEJeoY7CXwfhCe7argfQow==",
|
||||
"dependencies": {
|
||||
"passport-strategy": "1.x.x"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/passport-oauth2": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.7.0.tgz",
|
||||
@ -2414,14 +2402,6 @@
|
||||
"passport-strategy": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"passport-local": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz",
|
||||
"integrity": "sha512-9wCE6qKznvf9mQYYbgJ3sVOHmCWoUNMVFoZzNoznmISbhnNNPhN9xfY3sLmScHMetEJeoY7CXwfhCe7argfQow==",
|
||||
"requires": {
|
||||
"passport-strategy": "1.x.x"
|
||||
}
|
||||
},
|
||||
"passport-oauth2": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.7.0.tgz",
|
||||
|
@ -39,7 +39,6 @@
|
||||
"passport": "^0.6.0",
|
||||
"passport-google-oauth20": "^2.0.0",
|
||||
"passport-jwt": "^4.0.1",
|
||||
"passport-local": "^1.0.0",
|
||||
"passport-openidconnect": "^0.1.1",
|
||||
"socket.io": "^4.5.4",
|
||||
"xml2js": "^0.4.23"
|
||||
|
@ -1,12 +1,11 @@
|
||||
const passport = require('passport')
|
||||
const bcrypt = require('./libs/bcryptjs')
|
||||
const jwt = require('./libs/jsonwebtoken')
|
||||
const LocalStrategy = require('passport-local')
|
||||
const JwtStrategy = require('passport-jwt').Strategy;
|
||||
const ExtractJwt = require('passport-jwt').ExtractJwt;
|
||||
const GoogleStrategy = require('passport-google-oauth20').Strategy;
|
||||
var OpenIDConnectStrategy = require('passport-openidconnect');
|
||||
const User = require('./objects/user/User.js')
|
||||
const LocalStrategy = require('./libs/passportLocal')
|
||||
const JwtStrategy = require('passport-jwt').Strategy
|
||||
const ExtractJwt = require('passport-jwt').ExtractJwt
|
||||
const GoogleStrategy = require('passport-google-oauth20').Strategy
|
||||
const OpenIDConnectStrategy = require('passport-openidconnect')
|
||||
|
||||
/**
|
||||
* @class Class for handling all the authentication related functionality.
|
||||
@ -35,7 +34,7 @@ class Auth {
|
||||
}, (function (accessToken, refreshToken, profile, done) {
|
||||
// TODO: what to use as username
|
||||
// TODO: do we want to create the users which does not exist?
|
||||
var user = this.db.users.find(u => u.username.toLowerCase() === profile.emails[0].value.toLowerCase())
|
||||
const user = this.db.users.find(u => u.username.toLowerCase() === profile.emails[0].value.toLowerCase())
|
||||
|
||||
if (!user || !user.isActive) {
|
||||
done(null, null)
|
||||
@ -87,19 +86,19 @@ class Auth {
|
||||
return cb(null, JSON.stringify({
|
||||
"username": user.username,
|
||||
"id": user.id,
|
||||
}));
|
||||
});
|
||||
});
|
||||
}))
|
||||
})
|
||||
})
|
||||
|
||||
// define how to deseralize a user (use the username to get it from the database)
|
||||
passport.deserializeUser((function (user, cb) {
|
||||
process.nextTick((function () {
|
||||
const parsedUserInfo = JSON.parse(user)
|
||||
// TODO: do the matching on username or better on id?
|
||||
var dbUser = this.db.users.find(u => u.username.toLowerCase() === parsedUserInfo.username.toLowerCase())
|
||||
return cb(null, new User(dbUser));
|
||||
}).bind(this));
|
||||
}).bind(this));
|
||||
const dbUser = this.db.users.find(u => u.username.toLowerCase() === parsedUserInfo.username.toLowerCase())
|
||||
return cb(null, dbUser)
|
||||
}).bind(this))
|
||||
}).bind(this))
|
||||
}
|
||||
|
||||
/**
|
||||
@ -107,19 +106,11 @@ class Auth {
|
||||
* @param {express.Router} router
|
||||
*/
|
||||
initAuthRoutes(router) {
|
||||
// just a route saying "you need to login" where we redirect e.g. after logout
|
||||
// TODO: replace with a 401?
|
||||
router.get('/login', function (req, res) {
|
||||
res.send('please login')
|
||||
})
|
||||
|
||||
// Local strategy login route (takes username and password)
|
||||
router.post('/login', passport.authenticate('local', {
|
||||
failureRedirect: '/login'
|
||||
}),
|
||||
router.post('/login', passport.authenticate('local'),
|
||||
(function (req, res) {
|
||||
// return the user login response json if the login was successfull
|
||||
res.json(this.getUserLoginResponsePayload(req.user.username))
|
||||
res.json(this.getUserLoginResponsePayload(req.user))
|
||||
}).bind(this)
|
||||
)
|
||||
|
||||
@ -128,30 +119,35 @@ class Auth {
|
||||
|
||||
// google-oauth20 strategy callback route (this receives the token from google)
|
||||
router.get('/auth/google/callback',
|
||||
passport.authenticate('google', { failureRedirect: '/login' }),
|
||||
passport.authenticate('google'),
|
||||
(function (req, res) {
|
||||
// return the user login response json if the login was successfull
|
||||
res.json(this.getUserLoginResponsePayload(req.user.username))
|
||||
res.json(this.getUserLoginResponsePayload(req.user))
|
||||
}).bind(this)
|
||||
)
|
||||
|
||||
// openid strategy login route (this redirects to the configured openid login provider)
|
||||
router.get('/auth/openid', passport.authenticate('openidconnect'));
|
||||
router.get('/auth/openid', passport.authenticate('openidconnect'))
|
||||
|
||||
// openid strategy callback route (this receives the token from the configured openid login provider)
|
||||
router.get('/auth/openid/callback',
|
||||
passport.authenticate('openidconnect', { failureRedirect: '/login' }),
|
||||
passport.authenticate('openidconnect'),
|
||||
(function (req, res) {
|
||||
// return the user login response json if the login was successfull
|
||||
res.json(this.getUserLoginResponsePayload(req.user.username))
|
||||
res.json(this.getUserLoginResponsePayload(req.user))
|
||||
}).bind(this)
|
||||
)
|
||||
|
||||
// Logout route
|
||||
router.get('/logout', function (req, res) {
|
||||
router.post('/logout', (req, res) => {
|
||||
// TODO: invalidate possible JWTs
|
||||
req.logout()
|
||||
res.redirect('/login')
|
||||
req.logout((err) => {
|
||||
if (err) {
|
||||
res.sendStatus(500)
|
||||
} else {
|
||||
res.sendStatus(200)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@ -177,7 +173,7 @@ class Auth {
|
||||
* @returns the token.
|
||||
*/
|
||||
generateAccessToken(user) {
|
||||
return jwt.sign({ userId: user.id, username: user.username }, global.ServerSettings.tokenSecret);
|
||||
return jwt.sign({ userId: user.id, username: user.username }, global.ServerSettings.tokenSecret)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -206,7 +202,7 @@ class Auth {
|
||||
* @param {function} done
|
||||
*/
|
||||
jwtAuthCheck(jwt_payload, done) {
|
||||
var user = this.db.users.find(u => u.username.toLowerCase() === jwt_payload.username.toLowerCase())
|
||||
const user = this.db.users.find(u => u.username.toLowerCase() === jwt_payload.username.toLowerCase())
|
||||
|
||||
if (!user || !user.isActive) {
|
||||
done(null, null)
|
||||
@ -217,13 +213,13 @@ class Auth {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a username and passpword touple is valid and the user active.
|
||||
* Checks if a username and password tuple is valid and the user active.
|
||||
* @param {string} username
|
||||
* @param {string} password
|
||||
* @param {function} done
|
||||
*/
|
||||
localAuthCheckUserPw(username, password, done) {
|
||||
var user = this.db.users.find(u => u.username.toLowerCase() === username.toLowerCase())
|
||||
async localAuthCheckUserPw(username, password, done) {
|
||||
const user = this.db.users.find(u => u.username.toLowerCase() === username.toLowerCase())
|
||||
|
||||
if (!user || !user.isActive) {
|
||||
done(null, null)
|
||||
@ -241,7 +237,7 @@ class Auth {
|
||||
}
|
||||
|
||||
// Check password match
|
||||
var compare = bcrypt.compareSync(password, user.pash)
|
||||
const compare = await bcrypt.compare(password, user.pash)
|
||||
if (compare) {
|
||||
done(null, user)
|
||||
return
|
||||
@ -272,9 +268,7 @@ class Auth {
|
||||
* @param {string} username
|
||||
* @returns {string} jsonPayload
|
||||
*/
|
||||
getUserLoginResponsePayload(username) {
|
||||
var user = this.db.users.find(u => u.username.toLowerCase() === username.toLowerCase())
|
||||
user = new User(user)
|
||||
getUserLoginResponsePayload(user) {
|
||||
return {
|
||||
user: user.toJSONForBrowser(),
|
||||
userDefaultLibraryId: user.getDefaultLibraryId(this.db.libraries),
|
||||
|
@ -161,7 +161,7 @@ class Server {
|
||||
// config passport.js
|
||||
this.auth.initPassportJs()
|
||||
// use auth on all routes - not used now
|
||||
// app.use(passport.authenticate('session'));
|
||||
// app.use(passport.authenticate('session'))
|
||||
|
||||
const router = express.Router()
|
||||
app.use(global.RouterBasePath, router)
|
||||
@ -169,9 +169,8 @@ class Server {
|
||||
|
||||
this.server = http.createServer(app)
|
||||
|
||||
// router.use(this.auth.cors)
|
||||
router.use(fileUpload())
|
||||
router.use(express.urlencoded({ extended: true, limit: "5mb" }));
|
||||
router.use(express.urlencoded({ extended: true, limit: "5mb" }))
|
||||
router.use(express.json({ limit: "5mb" }))
|
||||
|
||||
// Static path to generated nuxt
|
||||
|
@ -140,7 +140,7 @@ class SocketAuthority {
|
||||
// When setting up a socket connection the user needs to be associated with a socket id
|
||||
// for this the client will send a 'auth' event that includes the users API token
|
||||
async authenticateSocket(socket, token) {
|
||||
const user = await this.Server.auth.authenticateUser(token)
|
||||
const user = await this.Server.db.users.find(u => u.token === token)
|
||||
if (!user) {
|
||||
Logger.error('Cannot validate socket - invalid token')
|
||||
return socket.emit('invalid_token')
|
||||
|
20
server/libs/passportLocal/LICENSE
Normal file
20
server/libs/passportLocal/LICENSE
Normal file
@ -0,0 +1,20 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2011-2014 Jared Hanson
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
20
server/libs/passportLocal/index.js
Normal file
20
server/libs/passportLocal/index.js
Normal file
@ -0,0 +1,20 @@
|
||||
//
|
||||
// modified for audiobookshelf
|
||||
// Source: https://github.com/jaredhanson/passport-local
|
||||
//
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
var Strategy = require('./strategy');
|
||||
|
||||
|
||||
/**
|
||||
* Expose `Strategy` directly from package.
|
||||
*/
|
||||
exports = module.exports = Strategy;
|
||||
|
||||
/**
|
||||
* Export constructors.
|
||||
*/
|
||||
exports.Strategy = Strategy;
|
119
server/libs/passportLocal/strategy.js
Normal file
119
server/libs/passportLocal/strategy.js
Normal file
@ -0,0 +1,119 @@
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
const passport = require('passport-strategy')
|
||||
const util = require('util')
|
||||
|
||||
|
||||
function lookup(obj, field) {
|
||||
if (!obj) { return null; }
|
||||
var chain = field.split(']').join('').split('[');
|
||||
for (var i = 0, len = chain.length; i < len; i++) {
|
||||
var prop = obj[chain[i]];
|
||||
if (typeof (prop) === 'undefined') { return null; }
|
||||
if (typeof (prop) !== 'object') { return prop; }
|
||||
obj = prop;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* `Strategy` constructor.
|
||||
*
|
||||
* The local authentication strategy authenticates requests based on the
|
||||
* credentials submitted through an HTML-based login form.
|
||||
*
|
||||
* Applications must supply a `verify` callback which accepts `username` and
|
||||
* `password` credentials, and then calls the `done` callback supplying a
|
||||
* `user`, which should be set to `false` if the credentials are not valid.
|
||||
* If an exception occured, `err` should be set.
|
||||
*
|
||||
* Optionally, `options` can be used to change the fields in which the
|
||||
* credentials are found.
|
||||
*
|
||||
* Options:
|
||||
* - `usernameField` field name where the username is found, defaults to _username_
|
||||
* - `passwordField` field name where the password is found, defaults to _password_
|
||||
* - `passReqToCallback` when `true`, `req` is the first argument to the verify callback (default: `false`)
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* passport.use(new LocalStrategy(
|
||||
* function(username, password, done) {
|
||||
* User.findOne({ username: username, password: password }, function (err, user) {
|
||||
* done(err, user);
|
||||
* });
|
||||
* }
|
||||
* ));
|
||||
*
|
||||
* @param {Object} options
|
||||
* @param {Function} verify
|
||||
* @api public
|
||||
*/
|
||||
function Strategy(options, verify) {
|
||||
if (typeof options == 'function') {
|
||||
verify = options;
|
||||
options = {};
|
||||
}
|
||||
if (!verify) { throw new TypeError('LocalStrategy requires a verify callback'); }
|
||||
|
||||
this._usernameField = options.usernameField || 'username';
|
||||
this._passwordField = options.passwordField || 'password';
|
||||
|
||||
passport.Strategy.call(this);
|
||||
this.name = 'local';
|
||||
this._verify = verify;
|
||||
this._passReqToCallback = options.passReqToCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherit from `passport.Strategy`.
|
||||
*/
|
||||
util.inherits(Strategy, passport.Strategy);
|
||||
|
||||
/**
|
||||
* Authenticate request based on the contents of a form submission.
|
||||
*
|
||||
* @param {Object} req
|
||||
* @api protected
|
||||
*/
|
||||
Strategy.prototype.authenticate = function (req, options) {
|
||||
options = options || {};
|
||||
var username = lookup(req.body, this._usernameField)
|
||||
if (username === null) {
|
||||
lookup(req.query, this._usernameField);
|
||||
}
|
||||
|
||||
var password = lookup(req.body, this._passwordField)
|
||||
if (password === null) {
|
||||
password = lookup(req.query, this._passwordField);
|
||||
}
|
||||
|
||||
if (username === null || password === null) {
|
||||
return this.fail({ message: options.badRequestMessage || 'Missing credentials' }, 400);
|
||||
}
|
||||
|
||||
var self = this;
|
||||
|
||||
function verified(err, user, info) {
|
||||
if (err) { return self.error(err); }
|
||||
if (!user) { return self.fail(info); }
|
||||
self.success(user, info);
|
||||
}
|
||||
|
||||
try {
|
||||
if (self._passReqToCallback) {
|
||||
this._verify(req, username, password, verified);
|
||||
} else {
|
||||
this._verify(username, password, verified);
|
||||
}
|
||||
} catch (ex) {
|
||||
return self.error(ex);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Expose `Strategy`.
|
||||
*/
|
||||
module.exports = Strategy;
|
@ -1,5 +1,4 @@
|
||||
const { BookshelfView } = require('../../utils/constants')
|
||||
const { isNullOrNaN } = require('../../utils')
|
||||
const Logger = require('../../Logger')
|
||||
|
||||
class ServerSettings {
|
||||
@ -144,7 +143,7 @@ class ServerSettings {
|
||||
this.authGoogleOauth20ClientSecret === '' ||
|
||||
this.authGoogleOauth20CallbackURL === ''
|
||||
)) {
|
||||
this.authActiveAuthMethods.splice(this.authActiveAuthMethods.indexOf('google-oauth20', 0), 1);
|
||||
this.authActiveAuthMethods.splice(this.authActiveAuthMethods.indexOf('google-oauth20', 0), 1)
|
||||
}
|
||||
|
||||
// remove uninitialized methods
|
||||
@ -158,7 +157,7 @@ class ServerSettings {
|
||||
this.authOpenIDClientSecret === '' ||
|
||||
this.authOpenIDCallbackURL === ''
|
||||
)) {
|
||||
this.authActiveAuthMethods.splice(this.authActiveAuthMethods.indexOf('generic-oauth20', 0), 1);
|
||||
this.authActiveAuthMethods.splice(this.authActiveAuthMethods.indexOf('generic-oauth20', 0), 1)
|
||||
}
|
||||
|
||||
// fallback to local
|
||||
@ -241,10 +240,10 @@ class ServerSettings {
|
||||
}
|
||||
|
||||
update(payload) {
|
||||
var hasUpdates = false
|
||||
let hasUpdates = false
|
||||
for (const key in payload) {
|
||||
if (key === 'sortingPrefixes' && payload[key] && payload[key].length) {
|
||||
var prefixesCleaned = payload[key].filter(prefix => !!prefix).map(prefix => prefix.toLowerCase())
|
||||
const prefixesCleaned = payload[key].filter(prefix => !!prefix).map(prefix => prefix.toLowerCase())
|
||||
if (prefixesCleaned.join(',') !== this[key].join(',')) {
|
||||
this[key] = [...prefixesCleaned]
|
||||
hasUpdates = true
|
||||
|
Loading…
x
Reference in New Issue
Block a user