| const passport = require('passport'); | |
| const { Issuer, Strategy: OpenIDStrategy } = require('openid-client'); | |
| const axios = require('axios'); | |
| const fs = require('fs'); | |
| const path = require('path'); | |
| const config = require('../../config/loader'); | |
| const domains = config.domains; | |
| const User = require('../models/User'); | |
| let crypto; | |
| try { | |
| crypto = require('node:crypto'); | |
| } catch (err) { | |
| console.error('crypto support is disabled!'); | |
| } | |
| const downloadImage = async (url, imagePath, accessToken) => { | |
| try { | |
| const response = await axios.get(url, { | |
| headers: { | |
| Authorization: `Bearer ${accessToken}`, | |
| }, | |
| responseType: 'arraybuffer', | |
| }); | |
| fs.mkdirSync(path.dirname(imagePath), { recursive: true }); | |
| fs.writeFileSync(imagePath, response.data); | |
| const fileName = path.basename(imagePath); | |
| return `/images/openid/${fileName}`; | |
| } catch (error) { | |
| console.error(`Error downloading image at URL "${url}": ${error}`); | |
| return ''; | |
| } | |
| }; | |
| async function setupOpenId() { | |
| try { | |
| const issuer = await Issuer.discover(process.env.OPENID_ISSUER); | |
| const client = new issuer.Client({ | |
| client_id: process.env.OPENID_CLIENT_ID, | |
| client_secret: process.env.OPENID_CLIENT_SECRET, | |
| redirect_uris: [domains.server + process.env.OPENID_CALLBACK_URL], | |
| }); | |
| const openidLogin = new OpenIDStrategy( | |
| { | |
| client, | |
| params: { | |
| scope: process.env.OPENID_SCOPE, | |
| }, | |
| }, | |
| async (tokenset, userinfo, done) => { | |
| try { | |
| let user = await User.findOne({ openidId: userinfo.sub }); | |
| if (!user) { | |
| user = await User.findOne({ email: userinfo.email }); | |
| } | |
| let fullName = ''; | |
| if (userinfo.given_name && userinfo.family_name) { | |
| fullName = userinfo.given_name + ' ' + userinfo.family_name; | |
| } else if (userinfo.given_name) { | |
| fullName = userinfo.given_name; | |
| } else if (userinfo.family_name) { | |
| fullName = userinfo.family_name; | |
| } else { | |
| fullName = userinfo.username || userinfo.email; | |
| } | |
| if (!user) { | |
| user = new User({ | |
| provider: 'openid', | |
| openidId: userinfo.sub, | |
| username: userinfo.username || userinfo.given_name || '', | |
| email: userinfo.email || '', | |
| emailVerified: userinfo.email_verified || false, | |
| name: fullName, | |
| }); | |
| } else { | |
| user.provider = 'openid'; | |
| user.openidId = userinfo.sub; | |
| user.username = userinfo.given_name || ''; | |
| user.name = fullName; | |
| } | |
| if (userinfo.picture) { | |
| const imageUrl = userinfo.picture; | |
| let fileName; | |
| if (crypto) { | |
| const hash = crypto.createHash('sha256'); | |
| hash.update(userinfo.sub); | |
| fileName = hash.digest('hex') + '.png'; | |
| } else { | |
| fileName = userinfo.sub + '.png'; | |
| } | |
| const imagePath = path.join( | |
| __dirname, | |
| '..', | |
| '..', | |
| 'client', | |
| 'public', | |
| 'images', | |
| 'openid', | |
| fileName, | |
| ); | |
| const imagePathOrEmpty = await downloadImage( | |
| imageUrl, | |
| imagePath, | |
| tokenset.access_token, | |
| ); | |
| user.avatar = imagePathOrEmpty; | |
| } else { | |
| user.avatar = ''; | |
| } | |
| await user.save(); | |
| done(null, user); | |
| } catch (err) { | |
| done(err); | |
| } | |
| }, | |
| ); | |
| passport.use('openid', openidLogin); | |
| } catch (err) { | |
| console.error(err); | |
| } | |
| } | |
| module.exports = setupOpenId; | |