diff --git a/docs/guides/cookie-consent.md b/docs/guides/cookie-consent.md index cbf977e92b..11ae7dbdea 100644 --- a/docs/guides/cookie-consent.md +++ b/docs/guides/cookie-consent.md @@ -37,10 +37,6 @@ cookieConsentOptions: { 'apiToken', 'cookieConsent', 'preferredLocale', - 'punchout_SID', - 'punchout_BasketID', - 'punchout_ReturnURL', - 'punchout_HookURL', ], }, ``` @@ -108,15 +104,11 @@ This route can be linked to from anywhere within the application. ## PWA Required Cookies -| Name | Expiration | Provider | Description | Category | -| ------------------ | ---------- | ------------- | ----------------------------------------------------------------- | ----------- | -| apiToken | 1 hour | Intershop PWA | The API token used by the Intershop Commerce Management REST API. | First Party | -| cookieConsent | 1 year | Intershop PWA | Saves the user's cookie consent settings. | First Party | -| preferredLocale | 1 year | Intershop PWA | Saves the user's language selection. | First Party | -| punchout_SID | Session | Intershop PWA | Saves punchout session related data - Session ID. | First Party | -| punchout_BasketID | Session | Intershop PWA | Saves punchout session related data - Basket ID. | First Party | -| punchout_ReturnURL | Session | Intershop PWA | Saves punchout session related data - Return URL. | First Party | -| punchout_HookURL | Session | Intershop PWA | Saves punchout session related data - Hook URL. | First Party | +| Name | Expiration | Provider | Description | Category | +| --------------- | ---------- | ------------- | ----------------------------------------------------------------- | ----------- | +| apiToken | 1 hour | Intershop PWA | The API token used by the Intershop Commerce Management REST API. | First Party | +| cookieConsent | 1 year | Intershop PWA | Saves the user's cookie consent settings. | First Party | +| preferredLocale | 1 year | Intershop PWA | Saves the user's language selection. | First Party | ## Disabling the Integrated Cookie Consent Handling diff --git a/src/app/extensions/punchout/identity-provider/punchout-identity-provider.spec.ts b/src/app/extensions/punchout/identity-provider/punchout-identity-provider.spec.ts index 116229d694..49672e6cc8 100644 --- a/src/app/extensions/punchout/identity-provider/punchout-identity-provider.spec.ts +++ b/src/app/extensions/punchout/identity-provider/punchout-identity-provider.spec.ts @@ -11,7 +11,6 @@ import { CheckoutFacade } from 'ish-core/facades/checkout.facade'; import { TokenService } from 'ish-core/services/token/token.service'; import { selectQueryParam } from 'ish-core/store/core/router'; import { ApiTokenService } from 'ish-core/utils/api-token/api-token.service'; -import { CookiesService } from 'ish-core/utils/cookies/cookies.service'; import { makeHttpError } from 'ish-core/utils/dev/api-service-utils'; import { BasketMockData } from 'ish-core/utils/dev/basket-mock-data'; @@ -32,7 +31,6 @@ describe('Punchout Identity Provider', () => { const appFacade = mock(AppFacade); const accountFacade = mock(AccountFacade); const checkoutFacade = mock(CheckoutFacade); - const cookiesService = mock(CookiesService); let punchoutIdentityProvider: PunchoutIdentityProvider; let store$: MockStore; @@ -45,7 +43,6 @@ describe('Punchout Identity Provider', () => { { provide: ApiTokenService, useFactory: () => instance(apiTokenService) }, { provide: AppFacade, useFactory: () => instance(appFacade) }, { provide: CheckoutFacade, useFactory: () => instance(checkoutFacade) }, - { provide: CookiesService, useFactory: () => instance(cookiesService) }, { provide: PunchoutService, useFactory: () => instance(punchoutService) }, { provide: TokenService, useFactory: () => instance(mock(TokenService)) }, provideMockStore(), @@ -67,9 +64,8 @@ describe('Punchout Identity Provider', () => { resetCalls(appFacade); resetCalls(accountFacade); resetCalls(checkoutFacade); - resetCalls(cookiesService); - window.sessionStorage.clear(); + sessionStorage.clear(); }); describe('init', () => { @@ -82,7 +78,7 @@ describe('Punchout Identity Provider', () => { it('should add basket-id to session storage, when basket is available', () => { when(checkoutFacade.basket$).thenReturn(of(BasketMockData.getBasket())); punchoutIdentityProvider.init(); - expect(window.sessionStorage.getItem('basket-id')).toEqual(BasketMockData.getBasket().id); + expect(sessionStorage.getItem('basket-id')).toEqual(BasketMockData.getBasket().id); }); }); @@ -95,12 +91,12 @@ describe('Punchout Identity Provider', () => { }); it('should remove api token and basket-id on logout', done => { - expect(window.sessionStorage.getItem('basket-id')).toEqual(BasketMockData.getBasket().id); + expect(sessionStorage.getItem('basket-id')).toEqual(BasketMockData.getBasket().id); const logoutTrigger$ = punchoutIdentityProvider.triggerLogout() as Observable; logoutTrigger$.subscribe(() => { - expect(window.sessionStorage.getItem('basket-id')).toBeNull(); + expect(sessionStorage.getItem('basket-id')).toBeNull(); verify(accountFacade.logoutUser()).once(); done(); }); @@ -187,14 +183,14 @@ describe('Punchout Identity Provider', () => { ); }); - it('should set cookies, load basket basket and return to homepage', fakeAsync(() => { + it('should set session storage, load basket basket and return to homepage', fakeAsync(() => { const login$ = punchoutIdentityProvider.triggerLogin(getSnapshot(queryParams)) as Observable< boolean | UrlTree >; login$.subscribe(() => { - verify(cookiesService.put('punchout_SID', 'sid')).once(); - verify(cookiesService.put('punchout_ReturnURL', 'home')).once(); - verify(cookiesService.put('punchout_BasketID', 'basket-id')).once(); + expect(sessionStorage.getItem('punchout_SID')).toEqual('sid'); + expect(sessionStorage.getItem('punchout_ReturnURL')).toEqual('home'); + expect(sessionStorage.getItem('punchout_BasketID')).toEqual('basket-id'); }); tick(500); @@ -208,12 +204,12 @@ describe('Punchout Identity Provider', () => { queryParams = { HOOK_URL: 'url', USERNAME: 'username', PASSWORD: 'password' }; }); - it('should set cookie and create basket on login', done => { + it('should set session storage and create basket on login', done => { const login$ = punchoutIdentityProvider.triggerLogin(getSnapshot(queryParams)) as Observable< boolean | UrlTree >; login$.subscribe(() => { - verify(cookiesService.put('punchout_HookURL', 'url')).once(); + expect(sessionStorage.getItem('punchout_HookURL')).toEqual('url'); verify(checkoutFacade.createBasket()).once(); expect(routerSpy).toHaveBeenCalledWith('/home'); done(); @@ -221,7 +217,7 @@ describe('Punchout Identity Provider', () => { }); it('should reload basket when basket is saved in session storage', done => { - window.sessionStorage.setItem('basket-id', 'basket-id'); + sessionStorage.setItem('basket-id', 'basket-id'); const login$ = punchoutIdentityProvider.triggerLogin(getSnapshot(queryParams)) as Observable< boolean | UrlTree >; diff --git a/src/app/extensions/punchout/identity-provider/punchout-identity-provider.ts b/src/app/extensions/punchout/identity-provider/punchout-identity-provider.ts index 65d13c2244..39007e09cd 100644 --- a/src/app/extensions/punchout/identity-provider/punchout-identity-provider.ts +++ b/src/app/extensions/punchout/identity-provider/punchout-identity-provider.ts @@ -12,7 +12,6 @@ import { IdentityProvider, TriggerReturnType } from 'ish-core/identity-provider/ import { TokenService } from 'ish-core/services/token/token.service'; import { selectQueryParam } from 'ish-core/store/core/router'; import { ApiTokenService } from 'ish-core/utils/api-token/api-token.service'; -import { CookiesService } from 'ish-core/utils/cookies/cookies.service'; import { whenTruthy } from 'ish-core/utils/operators'; import { PunchoutService } from '../services/punchout/punchout.service'; @@ -26,7 +25,6 @@ export class PunchoutIdentityProvider implements IdentityProvider { private appFacade: AppFacade, private accountFacade: AccountFacade, private punchoutService: PunchoutService, - private cookiesService: CookiesService, private checkoutFacade: CheckoutFacade, private tokenService: TokenService ) {} @@ -50,7 +48,7 @@ export class PunchoutIdentityProvider implements IdentityProvider { this.apiTokenService.restore$(['user', 'order']).subscribe(noop); this.checkoutFacade.basket$.pipe(whenTruthy(), first()).subscribe(basketView => { - window.sessionStorage.setItem('basket-id', basketView.id); + sessionStorage.setItem('basket-id', basketView.id); }); } @@ -123,7 +121,7 @@ export class PunchoutIdentityProvider implements IdentityProvider { } triggerLogout(): TriggerReturnType { - window.sessionStorage.removeItem('basket-id'); + sessionStorage.removeItem('basket-id'); this.accountFacade.logoutUser(); // user will be logged out and related refresh token is revoked on server return this.accountFacade.isLoggedIn$.pipe( // wait until the user is logged out before you go to homepage to prevent unnecessary REST calls @@ -147,11 +145,11 @@ export class PunchoutIdentityProvider implements IdentityProvider { private handleCxmlPunchoutLogin(route: ActivatedRouteSnapshot): Observable { // fetch sid session information (basketId, returnURL, operation, ...) return this.punchoutService.getCxmlPunchoutSession(route.queryParamMap.get('sid')).pipe( - // persist cXML session information (sid, returnURL, basketId) in cookies for later basket transfer + // persist cXML session information (sid, returnURL, basketId) in session storage for later basket transfer tap(data => { - this.cookiesService.put('punchout_SID', route.queryParamMap.get('sid')); - this.cookiesService.put('punchout_ReturnURL', data.returnURL); - this.cookiesService.put('punchout_BasketID', data.basketId); + sessionStorage.setItem('punchout_SID', route.queryParamMap.get('sid')); + sessionStorage.setItem('punchout_ReturnURL', data.returnURL); + sessionStorage.setItem('punchout_BasketID', data.basketId); }), // use the basketId basket for the current PWA session (instead of default current basket) // TODO: if load basket error (currently no error page) -> logout and do not use default 'current' basket @@ -163,10 +161,10 @@ export class PunchoutIdentityProvider implements IdentityProvider { } private handleOciPunchoutLogin(route: ActivatedRouteSnapshot) { - // save HOOK_URL to cookie for later basket transfer - this.cookiesService.put('punchout_HookURL', route.queryParamMap.get('HOOK_URL')); + // save HOOK_URL to session storage for later basket transfer + sessionStorage.setItem('punchout_HookURL', route.queryParamMap.get('HOOK_URL')); - const basketId = window.sessionStorage.getItem('basket-id'); + const basketId = sessionStorage.getItem('basket-id'); if (!basketId) { // create a new basket for every punchout session to avoid basket conflicts for concurrent punchout sessions this.checkoutFacade.createBasket(); diff --git a/src/app/extensions/punchout/services/punchout/punchout.service.spec.ts b/src/app/extensions/punchout/services/punchout/punchout.service.spec.ts index 0e74b1d9e4..65bfa7266f 100644 --- a/src/app/extensions/punchout/services/punchout/punchout.service.spec.ts +++ b/src/app/extensions/punchout/services/punchout/punchout.service.spec.ts @@ -6,7 +6,6 @@ import { anything, capture, instance, mock, verify, when } from 'ts-mockito'; import { Customer } from 'ish-core/models/customer/customer.model'; import { ApiService } from 'ish-core/services/api/api.service'; import { getLoggedInCustomer } from 'ish-core/store/customer/user'; -import { CookiesService } from 'ish-core/utils/cookies/cookies.service'; import { PunchoutUser } from '../../models/punchout-user/punchout-user.model'; @@ -14,13 +13,11 @@ import { PunchoutService } from './punchout.service'; describe('Punchout Service', () => { let apiServiceMock: ApiService; - let cookiesServiceMock: CookiesService; let punchoutService: PunchoutService; const punchoutUser = { login: 'ociuser', punchoutType: 'oci' } as PunchoutUser; beforeEach(() => { apiServiceMock = mock(ApiService); - cookiesServiceMock = mock(CookiesService); when(apiServiceMock.options(anything(), anything())).thenReturn(of({})); when(apiServiceMock.get(anything(), anything())).thenReturn(of({})); @@ -35,7 +32,6 @@ describe('Punchout Service', () => { TestBed.configureTestingModule({ providers: [ { provide: ApiService, useFactory: () => instance(apiServiceMock) }, - { provide: CookiesService, useFactory: () => instance(cookiesServiceMock) }, provideMockStore({ selectors: [ { selector: getLoggedInCustomer, value: { customerNo: '4711', isBusinessCustomer: true } as Customer }, diff --git a/src/app/extensions/punchout/services/punchout/punchout.service.ts b/src/app/extensions/punchout/services/punchout/punchout.service.ts index 13b8254e20..2504e69ff4 100644 --- a/src/app/extensions/punchout/services/punchout/punchout.service.ts +++ b/src/app/extensions/punchout/services/punchout/punchout.service.ts @@ -5,13 +5,13 @@ import { Store, select } from '@ngrx/store'; import { EMPTY, Observable, iif, throwError } from 'rxjs'; import { concatMap, filter, map, switchMap, take } from 'rxjs/operators'; +import { MessageFacade } from 'ish-core/facades/message.facade'; import { Attribute } from 'ish-core/models/attribute/attribute.model'; import { Link } from 'ish-core/models/link/link.model'; import { ApiService, unpackEnvelope } from 'ish-core/services/api/api.service'; import { getUserPermissions } from 'ish-core/store/customer/authorization'; import { getCurrentBasketId } from 'ish-core/store/customer/basket'; import { getLoggedInCustomer } from 'ish-core/store/customer/user'; -import { CookiesService } from 'ish-core/utils/cookies/cookies.service'; import { DomService } from 'ish-core/utils/dom/dom.service'; import { whenTruthy } from 'ish-core/utils/operators'; @@ -28,10 +28,10 @@ import { PunchoutType, PunchoutUser } from '../../models/punchout-user/punchout- @Injectable({ providedIn: 'root' }) export class PunchoutService { constructor( - private apiService: ApiService, - private cookiesService: CookiesService, private store: Store, + private apiService: ApiService, private domService: DomService, + private messageFacade: MessageFacade, @Inject(DOCUMENT) private document: Document ) {} @@ -208,7 +208,7 @@ export class PunchoutService { */ private submitPunchoutForm(form: HTMLFormElement, submit = true) { if (!form) { - return throwError(() => new Error('submitPunchoutForm() of the punchout service called without a form')); + throw new Error('submitPunchoutForm() of the punchout service called without a form'); } // replace the document content with the form and submit the form @@ -242,15 +242,17 @@ export class PunchoutService { * transferCxmlPunchoutBasket */ private transferCxmlPunchoutBasket() { - const punchoutSID = this.cookiesService.get('punchout_SID'); + const punchoutSID = sessionStorage.getItem('punchout_SID'); if (!punchoutSID) { - return throwError(() => new Error('no punchout_SID available in cookies for cXML punchout basket transfer')); + const errorMessage = 'no punchout_SID information available for cXML punchout basket transfer'; + this.messageFacade.error({ message: errorMessage }); + return throwError(() => new Error(errorMessage)); } - const returnURL = this.cookiesService.get('punchout_ReturnURL'); + const returnURL = sessionStorage.getItem('punchout_ReturnURL'); if (!returnURL) { - return throwError( - () => new Error('no punchout_ReturnURL available in cookies for cXML punchout basket transfer') - ); + const errorMessage = 'no punchout_ReturnURL information available for cXML punchout basket transfer'; + this.messageFacade.error({ message: errorMessage }); + return throwError(() => new Error(errorMessage)); } return this.currentCustomer$.pipe( @@ -270,8 +272,6 @@ export class PunchoutService { .pipe(map(data => this.submitPunchoutForm(this.createCxmlPunchoutForm(data, returnURL)))) ) ); - - // TODO: cleanup punchout cookies? } /** @@ -453,13 +453,13 @@ export class PunchoutService { */ submitOciPunchoutData(data: Attribute[], submit = true) { if (!data?.length) { - return throwError(() => new Error('submitOciPunchoutData() of the punchout service called without data')); + throw new Error('submitOciPunchoutData() of the punchout service called without data'); } - const hookUrl = this.cookiesService.get('punchout_HookURL'); + const hookUrl = sessionStorage.getItem('punchout_HookURL'); if (!hookUrl) { - return throwError( - () => new Error('no punchout_HookURL available in cookies for OCI Punchout submitPunchoutData()') - ); + const errorMessage = 'no punchout_HookURL information available for OCI Punchout submitPunchoutData()'; + this.messageFacade.error({ message: errorMessage }); + throw new Error(errorMessage); } this.submitPunchoutForm(this.createOciForm(data, hookUrl), submit); } diff --git a/src/app/extensions/punchout/store/punchout-functions/punchout-functions.effects.ts b/src/app/extensions/punchout/store/punchout-functions/punchout-functions.effects.ts index dafec746f0..0925d1838b 100644 --- a/src/app/extensions/punchout/store/punchout-functions/punchout-functions.effects.ts +++ b/src/app/extensions/punchout/store/punchout-functions/punchout-functions.effects.ts @@ -33,7 +33,7 @@ export class PunchoutFunctionsEffects { () => this.actions$.pipe( ofType(transferPunchoutBasketSuccess), - map(() => window.sessionStorage.removeItem('basket-id')) + map(() => sessionStorage.removeItem('basket-id')) ), { dispatch: false } ); diff --git a/src/environments/environment.model.ts b/src/environments/environment.model.ts index 9bf3f0a723..cbbf66928c 100644 --- a/src/environments/environment.model.ts +++ b/src/environments/environment.model.ts @@ -196,15 +196,7 @@ export const ENVIRONMENT_DEFAULTS: Omit = { description: 'cookie.consent.option.tracking.description', }, }, - allowedCookies: [ - 'apiToken', - 'cookieConsent', - 'preferredLocale', - 'punchout_SID', - 'punchout_BasketID', - 'punchout_ReturnURL', - 'punchout_HookURL', - ], + allowedCookies: ['apiToken', 'cookieConsent', 'preferredLocale'], }, cookieConsentVersion: 1,