From d58d6f1d642b7ace7331ddc18507ae5479dc2024 Mon Sep 17 00:00:00 2001 From: gab9281 Date: Tue, 24 Sep 2024 17:24:32 -0400 Subject: [PATCH] Adds base for OAuth - Still errors --- .vscode/launch.json | 18 +++ server/app.js | 9 ++ server/auth/auth-manager.js | 65 +++++++++ server/auth/modules/passport-js.js | 42 ++++++ .../auth/modules/passport-providers/oauth.js | 43 ++++++ server/package-lock.json | 129 ++++++++++++++++++ server/package.json | 4 + 7 files changed, 310 insertions(+) create mode 100644 .vscode/launch.json create mode 100644 server/auth/auth-manager.js create mode 100644 server/auth/modules/passport-js.js create mode 100644 server/auth/modules/passport-providers/oauth.js diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..96242b0 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,18 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Debug backend", + "skipFiles": [ + "/**" + ], + "program": "${workspaceFolder}/server/app.js", + "cwd":"${workspaceFolder}/server/" + } + ] +} \ No newline at end of file diff --git a/server/app.js b/server/app.js index 76053ba..e28e255 100644 --- a/server/app.js +++ b/server/app.js @@ -12,6 +12,7 @@ const userRouter = require('./routers/users.js'); const folderRouter = require('./routers/folders.js'); const quizRouter = require('./routers/quiz.js'); const imagesRouter = require('./routers/images.js') +const AuthManager = require('./auth/auth-manager.js') // Setup environement dotenv.config(); @@ -49,6 +50,14 @@ app.use('/api/folder', folderRouter); app.use('/api/quiz', quizRouter); app.use('/api/image', imagesRouter); +// Add Auths methods +const session = require('express-session'); +app.use(session({secret: process.env['SESSION_Secret']})); + +authManager = new AuthManager(app) +authManager.addModule('passport-js') +authManager.registerAuths() + app.use(errorHandler) // Start server diff --git a/server/auth/auth-manager.js b/server/auth/auth-manager.js new file mode 100644 index 0000000..9c0ea6e --- /dev/null +++ b/server/auth/auth-manager.js @@ -0,0 +1,65 @@ +const fs = require('fs'); + +const settings = { + "passport-js":{ + "gmatte" : { + type: "oauth", + authorization_url: process.env['OAUTH_AuthorizeUrl'], + client_id : process.env['OAUTH_ClientID'], + client_secret: process.env['OAUTH_ClientSecret'], + config_url: process.env['OAUTH_ConfigUrl'], + userinfo_url: process.env['OAUTH_UserinfoUrl'], + token_url: process.env['OAUTH_TokenUrl'], + logout_url: process.env['OAUTH_LogoutUrl'], + jwks : process.env['OAUTH_JWKS'], + scopes: ['openid','email','profile','groups','offline_access'] + }, + } +} + +class AuthManager{ + constructor(expressapp){ + this.modules = [] + this.app = expressapp + } + + async addModule(name){ + const modulePath = `${process.cwd()}/auth/modules/${name}.js` + + if(fs.existsSync(modulePath)){ + const Module = require(modulePath); + this.modules.push(new Module(this,settings[name])); + console.debug(`Auth module ${name} added`) + } + } + + async registerAuths(){ + for(const module of this.modules){ + module.registerAuth(this.app) + } + } + + async showAuths(){ + let authsData = [] + for(const module in this.modules){ + authsData.push(module.showAuth()) + } + return authsData; + } + + async login(userInfos){ + // TODO global user login method + console.log(userInfos) + } + + async register(userInfos){ + // TODO global user register method + console.log(userInfos) + } + + async logout(){ + // TODO global user logout method + } +} + +module.exports = AuthManager; \ No newline at end of file diff --git a/server/auth/modules/passport-js.js b/server/auth/modules/passport-js.js new file mode 100644 index 0000000..dd336b8 --- /dev/null +++ b/server/auth/modules/passport-js.js @@ -0,0 +1,42 @@ +const fs = require('fs'); +var passport = require('passport') + +class PassportJs{ + constructor(authmanager,settings){ + this.authmanager = authmanager + this.registeredProviders = {} + this.providers = Object.entries(settings) + } + + registerAuth(expressapp){ + expressapp.use(passport.initialize()); + expressapp.use(passport.session()); + + for(const [name,provider] of this.providers){ + if(!(provider.type in this.registeredProviders)){ + this.registerProvider(provider.type) + } + this.registeredProviders[provider.type].register(expressapp,passport,name,provider) + } + + passport.serializeUser(function(user, done) { + done(null, user); + }); + + passport.deserializeUser(function(user, done) { + done(null, user); + }); + } + + registerProvider(providerType){ + const providerPath = `${process.cwd()}/auth/modules/passport-providers/${providerType}.js` + + if(fs.existsSync(providerPath)){ + const Provider = require(providerPath); + this.registeredProviders[providerType]= new Provider() + } + } + +} + +module.exports = PassportJs; \ No newline at end of file diff --git a/server/auth/modules/passport-providers/oauth.js b/server/auth/modules/passport-providers/oauth.js new file mode 100644 index 0000000..116dfe3 --- /dev/null +++ b/server/auth/modules/passport-providers/oauth.js @@ -0,0 +1,43 @@ +var OAuth2Strategy = require('passport-oauth2') + +class PassportOAuth{ + + register(app,passport,name,provider){ + passport.use(name, new OAuth2Strategy({ + authorizationURL: provider.authorization_url, + tokenURL: provider.token_url, + clientID: provider.client_id, + clientSecret: provider.client_secret, + callbackURL: `http://gti700.gmatte.xyz:4400/api/auth/gmatte/callback`, + }, + async function(accessToken, refreshToken, params, profile, done) { + try { + const req = await fetch(provider.userinfo_url,{ + headers:{ + Authorization:`Bearer ${accessToken}` + } + }) + + const data = await req.json() + profile = data + done(null,{accessToken,refreshToken,profile}); + } catch (error) { + return done(error); + } + } + )); + + app.use(`/api/auth/${name}`, passport.authenticate(name,{scope: provider.scopes.join(' ') ?? 'openid profile email'})); + app.use(`/api/auth/${name}/callback`, + passport.authenticate(name, { + successRedirect: '/', + failureRedirect: '/login', + session:false + }), + function(accessToken, refreshToken, params, profile, cb) { + console.log(params); + } + ); + } +} +module.exports = PassportOAuth; \ No newline at end of file diff --git a/server/package-lock.json b/server/package-lock.json index 05f5480..55196fc 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -13,10 +13,14 @@ "cors": "^2.8.5", "dotenv": "^16.4.4", "express": "^4.18.2", + "express-session": "^1.18.0", "jsonwebtoken": "^9.0.2", "mongodb": "^6.3.0", "multer": "^1.4.5-lts.1", "nodemailer": "^6.9.9", + "passport": "^0.7.0", + "passport-oauth2": "^1.8.0", + "passport-openid-oauth20": "^1.2.6", "socket.io": "^4.7.2", "socket.io-client": "^4.7.2" }, @@ -1589,6 +1593,14 @@ "node": "^4.5.0 || >= 5.9" } }, + "node_modules/base64url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", + "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/bcrypt": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", @@ -2535,6 +2547,29 @@ "node": ">= 0.10.0" } }, + "node_modules/express-session": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.0.tgz", + "integrity": "sha512-m93QLWr0ju+rOwApSsyso838LQwgfs44QtOP/WBiwtAgPIo/SAh1a5c6nn2BR6mFNZehTpqKDESzP+fRHVbxwQ==", + "dependencies": { + "cookie": "0.6.0", + "cookie-signature": "1.0.7", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-headers": "~1.0.2", + "parseurl": "~1.3.3", + "safe-buffer": "5.2.1", + "uid-safe": "~2.1.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/express-session/node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==" + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -4492,6 +4527,11 @@ "set-blocking": "^2.0.0" } }, + "node_modules/oauth": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.10.0.tgz", + "integrity": "sha512-1orQ9MT1vHFGQxhuy7E/0gECD3fd2fCC+PIX+/jgmU/gI3EpRocXtmtvxCO5x3WZ443FLTLFWNDjl5MPJf9u+Q==" + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -4522,6 +4562,14 @@ "node": ">= 0.8" } }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -4607,6 +4655,58 @@ "node": ">= 0.8" } }, + "node_modules/passport": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz", + "integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==", + "dependencies": { + "passport-strategy": "1.x.x", + "pause": "0.0.1", + "utils-merge": "^1.0.1" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" + } + }, + "node_modules/passport-oauth2": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.8.0.tgz", + "integrity": "sha512-cjsQbOrXIDE4P8nNb3FQRCCmJJ/utnFKEz2NX209f7KOHPoX18gF7gBzBbLLsj2/je4KrgiwLLGjf0lm9rtTBA==", + "dependencies": { + "base64url": "3.x.x", + "oauth": "0.10.x", + "passport-strategy": "1.x.x", + "uid2": "0.0.x", + "utils-merge": "1.x.x" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" + } + }, + "node_modules/passport-openid-oauth20": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/passport-openid-oauth20/-/passport-openid-oauth20-1.2.6.tgz", + "integrity": "sha512-L9OMSH/sT73gvk0TLU2UaWb1Gk5KqQB4c9penDTtpZGw6czzznaiA+xPzOAygGtqAIcfQXbW0d3e/UItxjoODQ==", + "dependencies": { + "passport-oauth2": "^1.5.0" + } + }, + "node_modules/passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -4644,6 +4744,11 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==" }, + "node_modules/pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -4783,6 +4888,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/random-bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", + "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -5571,6 +5684,22 @@ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" }, + "node_modules/uid-safe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", + "dependencies": { + "random-bytes": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/uid2": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.4.tgz", + "integrity": "sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==" + }, "node_modules/undefsafe": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", diff --git a/server/package.json b/server/package.json index bc3a830..b0bf7a3 100644 --- a/server/package.json +++ b/server/package.json @@ -17,10 +17,14 @@ "cors": "^2.8.5", "dotenv": "^16.4.4", "express": "^4.18.2", + "express-session": "^1.18.0", "jsonwebtoken": "^9.0.2", "mongodb": "^6.3.0", "multer": "^1.4.5-lts.1", "nodemailer": "^6.9.9", + "passport": "^0.7.0", + "passport-oauth2": "^1.8.0", + "passport-openid-oauth20": "^1.2.6", "socket.io": "^4.7.2", "socket.io-client": "^4.7.2" },