Merge pull request #267 from ets-cfuhrman-pfe/restore-reverted-sso-pr
Some checks failed
CI/CD Pipeline for Backend / build_and_push_backend (push) Failing after 1m48s
CI/CD Pipeline for Nginx Router / build_and_push_nginx (push) Failing after 49s
CI/CD Pipeline for Frontend / build_and_push_frontend (push) Failing after 17s
Tests / lint-and-tests (client) (push) Failing after 1m24s
Tests / lint-and-tests (server) (push) Failing after 55s

Ajustement callback URI
This commit is contained in:
Christopher (Cris) Fuhrman 2025-03-03 11:29:24 -05:00 committed by GitHub
commit 1317d3b1af
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 174 additions and 56 deletions

View file

@ -15,7 +15,7 @@ const OAuthCallback: React.FC = () => {
if (user) {
apiService.saveToken(user);
apiService.saveUsername(username || "");
navigate('/');
navigate('/teacher/dashboard');
} else {
navigate('/login');
}

View file

@ -12,7 +12,7 @@ const Register: React.FC = () => {
const [name, setName] = useState(''); // State for name
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [roles, setRoles] = useState<string[]>(['student']); // Set 'student' as the default role
const [roles, setRoles] = useState<string[]>(['teacher']); // Set 'student' as the default role
const [connectionError, setConnectionError] = useState<string>('');
const [isConnecting] = useState<boolean>(false);

109
docker-compose-local.yaml Normal file
View file

@ -0,0 +1,109 @@
version: '3'
services:
frontend:
build:
context: ./client
dockerfile: Dockerfile
container_name: frontend
ports:
- "5173:5173"
restart: always
backend:
build:
context: ./server
dockerfile: Dockerfile
container_name: backend
ports:
- "3000:3000"
environment:
PORT: 3000
MONGO_URI: "mongodb://mongo:27017/evaluetonsavoir"
MONGO_DATABASE: evaluetonsavoir
EMAIL_SERVICE: gmail
SENDER_EMAIL: infoevaluetonsavoir@gmail.com
EMAIL_PSW: 'vvml wmfr dkzb vjzb'
JWT_SECRET: haQdgd2jp09qb897GeBZyJetC8ECSpbFJe
SESSION_Secret: 'lookMomImQuizzing'
SITE_URL: http://localhost
FRONTEND_PORT: 5173
USE_PORTS: false
AUTHENTICATED_ROOMS: false
volumes:
- ./server/auth_config.json:/usr/src/app/serveur/config/auth_config.json
depends_on:
- mongo
- keycloak
restart: always
# Ce conteneur sert de routeur pour assurer le bon fonctionnement de l'application
nginx:
image: fuhrmanator/evaluetonsavoir-routeur:latest
container_name: nginx
ports:
- "80:80"
depends_on:
- backend
- frontend
restart: always
# Ce conteneur est la base de données principale pour l'application
mongo:
image: mongo
container_name: mongo
ports:
- "27017:27017"
tty: true
volumes:
- mongodb_data:/data/db
restart: always
# Ce conteneur cherche des mises à jour à 5h du matin
watchtower:
image: containrrr/watchtower
container_name: watchtower
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
- TZ=America/Montreal
- WATCHTOWER_CLEANUP=true
- WATCHTOWER_DEBUG=true
- WATCHTOWER_INCLUDE_RESTARTING=true
- WATCHTOWER_SCHEDULE=0 0 5 * * * # At 5 am everyday
restart: always
watchtower-once:
image: containrrr/watchtower
container_name: watchtower-once
volumes:
- /var/run/docker.sock:/var/run/docker.sock
command: --run-once
environment:
- TZ=America/Montreal
- WATCHTOWER_CLEANUP=true
- WATCHTOWER_DEBUG=true
- WATCHTOWER_INCLUDE_RESTARTING=true
restart: "no"
keycloak:
container_name: keycloak
image: quay.io/keycloak/keycloak:latest
environment:
KEYCLOAK_ADMIN: admin
KEYCLOAK_ADMIN_PASSWORD: admin123
KC_HEALTH_ENABLED: 'true'
KC_FEATURES: preview
ports:
- "8080:8080"
volumes:
- ./oauth-tester/config.json:/opt/keycloak/data/import/realm-config.json
command:
- start-dev
- --import-realm
- --hostname-strict=false
volumes:
mongodb_data:
external: false

View file

@ -3,18 +3,19 @@ version: '3'
services:
frontend:
build:
context: ./client
dockerfile: Dockerfile
image: fuhrmanator/evaluetonsavoir-frontend:latest
container_name: frontend
environment:
# Define empty VITE_BACKEND_URL because it's production
- VITE_BACKEND_URL=
# Define empty VITE_BACKEND_SOCKET_URL so it will default to window.location.host
- VITE_BACKEND_SOCKET_URL=
ports:
- "5173:5173"
restart: always
backend:
build:
context: ./server
dockerfile: Dockerfile
image: fuhrmanator/evaluetonsavoir-backend:latest
container_name: backend
ports:
- "3000:3000"
@ -28,11 +29,12 @@ services:
JWT_SECRET: haQdgd2jp09qb897GeBZyJetC8ECSpbFJe
SESSION_Secret: 'lookMomImQuizzing'
SITE_URL: http://localhost
OIDC_URL: https://evalsa.etsmtl.ca
FRONTEND_PORT: 5173
USE_PORTS: false
AUTHENTICATED_ROOMS: false
volumes:
- ./server/auth_config.json:/usr/src/app/serveur/config/auth_config.json
- /opt/EvalueTonSavoir/auth_config.json:/usr/src/app/serveur/auth_config.json
depends_on:
- mongo
- keycloak
@ -98,7 +100,7 @@ services:
ports:
- "8080:8080"
volumes:
- ./oauth-tester/config.json:/opt/keycloak/data/import/realm-config.json
- /opt/EvalueTonSavoir/oauth-tester/config.json:/opt/keycloak/data/import/realm-config.json
command:
- start-dev
- --import-realm

View file

@ -41,7 +41,7 @@ class AuthManager{
async registerAuths(){
for(const module of this.modules){
try{
module.registerAuth(this.app)
module.registerAuth(this.app, this.simpleregister);
} catch(error){
console.error(`L'enregistrement du module ${module} a échoué.`);
console.error(`Error: ${error} `);
@ -56,7 +56,16 @@ class AuthManager{
console.info(`L'utilisateur '${userInfo.name}' vient de se connecter`)
}
// eslint-disable-next-line no-unused-vars
async loginSimple(email,pswd,req,res,next){ //passport and simpleauth use next
const userInfo = await this.simpleregister.login(email, pswd);
const tokenToSave = jwt.create(userInfo.email, userInfo._id,userInfo.roles);
res.redirect(`/auth/callback?user=${tokenToSave}&username=${userInfo.name}`);
console.info(`L'utilisateur '${userInfo.name}' vient de se connecter`)
}
async register(userInfos){
console.log(userInfos);
if (!userInfos.email || !userInfos.password) {
throw new AppError(MISSING_REQUIRED_PARAMETER);
}

View file

@ -1,6 +1,5 @@
var OAuth2Strategy = require('passport-oauth2')
var authUserAssoc = require('../../../models/authUserAssociation')
var users = require('../../../models/users')
var { hasNestedValue } = require('../../../utils')
class PassportOAuth {
@ -9,8 +8,8 @@ class PassportOAuth {
this.auth_name = auth_name
}
register(app, passport, endpoint, name, provider) {
const cb_url = `${process.env['BACKEND_URL']}${endpoint}/${name}/callback`
register(app, passport, endpoint, name, provider, userModel) {
const cb_url = `${process.env['OIDC_URL']}${endpoint}/${name}/callback`
const self = this
const scope = 'openid profile email offline_access' + ` ${provider.OAUTH_ADD_SCOPE}`;
@ -43,14 +42,14 @@ class PassportOAuth {
let user_account
if (user_association) {
user_account = await users.getById(user_association.user_id)
user_account = await userModel.getById(user_association.user_id)
}
else {
let user_id = await users.getId(received_user.email)
let user_id = await userModel.getId(received_user.email)
if (user_id) {
user_account = await users.getById(user_id);
user_account = await userModel.getById(user_id);
} else {
received_user.password = users.generatePassword()
received_user.password = userModel.generatePassword()
user_account = await self.passportjs.register(received_user)
}
await authUserAssoc.link(self.auth_name, received_user.auth_id, user_account._id)
@ -58,7 +57,7 @@ class PassportOAuth {
user_account.name = received_user.name
user_account.roles = received_user.roles
await users.editUser(user_account)
await userModel.editUser(user_account)
// Store the tokens in the session
req.session.oauth2Tokens = {

View file

@ -1,9 +1,9 @@
var OpenIDConnectStrategy = require('passport-openidconnect');
var authUserAssoc = require('../../../models/authUserAssociation');
var users = require('../../../models/users');
var { hasNestedValue } = require('../../../utils');
const { MISSING_OIDC_PARAMETER } = require('../../../constants/errorCodes.js');
const AppError = require('../../../middleware/AppError.js');
const expressListEndpoints = require('express-list-endpoints');
class PassportOpenIDConnect {
constructor(passportjs, auth_name) {
@ -21,13 +21,18 @@ class PassportOpenIDConnect {
}
}
async register(app, passport, endpoint, name, provider) {
async register(app, passport, endpoint, name, provider, userModel) {
const config = await this.getConfigFromConfigURL(name, provider)
const cb_url = `${process.env['BACKEND_URL']}${endpoint}/${name}/callback`
const self = this
const scope = 'openid profile email ' + `${provider.OIDC_ADD_SCOPE}`
const config = await this.getConfigFromConfigURL(name, provider);
const cb_url = `${process.env['OIDC_URL']}${endpoint}/${name}/callback`;
const self = this;
const scope = 'openid profile email ' + `${provider.OIDC_ADD_SCOPE}`;
console.log(config);
console.log('');
console.log(cb_url);
console.log('');
console.log(scope);
passport.use(name, new OpenIDConnectStrategy({
issuer: config.issuer,
authorizationURL: config.authorization_endpoint,
@ -49,7 +54,6 @@ class PassportOpenIDConnect {
roles: []
};
if (hasNestedValue(profile, provider.OIDC_ROLE_TEACHER_VALUE)) received_user.roles.push('teacher')
if (hasNestedValue(profile, provider.OIDC_ROLE_STUDENT_VALUE)) received_user.roles.push('student')
@ -57,14 +61,14 @@ class PassportOpenIDConnect {
let user_account
if (user_association) {
user_account = await users.getById(user_association.user_id)
user_account = await userModel.getById(user_association.user_id)
}
else {
let user_id = await users.getId(received_user.email)
let user_id = await userModel.getId(received_user.email)
if (user_id) {
user_account = await users.getById(user_id);
user_account = await userModel.getById(user_id);
} else {
received_user.password = users.generatePassword()
received_user.password = userModel.generatePassword()
user_account = await self.passportjs.register(received_user)
}
await authUserAssoc.link(self.auth_name, received_user.auth_id, user_account._id)
@ -72,7 +76,7 @@ class PassportOpenIDConnect {
user_account.name = received_user.name
user_account.roles = received_user.roles
await users.editUser(user_account);
await userModel.editUser(user_account);
return done(null, user_account);
} catch (error) {
@ -99,7 +103,8 @@ class PassportOpenIDConnect {
}
}
);
console.info(`Ajout de la connexion : ${name}(OIDC)`)
console.info(`Ajout de la connexion : ${name}(OIDC)`);
console.log(expressListEndpoints(app));
}
}

View file

@ -9,7 +9,7 @@ class PassportJs{
this.endpoint = "/api/auth"
}
async registerAuth(expressapp){
async registerAuth(expressapp, userModel){
expressapp.use(passport.initialize());
expressapp.use(passport.session());
@ -21,7 +21,7 @@ class PassportJs{
this.registerProvider(provider.type,auth_id)
}
try{
this.registeredProviders[provider.type].register(expressapp,passport,this.endpoint,name,provider)
this.registeredProviders[provider.type].register(expressapp,passport,this.endpoint,name,provider,userModel)
authprovider.create(auth_id)
} catch(error){
console.error(`La connexion ${name} de type ${provider.type} n'as pu être chargé.`);

View file

@ -32,7 +32,7 @@ class SimpleAuth {
email: req.body.email,
password: req.body.password,
roles: req.body.roles
}
};
let user = await self.authmanager.register(userInfos)
if (user) res.redirect("/login")
}
@ -52,11 +52,8 @@ class SimpleAuth {
error.statusCode = 400;
throw error;
}
const userModel = self.authmanager.getUserModel();
const user = userModel.login(email, password);
await self.authmanager.login(user, req, res, next);
await self.authmanager.loginSimple(email, password, req, res, next);
} catch (error) {
const statusCode = error.statusCode || 500;
const message = error.message || "An internal server error occurred";

View file

@ -14,7 +14,7 @@
}
}
],
"simple-login": {
"simpleauth": {
"enabled": true,
"name": "provider3",
"SESSION_SECRET": "your_session_secret"

View file

@ -53,20 +53,6 @@ class Users {
return user;
}
async login(userid) {
await this.db.connect();
const conn = this.db.getConnection();
const userCollection = conn.collection("users");
const user = await userCollection.findOne({ _id: userid });
if (!user) {
return false;
}
return user;
}
/*
async login(email, password) {
try {
await this.db.connect();
@ -95,7 +81,7 @@ class Users {
throw error;
}
}
*/
async resetPassword(email) {
const newPassword = this.generatePassword();

View file

@ -14,6 +14,7 @@
"cors": "^2.8.5",
"dotenv": "^16.4.4",
"express": "^4.18.2",
"express-list-endpoints": "^7.1.1",
"express-session": "^1.18.0",
"jsonwebtoken": "^9.0.2",
"mongodb": "^6.3.0",
@ -3247,6 +3248,15 @@
"url": "https://opencollective.com/express"
}
},
"node_modules/express-list-endpoints": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/express-list-endpoints/-/express-list-endpoints-7.1.1.tgz",
"integrity": "sha512-SA6YHH1r6DrioJ4fFJNqiwu1FweGFqJZO9KBApMzwPosoSGPOX2AW0wiMepOXjojjEXDuP9whIvckomheErbJA==",
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/express-session": {
"version": "1.18.1",
"resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.1.tgz",

View file

@ -18,6 +18,7 @@
"cors": "^2.8.5",
"dotenv": "^16.4.4",
"express": "^4.18.2",
"express-list-endpoints": "^7.1.1",
"express-session": "^1.18.0",
"jsonwebtoken": "^9.0.2",
"mongodb": "^6.3.0",