Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 92 additions & 2 deletions lib/api/authn/webid-oidc.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import { fromServerConfig } from '../../models/oidc-manager.mjs'
import { LoginRequest } from '../../requests/login-request.mjs'
import { SharingRequest } from '../../requests/sharing-request.mjs'
import debug from '../../debug.mjs'

Check warning on line 11 in lib/api/authn/webid-oidc.mjs

View workflow job for this annotation

GitHub Actions / build (^22.14.0, ubuntu-latest)

'debug' is defined but never used

import restrictToTopDomain from '../../handlers/restrict-to-top-domain.mjs'

Expand All @@ -21,6 +22,84 @@
const bodyParser = urlencoded({ extended: false })
const { AuthCallbackRequest } = oidcAuthManager.handlers

function oidcCookieNames (req) {
const provider = req.app?.locals?.oidc?.provider
if (!provider || typeof provider.configuration !== 'function') {
return []
}

const cookieNames = provider.configuration('cookies')?.names || {}
const baseNames = Object.values(cookieNames).filter(Boolean)
const expandedNames = baseNames.flatMap(name => [
name,
`${name}.sig`,
`${name}.legacy`,
`${name}.legacy.sig`
])

return Array.from(new Set(expandedNames))
}

function cookieNamesFromRequest (req) {
const cookieHeader = req.headers?.cookie
if (!cookieHeader) {
return []
}

return cookieHeader
.split(';')
.map(fragment => fragment.trim())
.filter(Boolean)
.map(fragment => fragment.split('=')[0].trim())
.filter(Boolean)
}

function clearAuthCookies (res, domain, cookieNames) {
const cookiePaths = ['/', '/.oidc']
const allCookieNames = Array.from(new Set([
'nssidp.sid',
'nssidp.sid.sig',
'nssidp.sid.legacy',
'nssidp.sid.legacy.sig',
...(cookieNames || [])
]))

for (const path of cookiePaths) {
const noDomainOptions = { path }
for (const name of allCookieNames) {
res.clearCookie(name, noDomainOptions)
}

if (domain) {
const domainOptions = { domain, path }
for (const name of allCookieNames) {
res.clearCookie(name, domainOptions)
}
}
}
}

function renderGoodbyeAndClearSession (req, res, next) {
const domain = req.session?.cookie?.domain
const providerCookieNames = oidcCookieNames(req)
const incomingCookieNames = cookieNamesFromRequest(req)
const cookiesToClear = Array.from(new Set([...providerCookieNames, ...incomingCookieNames]))

if (!req.session) {
clearAuthCookies(res, domain, cookiesToClear)
return res.render('auth/goodbye')
}

req.session.destroy((err) => {
if (err) {
return next(err)
}

clearAuthCookies(res, domain, cookiesToClear)
res.render('auth/goodbye')
})
}

/**
* Sets up OIDC authentication for the given app.
*
Expand Down Expand Up @@ -102,9 +181,20 @@
router.get('/account/password/change', restrictToTopDomain, PasswordChangeRequest.get)
router.post('/account/password/change', restrictToTopDomain, bodyParser, PasswordChangeRequest.post)

router.get('/.well-known/solid/logout/', (req, res) => res.redirect('/logout'))
router.get([
'/.well-known/solid/logout',
'/.well-known/solid/logout/',
'/solid/logout',
'/solid/logout/'
], (req, res) => {
res.redirect('/goodbye')
})

router.get(['/logout', '/logout/'], (req, res) => {
res.redirect('/goodbye')
})

router.get('/goodbye', (req, res) => { res.render('auth/goodbye') })
router.get('/goodbye', renderGoodbyeAndClearSession)

// The relying party callback is called at the end of the OIDC signin process
router.get('/api/oidc/rp/:issuer_id', AuthCallbackRequest.get)
Expand Down
7 changes: 6 additions & 1 deletion lib/create-app.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,12 @@ function initLoggers () {
function isLogoutRequest (req) {
// TODO: this is a hack that hard-codes OIDC paths,
// this code should live in the OIDC module
return req.path === '/logout' || req.path === '/goodbye'
return req.path === '/logout' ||
req.path === '/goodbye' ||
req.path === '/solid/logout' ||
req.path === '/solid/logout/' ||
req.path === '/.well-known/solid/logout' ||
req.path === '/.well-known/solid/logout/'
}

/**
Expand Down
Loading
Loading