diff --git a/api/src/routes/auth.ts b/api/src/routes/auth.ts index d688290..3b34c23 100644 --- a/api/src/routes/auth.ts +++ b/api/src/routes/auth.ts @@ -10,10 +10,14 @@ import { Role } from '@app/shared/types/roles'; import pool from '../db'; import { requireLogin } from '../middleware/auth'; import { getUserRoles } from '../services/rolesService'; -import { getUserState } from '../services/memberService'; +import { getUserState, mapDiscordtoID } from '../services/memberService'; import { MemberState } from '@app/shared/types/member'; +import { toDateTime } from '@app/shared/utils/time'; const querystring = require('querystring'); +function parseJwt(token) { + return JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString()); +} passport.use(new OpenIDConnectStrategy({ issuer: process.env.AUTH_ISSUER, @@ -23,16 +27,17 @@ passport.use(new OpenIDConnectStrategy({ clientID: process.env.AUTH_CLIENT_ID, clientSecret: process.env.AUTH_CLIENT_SECRET, callbackURL: process.env.AUTH_REDIRECT_URI, - scope: ['openid', 'profile'] + scope: ['openid', 'profile', 'discord'] }, async function verify(issuer, sub, profile, jwtClaims, accessToken, refreshToken, params, cb) { // console.log('--- OIDC verify() called ---'); // console.log('issuer:', issuer); // console.log('sub:', sub); - // // console.log('profile:', JSON.stringify(profile, null, 2)); + // // console.log('discord:', discord); // console.log('profile:', profile); - // console.log('id_token claims:', JSON.stringify(jwtClaims, null, 2)); - // console.log('preferred_username:', jwtClaims?.preferred_username); + // console.log('jwt: ', parseJwt(jwtClaims)); + // console.log('params:', params); + try { var con = await pool.getConnection(); @@ -41,20 +46,37 @@ passport.use(new OpenIDConnectStrategy({ //lookup existing user const existing = await con.query(`SELECT id FROM members WHERE authentik_issuer = ? AND authentik_sub = ? LIMIT 1;`, [issuer, sub]); - let memberId; + let memberId: number; //if member exists if (existing.length > 0) { memberId = existing[0].id; } else { //otherwise: create account - const username = sub.username; + const jwt = parseJwt(jwtClaims); + const discordID = jwt.discord.id as number; - const result = await con.query( - `INSERT INTO members (name, authentik_sub, authentik_issuer) VALUES (?, ?, ?)`, - [username, sub, issuer] - ) - memberId = Number(result.insertId); + //check if account is available to claim + memberId = await mapDiscordtoID(discordID); + + if (memberId === null) { + // create new account + const username = sub.username; + const result = await con.query( + `INSERT INTO members (name, authentik_sub, authentik_issuer) VALUES (?, ?, ?)`, + [username, sub, issuer] + ) + memberId = Number(result.insertId); + } else { + // claim existing account + const result = await con.query( + `UPDATE members SET authentik_sub = ?, authentik_issuer = ? WHERE id = ?;`, + [sub, issuer, memberId] + ) + } } + + await con.query(`UPDATE members SET last_login = ? WHERE id = ?`, [toDateTime(new Date()), memberId]) + await con.commit(); return cb(null, { memberId }); } catch (error) { @@ -116,11 +138,10 @@ passport.deserializeUser(function (user, cb) { var userData: { id: number, name: string, roles: Role[], state: MemberState }; try { var con = await pool.getConnection(); - let userResults = await con.query(`SELECT id, name FROM members WHERE id = ?;`, [memberID]) userData = userResults[0]; let userRoles = await getUserRoles(memberID); - userData.roles = userRoles; + userData.roles = userRoles || []; userData.state = await getUserState(memberID); } catch (error) { console.error(error) diff --git a/api/src/routes/members.ts b/api/src/routes/members.ts index 26e3fa8..3e574aa 100644 --- a/api/src/routes/members.ts +++ b/api/src/routes/members.ts @@ -38,18 +38,11 @@ router.get('/me', [requireLogin], async (req, res) => { try { const { id, name, state } = await getUserData(req.user.id); - // const LOAData = await pool.query( - // `SELECT * - // FROM leave_of_absences - // WHERE member_id = ? - // AND deleted = 0 - // AND UTC_TIMESTAMP() BETWEEN start_date AND end_date;`, req.user.id); const LOAData = await getUserActiveLOA(req.user.id); const roleData = await getUserRoles(req.user.id); const userDataFull = { id, name, state, LOAData, roleData }; - console.log(userDataFull) res.status(200).json(userDataFull); } catch (error) { console.error('Error fetching user data:', error); diff --git a/api/src/services/memberService.ts b/api/src/services/memberService.ts index 66b5917..6381ad1 100644 --- a/api/src/services/memberService.ts +++ b/api/src/services/memberService.ts @@ -15,9 +15,8 @@ export async function setUserState(userID: number, state: MemberState) { } export async function getUserState(user: number): Promise { - let out = await pool.query(`SELECT state FROM members WHERE id = ?`, [user]); - console.log('hi') - return (out[0].state as MemberState); + let out = await pool.query(`SELECT state FROM members WHERE id = ?`, [user]); + return (out[0].state as MemberState); } export async function getMemberSettings(id: number): Promise { @@ -54,4 +53,10 @@ export async function getMembersFull(ids: number[]): Promise { const sql = `SELECT * FROM view_member_rank_unit_status_latest WHERE member_id IN (?);`; const res: Member[] = await pool.query(sql, [ids]); return res; +} + +export async function mapDiscordtoID(id: number): Promise { + const sql = `SELECT id FROM members WHERE discord_id = ?;` + let res = await pool.query(sql, [id]); + return res.length > 0 ? res[0].id : null; } \ No newline at end of file