Search code examples
angularauthenticationcheckoutspartacus-storefrontangular-router-guards

How to get user and cart data in authentication guard on refresh?


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!


Solution

  • 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
        }
    ]