mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-27 11:18:14 +01:00 
			
		
		
		
	Updated + first rough implementation
This commit is contained in:
		
							parent
							
								
									f0f03efe17
								
							
						
					
					
						commit
						405c954b65
					
				| @ -37,6 +37,15 @@ | ||||
|             <ui-btn type="submit" :disabled="processing" color="primary" class="leading-none">{{ processing ? 'Checking...' : $strings.ButtonSubmit }}</ui-btn> | ||||
|           </div> | ||||
|         </form> | ||||
|         <hr /> | ||||
|         <div class="w-full flex py-3"> | ||||
|           <a href="http://localhost:3333/auth/google"> | ||||
|             <ui-btn color="primary" class="leading-none">Login with Google</ui-btn> | ||||
|           </a> | ||||
|           <a href="http://localhost:3333/auth/openid"> | ||||
|             <ui-btn color="primary" class="leading-none">Login with OpenId</ui-btn> | ||||
|           </a> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| @ -132,11 +141,7 @@ export default { | ||||
| 
 | ||||
|       location.reload() | ||||
|     }, | ||||
| <<<<<<< HEAD | ||||
|     setUser({ user, userDefaultLibraryId, serverSettings, Source }) { | ||||
| ======= | ||||
|     setUser({ user, userDefaultLibraryId, serverSettings, Source, ereaderDevices }) { | ||||
| >>>>>>> origin/master | ||||
|       this.$store.commit('setServerSettings', serverSettings) | ||||
|       this.$store.commit('setSource', Source) | ||||
|       this.$store.commit('libraries/setEReaderDevices', ereaderDevices) | ||||
| @ -166,10 +171,7 @@ export default { | ||||
|         else this.error = 'Unknown Error' | ||||
|         return false | ||||
|       }) | ||||
| <<<<<<< HEAD | ||||
|       console.log('Auth res', authRes) | ||||
| ======= | ||||
| >>>>>>> origin/master | ||||
|       if (authRes?.error) { | ||||
|         this.error = authRes.error | ||||
|       } else if (authRes) { | ||||
| @ -222,6 +224,11 @@ export default { | ||||
|     } | ||||
|   }, | ||||
|   async mounted() { | ||||
|     console.log(new URLSearchParams(window.location.search).get('setToken')) | ||||
|     if (new URLSearchParams(window.location.search).get('setToken')) { | ||||
|       localStorage.setItem('token', new URLSearchParams(window.location.search).get('setToken')) | ||||
|       console.log('hereasd') | ||||
|     } | ||||
|     if (localStorage.getItem('token')) { | ||||
|       var userfound = await this.checkAuth() | ||||
|       if (userfound) return // if valid user no need to check status | ||||
|  | ||||
| @ -6,14 +6,14 @@ const JwtStrategy = require('passport-jwt').Strategy | ||||
| const ExtractJwt = require('passport-jwt').ExtractJwt | ||||
| const GoogleStrategy = require('passport-google-oauth20').Strategy | ||||
| const OpenIDConnectStrategy = require('passport-openidconnect') | ||||
| const Database = require('./Database') | ||||
| 
 | ||||
| /** | ||||
|  * @class Class for handling all the authentication related functionality. | ||||
|  */ | ||||
| class Auth { | ||||
| 
 | ||||
|   constructor(db) { | ||||
|     this.db = db | ||||
|   constructor() { | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
| @ -31,10 +31,10 @@ class Auth { | ||||
|         clientID: global.ServerSettings.authGoogleOauth20ClientID, | ||||
|         clientSecret: global.ServerSettings.authGoogleOauth20ClientSecret, | ||||
|         callbackURL: global.ServerSettings.authGoogleOauth20CallbackURL | ||||
|       }, (function (accessToken, refreshToken, profile, done) { | ||||
|       }, (async function (accessToken, refreshToken, profile, done) { | ||||
|         // TODO: what to use as username
 | ||||
|         // TODO: do we want to create the users which does not exist?
 | ||||
|         const user = this.db.users.find(u => u.username.toLowerCase() === profile.emails[0].value.toLowerCase()) | ||||
|         const user = await Database.userModel.getUserByEmail(profile.emails[0].value.toLowerCase()) | ||||
| 
 | ||||
|         if (!user || !user.isActive) { | ||||
|           done(null, null) | ||||
| @ -61,7 +61,7 @@ class Auth { | ||||
|         (function (issuer, 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()) | ||||
|           var user = Database.userModel.getUserByEmail(profile.emails[0].value.toLowerCase()) | ||||
| 
 | ||||
|           if (!user || !user.isActive) { | ||||
|             done(null, null) | ||||
| @ -86,16 +86,17 @@ class Auth { | ||||
|         return cb(null, JSON.stringify({ | ||||
|           "username": user.username, | ||||
|           "id": user.id, | ||||
|           "email": user.email, | ||||
|         })) | ||||
|       }) | ||||
|     }) | ||||
| 
 | ||||
|     // define how to deseralize a user (use the username to get it from the database)
 | ||||
|     passport.deserializeUser((function (user, cb) { | ||||
|       process.nextTick((function () { | ||||
|       process.nextTick((async function () { | ||||
|         const parsedUserInfo = JSON.parse(user) | ||||
|         // TODO: do the matching on username or better on id?
 | ||||
|         const dbUser = this.db.users.find(u => u.username.toLowerCase() === parsedUserInfo.username.toLowerCase()) | ||||
|         const dbUser = await Database.userModel.getUserByUsername(parsedUserInfo.username.toLowerCase()) | ||||
|         return cb(null, dbUser) | ||||
|       }).bind(this)) | ||||
|     }).bind(this)) | ||||
| @ -108,9 +109,9 @@ class Auth { | ||||
|   initAuthRoutes(router) { | ||||
|     // Local strategy login route (takes username and password)
 | ||||
|     router.post('/login', passport.authenticate('local'), | ||||
|       (function (req, res) { | ||||
|       (async function (req, res) { | ||||
|         // return the user login response json if the login was successfull
 | ||||
|         res.json(this.getUserLoginResponsePayload(req.user)) | ||||
|         res.json(await this.getUserLoginResponsePayload(req.user)) | ||||
|       }).bind(this) | ||||
|     ) | ||||
| 
 | ||||
| @ -120,9 +121,12 @@ class Auth { | ||||
|     // google-oauth20 strategy callback route (this receives the token from google)
 | ||||
|     router.get('/auth/google/callback', | ||||
|       passport.authenticate('google'), | ||||
|       (function (req, res) { | ||||
|       (async function (req, res) { | ||||
|         // return the user login response json if the login was successfull
 | ||||
|         res.json(this.getUserLoginResponsePayload(req.user)) | ||||
|         var data_json = await this.getUserLoginResponsePayload(req.user) | ||||
|         // res.json(data_json)
 | ||||
|         // TODO: figure out how to redirect back to the app page
 | ||||
|         res.redirect(301, `http://localhost:3000/login?setToken=${data_json.user.token}`) | ||||
|       }).bind(this) | ||||
|     ) | ||||
| 
 | ||||
| @ -132,9 +136,12 @@ class Auth { | ||||
|     // openid strategy callback route (this receives the token from the configured openid login provider)
 | ||||
|     router.get('/auth/openid/callback', | ||||
|       passport.authenticate('openidconnect'), | ||||
|       (function (req, res) { | ||||
|       (async function (req, res) { | ||||
|         // return the user login response json if the login was successfull
 | ||||
|         res.json(this.getUserLoginResponsePayload(req.user)) | ||||
|         var data_json = await this.getUserLoginResponsePayload(req.user) | ||||
|         // res.json(data_json)
 | ||||
|         // TODO: figure out how to redirect back to the app page
 | ||||
|         res.redirect(301, `http://localhost:3000/login?setToken=${data_json.user.token}`) | ||||
|       }).bind(this) | ||||
|     ) | ||||
| 
 | ||||
| @ -176,6 +183,20 @@ class Auth { | ||||
|     return jwt.sign({ userId: user.id, username: user.username }, global.ServerSettings.tokenSecret) | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Function to generate a jwt token for a given user. | ||||
|    * @param {string} token  | ||||
|    * @returns the tokens data. | ||||
|    */ | ||||
|   static validateAccessToken(token) { | ||||
|     try { | ||||
|       return jwt.verify(token, global.ServerSettings.tokenSecret) | ||||
|     } | ||||
|     catch (err) { | ||||
|       return null | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Generate a token for each user. | ||||
|    */ | ||||
| @ -203,7 +224,7 @@ class Auth { | ||||
|    * @param {function} done  | ||||
|    */ | ||||
|   jwtAuthCheck(jwt_payload, done) { | ||||
|     const user = this.db.users.find(u => u.username.toLowerCase() === jwt_payload.username.toLowerCase()) | ||||
|     const user = Database.userModel.getUserByUsername(jwt_payload.username.toLowerCase()) | ||||
| 
 | ||||
|     if (!user || !user.isActive) { | ||||
|       done(null, null) | ||||
| @ -220,7 +241,7 @@ class Auth { | ||||
|    * @param {function} done  | ||||
|    */ | ||||
|   async localAuthCheckUserPw(username, password, done) { | ||||
|     const user = this.db.users.find(u => u.username.toLowerCase() === username.toLowerCase()) | ||||
|     const user = Database.userModel.getUserByUsername(username.toLowerCase()) | ||||
| 
 | ||||
|     if (!user || !user.isActive) { | ||||
|       done(null, null) | ||||
| @ -269,7 +290,8 @@ class Auth { | ||||
|    * @param {string} username  | ||||
|    * @returns {string} jsonPayload | ||||
|    */ | ||||
|   getUserLoginResponsePayload(user) { | ||||
|   async getUserLoginResponsePayload(user) { | ||||
|     const libraryIds = await Database.libraryModel.getAllLibraryIds() | ||||
|     return { | ||||
|       user: user.toJSONForBrowser(), | ||||
|       userDefaultLibraryId: user.getDefaultLibraryId(libraryIds), | ||||
|  | ||||
| @ -161,7 +161,8 @@ class Server { | ||||
| 
 | ||||
|     this.server = http.createServer(app) | ||||
| 
 | ||||
|     router.use(this.auth.cors) | ||||
| 
 | ||||
| 
 | ||||
|     router.use(fileUpload({ | ||||
|       defCharset: 'utf8', | ||||
|       defParamCharset: 'utf8', | ||||
| @ -195,6 +196,9 @@ class Server { | ||||
|       this.rssFeedManager.getFeedItem(req, res) | ||||
|     }) | ||||
| 
 | ||||
|     // Auth routes
 | ||||
|     this.auth.initAuthRoutes(router) | ||||
| 
 | ||||
|     // Client dynamic routes
 | ||||
|     const dyanimicRoutes = [ | ||||
|       '/item/:id', | ||||
|  | ||||
| @ -1,6 +1,9 @@ | ||||
| const SocketIO = require('socket.io') | ||||
| const Logger = require('./Logger') | ||||
| const Database = require('./Database') | ||||
| const Auth = require('./Auth') | ||||
| const passport = require('passport') | ||||
| const expressSession = require('express-session') | ||||
| 
 | ||||
| class SocketAuthority { | ||||
|   constructor() { | ||||
| @ -81,6 +84,24 @@ class SocketAuthority { | ||||
|         methods: ["GET", "POST"] | ||||
|       } | ||||
|     }) | ||||
| 
 | ||||
|     /* | ||||
|     const wrap = middleware => (socket, next) => middleware(socket.request, {}, next); | ||||
| 
 | ||||
|     io.use(wrap(expressSession({ | ||||
|       secret: global.ServerSettings.tokenSecret, | ||||
|       resave: false, | ||||
|       saveUninitialized: false, | ||||
|       cookie: { | ||||
|         // also send the cookie if were hare not on https
 | ||||
|         secure: false | ||||
|       }, | ||||
|     }))); | ||||
|      | ||||
|     io.use(wrap(passport.initialize())); | ||||
|     io.use(wrap(passport.session())); | ||||
|     */ | ||||
| 
 | ||||
|     this.io.on('connection', (socket) => { | ||||
|       this.clients[socket.id] = { | ||||
|         id: socket.id, | ||||
| @ -147,7 +168,13 @@ 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.db.users.find(u => u.token === token) | ||||
|     // TODO
 | ||||
|     const token_data = Auth.validateAccessToken(token) | ||||
|     if (!token_data || !token_data.username) { | ||||
|       Logger.error('Cannot validate socket - invalid token') | ||||
|       return socket.emit('invalid_token') | ||||
|     } | ||||
|     const user = await Database.userModel.getUserByUsername(token_data.username) | ||||
|     if (!user) { | ||||
|       Logger.error('Cannot validate socket - invalid token') | ||||
|       return socket.emit('invalid_token') | ||||
|  | ||||
| @ -194,6 +194,25 @@ class User extends Model { | ||||
|     return this.getOldUser(user) | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Get user by email case insensitive | ||||
|    * @param {string} username  | ||||
|    * @returns {Promise<oldUser|null>} returns null if not found | ||||
|    */ | ||||
|   static async getUserByEmail(email) { | ||||
|     if (!email) return null | ||||
|     const user = await this.findOne({ | ||||
|       where: { | ||||
|         email: { | ||||
|           [Op.like]: email | ||||
|         } | ||||
|       }, | ||||
|       include: this.sequelize.models.mediaProgress | ||||
|     }) | ||||
|     if (!user) return null | ||||
|     return this.getOldUser(user) | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Get user by id | ||||
|    * @param {string} userId  | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user