Skip to content

Commit 2400f44

Browse files
Redirect to provider /logout to clear http-only cookies
1 parent ed1dead commit 2400f44

File tree

5 files changed

+244
-48
lines changed

5 files changed

+244
-48
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,8 @@ After `login()` is successful, the following variables are set:
9393

9494
##### logout
9595

96+
`logout()`
97+
98+
Clears the current user and tokens, and does a url redirect to the current
99+
RP client's provider's 'end session' endpoint. A redirect is done (instead of an
100+
ajax 'get') to enable the provider to clear any http-only session cookies.

demo/index.html

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
<link rel="stylesheet" href="./demo.css" />
1111
<!-- Relying Party lib, exports a global var: SolidAuth -->
1212
<!--<script src="./solid-client.min.js"></script>-->
13-
<script src="https://solid.github.io/releases/solid.js/solid-client-0.24.0-oidc.min.js"></script>
13+
<script src="https://solid.github.io/releases/solid.js/solid-client.min.js"></script>
1414
</head>
1515
<body>
1616
<div class="container">
@@ -59,15 +59,15 @@ <h5>App body</h5>
5959
// Trigger a currentUser() check on page load, in case user is logged in already
6060
document.addEventListener('DOMContentLoaded', function () {
6161
init()
62-
SolidClient.currentUser()
62+
SolidClient.auth.currentUser()
6363
.then(function (webId) {
6464
if (webId) { loginSuccess(webId) }
6565
})
6666
})
6767

6868
// This is bound to the Login button
6969
function login () {
70-
SolidClient.login()
70+
SolidClient.auth.login()
7171
.then(function (webId) {
7272
loginSuccess(webId)
7373
})
@@ -97,15 +97,8 @@ <h5>App body</h5>
9797
}
9898

9999
function logout () {
100-
SolidClient.logout()
101-
.then(function () {
102-
console.log('logged out')
103-
hide('logoutDiv')
104-
setField('webId', SolidClient.auth.webId)
105-
})
106-
.catch(function (err) {
107-
console.error('Error logging out: ', err)
108-
})
100+
console.log('Logging out...')
101+
SolidClient.auth.logout()
109102
}
110103

111104
function getRootAcl () {

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
},
5757
"homepage": "https://github.com/solid/solid-auth-oidc",
5858
"dependencies": {
59-
"oidc-rp": "git://github.com/anvilresearch/oidc-rp.git#rc-003"
59+
"oidc-rp": "git://github.com/anvilresearch/oidc-rp.git#rc-004"
6060
},
6161
"devDependencies": {
6262
"babel-cli": "^6.18.0",
@@ -65,6 +65,7 @@
6565
"babel-preset-es2015": "^6.18.0",
6666
"chai": "^3.5.0",
6767
"chai-as-promised": "^6.0.0",
68+
"dirty-chai": "^1.2.2",
6869
"localstorage-polyfill": "^1.0.1",
6970
"mocha": "^3.2.0",
7071
"nyc": "^10.2.0",

src/index.js

Lines changed: 81 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,14 @@ class ClientAuthOIDC {
6060

6161
/**
6262
* Returns the current window's URI
63-
* @return {string}
63+
*
64+
* @return {string|null}
6465
*/
6566
currentLocation () {
6667
let window = this.window
68+
69+
if (!window || !window.location) { return null }
70+
6771
return window.location.href
6872
}
6973

@@ -85,10 +89,30 @@ class ClientAuthOIDC {
8589
}
8690
}
8791

92+
/**
93+
* Returns the 'end session' api endpoint of the current RP client's provider
94+
* (e.g. 'https://example.com/logout'), if one is available.
95+
*
96+
* @return {string|null}
97+
*/
98+
providerEndSessionEndpoint () {
99+
let rp = this.currentClient
100+
101+
if (!rp || !rp.provider || !rp.provider.configuration) { return null }
102+
103+
let config = rp.provider.configuration
104+
105+
if (!config.end_session_endpoint) { return null }
106+
107+
return config.end_session_endpoint
108+
}
109+
88110
/**
89111
* Extracts and returns the `state` query or hash fragment param from a uri
112+
*
90113
* @param uri {string}
91-
* @param uriType {string} 'hash' or QUERY
114+
* @param uriType {string} 'hash' or 'query'
115+
*
92116
* @return {string|null} Value of the `state` query or hash fragment param
93117
*/
94118
extractState (uri, uriType = HASH) {
@@ -122,6 +146,7 @@ class ClientAuthOIDC {
122146

123147
/**
124148
* @param providerUri {string}
149+
*
125150
* @return {Promise<RelyingParty>}
126151
*/
127152
loadOrRegisterClient (providerUri) {
@@ -210,12 +235,24 @@ class ClientAuthOIDC {
210235
this.idToken = null
211236
}
212237

238+
/**
239+
* Clears the current user and tokens, and does a url redirect to the
240+
* current RP client's provider's 'end session' endpoint.
241+
* A redirect is done (instead of an ajax 'get') to enable the provider to
242+
* clear any http-only session cookies.
243+
*/
213244
logout () {
214245
this.clearCurrentUser()
215246

216-
if (!this.currentClient) { return Promise.resolve(null) }
247+
let logoutEndpoint = this.providerEndSessionEndpoint()
248+
249+
if (!logoutEndpoint) { return }
250+
251+
let logoutUrl = new URL(logoutEndpoint)
252+
253+
logoutUrl.searchParams.set('returnToUrl', this.currentLocation())
217254

218-
return this.currentClient.logout()
255+
this.redirectTo(logoutUrl.toString())
219256
}
220257

221258
/**
@@ -353,19 +390,26 @@ class ClientAuthOIDC {
353390

354391
/**
355392
* Validates the auth response in the current uri, initializes the current
356-
* user's ID Token and Access token, and returns the
393+
* user's ID Token and Access token, and returns the user's WebID
394+
*
357395
* @param client {RelyingParty}
396+
*
358397
* @throws {Error}
359-
* @returns {Promise<string>}
398+
*
399+
* @returns {Promise<string>} Current user's web id
360400
*/
361401
initUserFromResponse (client) {
362402
return client.validateResponse(this.currentLocation(), this.localStorage)
363403
.then(response => {
364404
this.idToken = response.params.id_token
365405
this.accessToken = response.params.access_token
406+
407+
this.clearAuthResponseFromUrl()
408+
366409
return this.extractAndValidateWebId(response.decoded)
367410
})
368411
.catch(error => {
412+
this.clearAuthResponseFromUrl()
369413
if (error.message === 'Cannot resolve signing key for ID Token.') {
370414
console.log('ID Token found, but could not validate. Provider likely has changed their public keys. Please retry login.')
371415
return null
@@ -377,7 +421,9 @@ class ClientAuthOIDC {
377421

378422
/**
379423
* @param idToken {IDToken}
424+
*
380425
* @throws {Error}
426+
*
381427
* @return {string}
382428
*/
383429
extractAndValidateWebId (idToken) {
@@ -386,6 +432,35 @@ class ClientAuthOIDC {
386432
return webId
387433
}
388434

435+
/**
436+
* Removes authentication response data (access token, id token etc) from
437+
* the current url's hash fragment.
438+
*/
439+
clearAuthResponseFromUrl () {
440+
let clearedUrl = this.currentLocationNoHash()
441+
442+
this.replaceCurrentUrl(clearedUrl)
443+
}
444+
445+
currentLocationNoHash () {
446+
let currentLocation = this.currentLocation()
447+
if (!currentLocation) { return null }
448+
449+
let currentUrl = new URL(this.currentLocation())
450+
currentUrl.hash = '' // remove the hash fragment
451+
let clearedUrl = currentUrl.toString()
452+
453+
return clearedUrl
454+
}
455+
456+
replaceCurrentUrl (newUrl) {
457+
let history = this.window.history
458+
459+
if (!history) { return }
460+
461+
history.replaceState(history.state, history.title, newUrl)
462+
}
463+
389464
/**
390465
* @param providerUri {string}
391466
* @param [options={}]

0 commit comments

Comments
 (0)