Refactor authentication code; add OIDC support

This commit is contained in:
yu-i-i
2026-07-07 14:42:49 +02:00
parent 81d51d1498
commit cfeed73e52
48 changed files with 1169 additions and 606 deletions
@@ -110,6 +110,7 @@ const AuthenticationController = {
alphaProgram: user.alphaProgram || undefined, // only store if set
betaProgram: user.betaProgram || undefined, // only store if set
labsProgram: user.labsProgram || undefined, // only store if set
externalAuth: user.externalAuth || false,
}
if (user.isAdmin) {
lightUser.isAdmin = true
@@ -128,9 +129,9 @@ const AuthenticationController = {
// so we can send back our custom `{message: {text: "", type: ""}}` responses on failure,
// and send a `{redir: ""}` response on success
passport.authenticate(
Settings.ldap?.enable ? ['custom-fail-ldapauth','local'] : ['local'],
'local',
{ keepSessionInfo: true },
async function (err, user, infoArray) {
async function (err, user, info) {
if (err) {
return next(err)
}
@@ -152,7 +153,6 @@ const AuthenticationController = {
return next(err)
}
} else {
let info = infoArray[0]
if (info.redir != null) {
return res.json({ redir: info.redir })
} else {
@@ -158,10 +158,6 @@ async function requestReset(req, res, next) {
return res.status(404).json({
message: req.i18n.translate('secondary_email_password_reset'),
})
} else if (status === 'external') {
return res.status(403).json({
message: req.i18n.translate('password_managed_externally'),
})
} else {
return res.status(404).json({
message: req.i18n.translate('cant_find_email'),
@@ -18,10 +18,6 @@ async function generateAndEmailResetToken(email) {
return null
}
if (!user.hashedPassword) {
return 'external'
}
if (user.email !== email) {
return 'secondary'
}
@@ -76,6 +72,7 @@ async function getUserForPasswordResetToken(token) {
'overleaf.id': 1,
email: 1,
must_reconfirm: 1,
hashedPassword: 1,
})
await assertUserPermissions(user, ['change-password'])
@@ -453,7 +453,7 @@ async function updateUserSettings(req, res, next) {
if (
newEmail == null ||
newEmail === user.email ||
(req.externalAuthenticationSystemUsed() && !user.hashedPassword)
req.externalAuthenticationSystemUsed()
) {
// end here, don't update email
SessionManager.setInSessionUser(req.session, {
@@ -540,7 +540,6 @@ async function doLogout(req) {
}
async function logout(req, res, next) {
if (req?.session.saml_extce) return res.redirect(308, '/saml/logout')
const requestedRedirect = req.body.redirect
? UrlHelper.getSafeRedirectPath(req.body.redirect)
: undefined
@@ -52,10 +52,8 @@ async function settingsPage(req, res) {
const reconfirmedViaSAML = _.get(req.session, ['saml', 'reconfirmed'])
delete req.session.saml
let shouldAllowEditingDetails = true
if (Settings.ldap && Settings.ldap.updateUserDetailsOnLogin) {
shouldAllowEditingDetails = false
}
if (Settings.saml && Settings.saml.updateUserDetailsOnLogin) {
const externalAuth = req.user.externalAuth
if (externalAuth && Settings[externalAuth].updateUserDetailsOnLogin) {
shouldAllowEditingDetails = false
}
const oauthProviders = Settings.oauthProviders || {}
@@ -116,9 +116,9 @@ export default async function (webRouter, privateApiRouter, publicApiRouter) {
webRouter.use(function (req, res, next) {
req.externalAuthenticationSystemUsed =
Features.externalAuthenticationSystemUsed
() => !!req?.user?.externalAuth
res.locals.externalAuthenticationSystemUsed =
Features.externalAuthenticationSystemUsed
() => !!req?.user?.externalAuth
req.hasFeature = res.locals.hasFeature = Features.hasFeature
next()
})
+2 -2
View File
@@ -236,6 +236,8 @@ async function initialize(webRouter, privateApiRouter, publicApiRouter) {
CaptchaMiddleware.canSkipCaptcha
)
await Modules.applyRouter(webRouter, privateApiRouter, publicApiRouter)
webRouter.get('/login', UserPagesController.loginPage)
AuthenticationController.addEndpointToLoginWhitelist('/login')
@@ -305,8 +307,6 @@ async function initialize(webRouter, privateApiRouter, publicApiRouter) {
TokenAccessRouter.apply(webRouter)
HistoryRouter.apply(webRouter, privateApiRouter)
await Modules.applyRouter(webRouter, privateApiRouter, publicApiRouter)
if (Settings.enableSubscriptions) {
webRouter.get(
'/user/bonus',
+9
View File
@@ -57,3 +57,12 @@ block content
)
span(data-ol-inflight="idle") #{settings.saml.identityServiceName}
span(hidden data-ol-inflight="pending") #{translate("logging_in")}…
if settings.oidc && settings.oidc.enable
form(data-ol-async-form, name="oidcLoginForm")
.actions(style='margin-top: 30px;')
a.btn.btn-secondary.btn-block(
href='/oidc/login',
data-ol-disabled-inflight
)
span(data-ol-inflight="idle") #{settings.oidc.identityServiceName}
span(hidden data-ol-inflight="pending") #{translate("logging_in")}…
@@ -50,7 +50,7 @@ block content
+notification({ariaLive: 'assertive', type: 'error', className: 'mb-3', content: translate(error)})
div(data-ol-custom-form-message='no-password-allowed-due-to-sso' hidden)
+notification({ariaLive: 'polite', type: 'error', className: 'mb-3', content: translate('you_cant_reset_password_due_to_sso', {}, [{name: 'a', attrs: {href: '/sso-login'}}])})
+notification({ariaLive: 'polite', type: 'error', className: 'mb-3', content: translate('you_cant_reset_password_due_to_ldap_or_sso')})
input(name='_csrf' type='hidden' value=csrfToken)
.form-group.mb-3
label.form-label(for='email') #{translate("email")}
+2 -2
View File
@@ -11,7 +11,7 @@ block append meta
meta(
name='ol-shouldAllowEditingDetails'
data-type='boolean'
content=shouldAllowEditingDetails || hasPassword
content=shouldAllowEditingDetails
)
meta(name='ol-oauthProviders' data-type='json' content=oauthProviders)
meta(name='ol-institutionLinked' data-type='json' content=institutionLinked)
@@ -34,7 +34,7 @@ block append meta
meta(
name='ol-isExternalAuthenticationSystemUsed'
data-type='boolean'
content=externalAuthenticationSystemUsed() && !hasPassword
content=externalAuthenticationSystemUsed()
)
meta(name='ol-user' data-type='json' content=user)
meta(name='ol-showAiFeatures' data-type='boolean' content=showAiFeatures)