@@ -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