Files
overleaf-cep/services/web/modules/authentication/oidc/app/src/OIDCAuthenticationManager.mjs
T

109 lines
4.1 KiB
JavaScript

import Settings from '@overleaf/settings'
import UserCreator from '../../../../../app/src/Features/User/UserCreator.mjs'
import ThirdPartyIdentityManager from '../../../../../app/src/Features/User/ThirdPartyIdentityManager.js'
import { ParallelLoginError } from '../../../../../app/src/Features/Authentication/AuthenticationErrors.js'
import { User } from '../../../../../app/src/models/User.js'
const OIDCAuthenticationManager = {
async findOrCreateUser(profile, auditLog) {
const {
attUserId,
attAdmin,
valAdmin,
updateUserDetailsOnLogin,
providerId,
} = Settings.oidc
const email = profile.emails[0].value
const oidcUserId = (attUserId === 'email') ? email : profile[attUserId]
const firstName = profile.name?.givenName || ""
const lastName = profile.name?.familyName || ""
let isAdmin = false
if (attAdmin && valAdmin) {
if (attAdmin === 'email') {
isAdmin = (email === valAdmin)
} else {
isAdmin = (profile[attAdmin] === valAdmin)
}
}
const oidcUserData = null // Possibly it can be used later
let user
try {
user = await ThirdPartyIdentityManager.promises.login(providerId, oidcUserId, oidcUserData)
} catch {
// A user with the specified OIDC ID and provider ID is not found. Search for a user with the given email.
// If no user exists with this email, create a new user and link the OIDC account to it (provided this is allowed by allowedOIDCEmailDomains).
// If a user exists but no account from the specified OIDC provider is linked to this user, link the OIDC account to this user.
// If an account from the specified provider is already linked to this user, unlink it, and link the OIDC account to this user.
// (Is it safe? Concider: If an account from the specified provider is already linked to this user, throw an error)
user = await User.findOne({ 'email': email }).exec()
if (!user) {
const allowedDomains = Settings.oidc.allowedOIDCEmailDomains
if (
allowedDomains &&
!allowedDomains.some(pattern => {
const domain = email.split('@')[1]
if (pattern.startsWith('*.')) {
const base = pattern.slice(2)
return domain.endsWith(`.${base}`)
}
return domain === pattern
})
) {
return null
}
user = await UserCreator.promises.createNewUser(
{
email: email,
first_name: firstName,
last_name: lastName,
isAdmin: isAdmin,
holdingAccount: false,
}
)
}
// const alreadyLinked = user.thirdPartyIdentifiers.some(item => item.providerId === providerId)
// if (!alreadyLinked) {
auditLog.initiatorId = user._id
await ThirdPartyIdentityManager.promises.link(user._id, providerId, oidcUserId, oidcUserData, auditLog)
await User.updateOne(
{ _id: user._id },
{ $set : {
'emails.0.confirmedAt': Date.now(), //email of external user is confirmed
},
}
).exec()
// } else {
// throw new Error(`Overleaf user ${user.email} is already linked to another ${providerId} user`)
// }
}
let userDetails = updateUserDetailsOnLogin ? { first_name : firstName, last_name: lastName } : {}
if (attAdmin && valAdmin) {
user.isAdmin = isAdmin
userDetails.isAdmin = isAdmin
}
const result = await User.updateOne(
{ _id: user._id, loginEpoch: user.loginEpoch }, { $inc: { loginEpoch: 1 }, $set: userDetails },
{}
).exec()
if (result.modifiedCount !== 1) {
throw new ParallelLoginError()
}
return user
},
async linkAccount(userId, profile, auditLog) {
const {
attUserId,
providerId,
} = Settings.oidc
const oidcUserId = (attUserId === 'email') ? profile.emails[0].value : profile[attUserId]
const oidcUserData = null // Possibly it can be used later
await ThirdPartyIdentityManager.promises.link(userId, providerId, oidcUserId, oidcUserData, auditLog)
},
}
export default {
promises: OIDCAuthenticationManager,
}