I am having problem with CheckoutAuthGuard
on spartacus storefront. When browser refresh button is pressed on any page in checkout process, guard redirects me to a login due to missing user and cart info.
I have used CustomCheckoutAuthGuard
like this:
import { Injectable } from '@angular/core';
import {CanActivate, Router} from '@angular/router';
import {
ActiveCartService,
AuthRedirectService,
AuthService,
RoutingService,
User,
UserToken,
} from '@spartacus/core';
import { combineLatest, Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import {CheckoutConfigService} from '@spartacus/storefront';
@Injectable()
export class CustomCheckoutAuthGuard implements CanActivate {
constructor(
protected routingService: RoutingService,
protected authService: AuthService,
protected authRedirectService: AuthRedirectService,
protected checkoutConfigService: CheckoutConfigService,
protected activeCartService: ActiveCartService,
private router: Router
) {}
canActivate(): Observable<boolean> {
return combineLatest([
this.authService.getUserToken(),
this.activeCartService.getAssignedUser(),
]).pipe(
map(([token, user]: [UserToken, User]) => {
if (!token.access_token) {
if (this.activeCartService.isGuestCart()) {
return Boolean(user);
}
if (this.checkoutConfigService.isGuestCheckout()) {
this.router.navigate(['checkout-login']);
} else {
this.routingService.go({ cxRoute: 'login' });
}
this.authRedirectService.reportAuthGuard();
}
return !!token.access_token;
})
);
}
}
It is very similar to default CheckoutAuthGuard
from spartacus. I also switched this guard for all relevant checkout components:
CheckoutOrchestrator: {
component: CheckoutOrchestratorComponent,
guards: [CustomCheckoutAuthGuard, CartNotEmptyGuard, CheckoutGuard],
},
CheckoutProgressMobileTop: {
component: CheckoutProgressMobileTopComponent,
guards: [CustomCheckoutAuthGuard, CartNotEmptyGuard],
},
CheckoutProgressMobileBottomComponent: {
component: CheckoutProgressMobileBottomComponent,
guards: [CustomCheckoutAuthGuard, CartNotEmptyGuard]
},
CheckoutProgress: {
component: CheckoutProgressComponent,
guards: [CustomCheckoutAuthGuard, CartNotEmptyGuard]
},
CheckoutDeliveryMode: {
component: DeliveryModeComponent,
guards: [CustomCheckoutAuthGuard, CartNotEmptyGuard, ShippingAddressSetGuard]
},
CheckoutPlaceOrder: {
component: PlaceOrderComponent,
guards: [CustomCheckoutAuthGuard, CartNotEmptyGuard]
},
CheckoutReviewOrder: {
component: ReviewSubmitComponent,
guards: [CustomCheckoutAuthGuard, CartNotEmptyGuard]
},
CheckoutShippingAddress: {
component: ShippingAddressComponent,
guards: [CustomCheckoutAuthGuard, CartNotEmptyGuard, CheckoutDetailsLoadedGuard]
}
But on refresh, user from ActiveCartService
is undefined
and also this.activeCartService.isGuestCart()
returns undefined
.
This is all for guest checkout and unfortunately I can not tell what is behavior for logged in user. It is because we use CDC, and currently with CDC there is a bug where any refresh redirects to home page, so this guard is completely bypassed. More on this issue here.
Can someone give me advice what is best to try out here? Maybe some cart state needs to be synced with local storage, or combineLatest
is wrong rxjs
function here? Or something else? Thanks in advance!
This is a known issue, it is fixed in Spartacus version 3.0 you can see the issue we opened here. At the moment there is no plan to back port this change to a version prior to 3.0.
The issue was caused by the fact that the guard checks were run before the cart was loaded loaded.
If you need this solution before 3.0, I recommend that you override the out of the box CheckoutAuthGuard
with a custom version of it. You should add the this.activeCartService.isStable()
value to the combineLatest
stream and filter
on "cart stable". Here you can see what I mean.
Here is how you should provide your custom guard so it replaces the default guard:
(in app.module.ts or other module imported in AppModule)
providers: [
...,
{
provide: CheckoutAuthGuard,
useClass: CustomCheckoutAuthGuard, //or whatever your guard class is named
}
]