@@ -8,6 +8,7 @@ import bodyParserPkg from 'body-parser'
88import { fromServerConfig } from '../../models/oidc-manager.mjs'
99import { LoginRequest } from '../../requests/login-request.mjs'
1010import { SharingRequest } from '../../requests/sharing-request.mjs'
11+ import debug from '../../debug.mjs'
1112
1213import restrictToTopDomain from '../../handlers/restrict-to-top-domain.mjs'
1314
@@ -21,6 +22,84 @@ const { urlencoded } = bodyParserPkg
2122const bodyParser = urlencoded ( { extended : false } )
2223const { AuthCallbackRequest } = oidcAuthManager . handlers
2324
25+ function oidcCookieNames ( req ) {
26+ const provider = req . app ?. locals ?. oidc ?. provider
27+ if ( ! provider || typeof provider . configuration !== 'function' ) {
28+ return [ ]
29+ }
30+
31+ const cookieNames = provider . configuration ( 'cookies' ) ?. names || { }
32+ const baseNames = Object . values ( cookieNames ) . filter ( Boolean )
33+ const expandedNames = baseNames . flatMap ( name => [
34+ name ,
35+ `${ name } .sig` ,
36+ `${ name } .legacy` ,
37+ `${ name } .legacy.sig`
38+ ] )
39+
40+ return Array . from ( new Set ( expandedNames ) )
41+ }
42+
43+ function cookieNamesFromRequest ( req ) {
44+ const cookieHeader = req . headers ?. cookie
45+ if ( ! cookieHeader ) {
46+ return [ ]
47+ }
48+
49+ return cookieHeader
50+ . split ( ';' )
51+ . map ( fragment => fragment . trim ( ) )
52+ . filter ( Boolean )
53+ . map ( fragment => fragment . split ( '=' ) [ 0 ] . trim ( ) )
54+ . filter ( Boolean )
55+ }
56+
57+ function clearAuthCookies ( res , domain , cookieNames ) {
58+ const cookiePaths = [ '/' , '/.oidc' ]
59+ const allCookieNames = Array . from ( new Set ( [
60+ 'nssidp.sid' ,
61+ 'nssidp.sid.sig' ,
62+ 'nssidp.sid.legacy' ,
63+ 'nssidp.sid.legacy.sig' ,
64+ ...( cookieNames || [ ] )
65+ ] ) )
66+
67+ for ( const path of cookiePaths ) {
68+ const noDomainOptions = { path }
69+ for ( const name of allCookieNames ) {
70+ res . clearCookie ( name , noDomainOptions )
71+ }
72+
73+ if ( domain ) {
74+ const domainOptions = { domain, path }
75+ for ( const name of allCookieNames ) {
76+ res . clearCookie ( name , domainOptions )
77+ }
78+ }
79+ }
80+ }
81+
82+ function renderGoodbyeAndClearSession ( req , res , next ) {
83+ const domain = req . session ?. cookie ?. domain
84+ const providerCookieNames = oidcCookieNames ( req )
85+ const incomingCookieNames = cookieNamesFromRequest ( req )
86+ const cookiesToClear = Array . from ( new Set ( [ ...providerCookieNames , ...incomingCookieNames ] ) )
87+
88+ if ( ! req . session ) {
89+ clearAuthCookies ( res , domain , cookiesToClear )
90+ return res . render ( 'auth/goodbye' )
91+ }
92+
93+ req . session . destroy ( ( err ) => {
94+ if ( err ) {
95+ return next ( err )
96+ }
97+
98+ clearAuthCookies ( res , domain , cookiesToClear )
99+ res . render ( 'auth/goodbye' )
100+ } )
101+ }
102+
24103/**
25104 * Sets up OIDC authentication for the given app.
26105 *
@@ -102,25 +181,21 @@ export function middleware (oidc) {
102181 router . get ( '/account/password/change' , restrictToTopDomain , PasswordChangeRequest . get )
103182 router . post ( '/account/password/change' , restrictToTopDomain , bodyParser , PasswordChangeRequest . post )
104183
105- router . get ( [ '/.well-known/solid/logout' , '/.well-known/solid/logout/' ] , ( req , res ) => res . redirect ( '/logout' ) )
106-
107- router . get ( '/goodbye' , ( req , res , next ) => {
108- if ( ! req . session ) {
109- return res . render ( 'auth/goodbye' )
110- }
111-
112- const domain = req . session . cookie && req . session . cookie . domain
113- req . session . destroy ( ( err ) => {
114- if ( err ) {
115- return next ( err )
116- }
184+ router . get ( [
185+ '/.well-known/solid/logout' ,
186+ '/.well-known/solid/logout/' ,
187+ '/solid/logout' ,
188+ '/solid/logout/'
189+ ] , ( req , res ) => {
190+ res . redirect ( '/goodbye' )
191+ } )
117192
118- const cookieOptions = domain ? { domain, path : '/' } : { path : '/' }
119- res . clearCookie ( 'nssidp.sid' , cookieOptions )
120- res . render ( 'auth/goodbye' )
121- } )
193+ router . get ( [ '/logout' , '/logout/' ] , ( req , res ) => {
194+ res . redirect ( '/goodbye' )
122195 } )
123196
197+ router . get ( '/goodbye' , renderGoodbyeAndClearSession )
198+
124199 // The relying party callback is called at the end of the OIDC signin process
125200 router . get ( '/api/oidc/rp/:issuer_id' , AuthCallbackRequest . get )
126201
0 commit comments