Skip to content

Commit

Permalink
Removing closeSession() as uses third-party cookies. Create signOut p…
Browse files Browse the repository at this point in the history
…age and redirect there so we can redirect user back to their previous page before signOut.
  • Loading branch information
dr-bizz committed Mar 6, 2024
1 parent 5c645dc commit b026ba5
Show file tree
Hide file tree
Showing 7 changed files with 259 additions and 74 deletions.
6 changes: 6 additions & 0 deletions src/app/main/main.component.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import checkoutComponent from '../checkout/checkout.component'
import thankYouComponent from '../thankYou/thankYou.component'
import productConfigComponent from '../productConfig/productConfig.component'
import signInComponent from '../signIn/signIn.component'
import signOutComponent from '../signOut/signOut.component'
import searchResultsComponent from '../searchResults/searchResults.component'
import designationEditorComponent from '../designationEditor/designationEditor.component'
import yourGivingComponent from '../profile/yourGiving/yourGiving.component'
Expand Down Expand Up @@ -46,6 +47,10 @@ const routingConfig = /* @ngInject */ function ($stateProvider, $locationProvide
url: '/sign-in.html',
template: '<sign-in></sign-in>'
})
.state('sign-out', {
url: '/sign-out.html',
template: '<sign-out></sign-out>'
})
.state('checkout', {
url: '/checkout.html',
template: '<checkout></checkout>'
Expand Down Expand Up @@ -96,6 +101,7 @@ export default angular
yourGivingComponent.name,
productConfigComponent.name,
signInComponent.name,
signOutComponent.name,
searchResultsComponent.name,
profileComponent.name,
designationEditorComponent.name,
Expand Down
52 changes: 52 additions & 0 deletions src/app/signOut/signOut.component.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import angular from 'angular'
import commonModule from 'common/common.module'
import sessionService, { Roles } from 'common/services/session/session.service'
import template from './signOut.tpl.html'

const componentName = 'signOut'

class SignOutController {
/* @ngInject */
constructor ($window, sessionService) {
this.$window = $window
this.sessionService = sessionService
}

$onInit () {
if (this.sessionService.getRole() !== Roles.public) {
this.redirectToHomepage()
} else {
this.redirectToLocationPriorToSignOut()
}
}

redirectToHomepage () {
this.$window.location.href = '/'
}

redirectToLocationPriorToSignOut () {
this.showRedirectingLoadingIcon = true
const locationToReturnUser = this.sessionService.hasLocationOnLogin()
if (locationToReturnUser) {
this.sessionService.removeLocationOnLogin()
this.$window.location.href = locationToReturnUser
} else {
this.redirectToHomepage()
}
}

closeRedirectingLoading () {
this.showRedirectingLoadingIcon = false
this.redirectToHomepage()
}
}

export default angular
.module(componentName, [
commonModule.name,
sessionService.name
])
.component(componentName, {
controller: SignOutController,
templateUrl: template
})
100 changes: 100 additions & 0 deletions src/app/signOut/signOut.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import angular from 'angular'
import 'angular-mocks'
import module from './signOut.component'
import { Observable } from 'rxjs/Observable'
import 'rxjs/add/observable/of'
import 'rxjs/add/observable/throw'

describe('signOut', function () {
beforeEach(angular.mock.module(module.name))
let $ctrl

beforeEach(inject(function (_$componentController_) {
$ctrl = _$componentController_(module.name,
{ $window: { location: { href: '/sign-out.html'} } }
)
}))

it('to be defined', function () {
expect($ctrl).toBeDefined()
})

it('redirectToHomepage()', function () {
$ctrl.redirectToHomepage()
expect($ctrl.$window.location.href).toEqual('/')
})

describe('redirectToLocationPriorToSignOut()', () => {
beforeEach(() => {
jest.spyOn($ctrl, 'redirectToHomepage')
jest.spyOn($ctrl.sessionService, 'removeLocationOnLogin')
})

it('redirects to prior page if hasLocationOnLogin() returns value', () => {
jest.spyOn($ctrl.sessionService, 'hasLocationOnLogin').mockReturnValue('https://give-stage2.cru.org/search-results.html')
$ctrl.showRedirectingLoadingIcon = false
$ctrl.redirectToLocationPriorToSignOut()
expect($ctrl.showRedirectingLoadingIcon).toEqual(true)
expect($ctrl.sessionService.hasLocationOnLogin).toHaveBeenCalled()
expect($ctrl.sessionService.removeLocationOnLogin).toHaveBeenCalled()
expect($ctrl.$window.location.href).toEqual('https://give-stage2.cru.org/search-results.html')
})

it('redirects to home page if hasLocationOnLogin() returns no value', () => {
jest.spyOn($ctrl.sessionService, 'hasLocationOnLogin').mockReturnValue(undefined)
$ctrl.showRedirectingLoadingIcon = false
$ctrl.redirectToLocationPriorToSignOut()
expect($ctrl.showRedirectingLoadingIcon).toEqual(true)
expect($ctrl.sessionService.hasLocationOnLogin).toHaveBeenCalled()
expect($ctrl.sessionService.removeLocationOnLogin).not.toHaveBeenCalled()
expect($ctrl.$window.location.href).toEqual('/')
})
})



describe('as \'IDENTIFIED\'', () => {
it('should redirect to the home page', () => {
jest.spyOn($ctrl.sessionService, 'getRole').mockReturnValue('IDENTIFIED')
jest.spyOn($ctrl, 'redirectToHomepage')
jest.spyOn($ctrl, 'redirectToLocationPriorToSignOut')
$ctrl.$onInit()

expect($ctrl.redirectToHomepage).toHaveBeenCalled()
expect($ctrl.redirectToLocationPriorToSignOut).not.toHaveBeenCalled()
})
})

describe('as \'REGISTERED\'', () => {
it('should redirect to the home page', () => {
jest.spyOn($ctrl.sessionService, 'getRole').mockReturnValue('REGISTERED')
jest.spyOn($ctrl, 'redirectToHomepage')
jest.spyOn($ctrl, 'redirectToLocationPriorToSignOut')
$ctrl.$onInit()

expect($ctrl.redirectToHomepage).toHaveBeenCalled()
expect($ctrl.redirectToLocationPriorToSignOut).not.toHaveBeenCalled()
})
})


describe('as \'GUEST\'', () => {
it('should run redirectToLocationPriorToSignOut()', () => {
jest.spyOn($ctrl.sessionService, 'getRole').mockReturnValue('PUBLIC')
jest.spyOn($ctrl, 'redirectToLocationPriorToSignOut')
$ctrl.$onInit()

expect($ctrl.redirectToLocationPriorToSignOut).toHaveBeenCalled()
})
})

describe('closeRedirectingLoading()', () => {
it('should run redirectToHomepage()', () => {
jest.spyOn($ctrl, 'redirectToHomepage')
$ctrl.showRedirectingLoadingIcon = true
$ctrl.closeRedirectingLoading()
expect($ctrl.showRedirectingLoadingIcon).toEqual(false)
expect($ctrl.redirectToHomepage).toHaveBeenCalled()
})
})
})
13 changes: 13 additions & 0 deletions src/app/signOut/signOut.tpl.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<div class="screenContent">
<div class="redirect-user-after-login" ng-if="$ctrl.showRedirectingLoadingIcon">
<div>
<loading>
<p><translate>Successfully logged out.</translate></p>
<p><translate>Redirecting to your previous page...</translate></p>
</loading>
<button id="closeButtonTop" type="button" ng-click="$ctrl.closeRedirectingLoading()" class="btn btn-block btn-secondary" aria-label="{{'Close' | translate}}">
Close <span aria-hidden="true">×</span>
</button>
</div>
</div>
</div>
40 changes: 22 additions & 18 deletions src/common/services/session/session.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -297,33 +297,38 @@ const session = /* @ngInject */ function ($cookies, $rootScope, $http, $timeout,
}

async function internalSignOut (redirectHome = true) {
const oktaSignOut = async () => {
console.log('oktaSignOut', redirectHome)
// Add session data so on return to page we can show an explaination to the user about what happened.
if (!redirectHome) {
$window.sessionStorage.setItem(forcedUserToLogout, true)
// Save location we need to redirect the user back to
$window.sessionStorage.setItem(locationOnLogin, $window.location.href)
}
return await authClient.signOut({
postLogoutRedirectUri: redirectHome ? null : `${envService.read('oktaReferrer')}/sign-out.html`
})
}

try {
await $http({
method: 'DELETE',
url: oktaApiUrl('logout'),
withCredentials: true
})
await clearCheckoutSavedData()
// Use revokeAccessToken, revokeRefreshToken to ensure authClient is cleared before logging out entirely.
await authClient.revokeAccessToken()
await authClient.revokeRefreshToken()
await authClient.closeSession()
// Add session data so on return to page we can show an explaination to the user about what happened.
if (!redirectHome) {
$window.sessionStorage.setItem('forcedUserToLogout', true)
}
return authClient.signOut({
postLogoutRedirectUri: redirectHome ? null : $window.location.href
})

return await oktaSignOut()
} catch {
// closeSession errors out due to CORS. to fix this temporarily, I've added the logout in a catch just in case.
if (!redirectHome) {
$window.sessionStorage.setItem('forcedUserToLogout', true)
try {
return await oktaSignOut()
} catch {
console.log('woof')
$window.location.href = `${envService.read('oktaUrl')}/login/signout?fromURI=${envService.read('oktaReferrer')}`
}
return authClient.signOut({
postLogoutRedirectUri: redirectHome ? null : $window.location.href
}).catch(() => {
$window.location = `https://signon.okta.com/login/signout?fromURI=${envService.read('oktaReferrer')}`
})
}
}

Expand Down Expand Up @@ -351,7 +356,7 @@ const session = /* @ngInject */ function ($cookies, $rootScope, $http, $timeout,
function removeForcedUserToLogoutSessionData () {
// Allow for 2 seconds, so component can show error to user.
setTimeout(() => {
$window.sessionStorage.removeItem('forcedUserToLogout')
$window.sessionStorage.removeItem(forcedUserToLogout)
}, 2000)
}

Expand All @@ -368,7 +373,6 @@ const session = /* @ngInject */ function ($cookies, $rootScope, $http, $timeout,
.map((response) => response.data)
authClient.revokeAccessToken()
authClient.revokeRefreshToken()
authClient.closeSession()
return skipEvent
? observable
: observable.finally(() => {
Expand Down
Loading

0 comments on commit b026ba5

Please sign in to comment.