Skip to content

Commit

Permalink
refactor: auth with middleware (#776)
Browse files Browse the repository at this point in the history
* refactor: auth with middleware

* refactor: test and comment auth-guard

* fix: lint

* fix: lint

* Update auth-guard.ts

* feat: redirect to requested page after auth

* test: add e2e test for login redirect

* chore: remove unused code

---------

Co-authored-by: Fabian Gerke <[email protected]>
  • Loading branch information
maxiroellplenty and FabianGerke authored Nov 6, 2024
1 parent 7786f44 commit 7004420
Show file tree
Hide file tree
Showing 13 changed files with 133 additions and 27 deletions.
82 changes: 82 additions & 0 deletions apps/web/__tests__/test/feature/auth-guard.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { MyAccountPageObject } from '../../support/pageObjects/MyAccountPageObject';
import { paths } from '../../../utils/paths';

const guardedRoutes = [
paths.accountPersonalData,
paths.accountBillingDetails,
paths.accountShippingDetails,
paths.accountMyOrders,
paths.accountMyWishlist,
paths.accountReturns,
paths.accountNewReturn + '/1/accessKey'
];

describe('Auth Guard', () => {
beforeEach(() => {
cy.clearCookie('pwa-session-id');
});

it('should redirect from accountPersonalData to login page if user is not authorized', () => {
cy.visit(paths.accountPersonalData);
cy.url().should('include', paths.authLogin);
});

it('should redirect from accountBillingDetails to login page if user is not authorized', () => {
cy.visit(paths.accountBillingDetails);
cy.url().should('include', paths.authLogin);
});

it('should redirect from accountShippingDetails to login page if user is not authorized', () => {
cy.visit(paths.accountShippingDetails);
cy.url().should('include', paths.authLogin);
});

it('should redirect from accountMyOrders to login page if user is not authorized', () => {
cy.visit(paths.accountMyOrders);
cy.url().should('include', paths.authLogin);
});

it('should redirect from accountMyWishlist to login page if user is not authorized', () => {
cy.visit(paths.accountMyWishlist);
cy.url().should('include', paths.authLogin);
});

it('should redirect from accountReturns to login page if user is not authorized', () => {
cy.visit(paths.accountReturns);
cy.url().should('include', paths.authLogin);
});

it('should redirect from accountNewReturn to login page if user is not authorized', () => {
cy.visit(paths.accountNewReturn + '/1/accessKey');
cy.url().should('include', paths.authLogin);
});

it('should allow access to authorized users', () => {
const myAccount = new MyAccountPageObject();

cy.intercept('/plentysystems/doLogin').as('doLogin');
cy.visitAndHydrate(paths.authLogin);
myAccount.successLogin();

cy.wait('@doLogin');

guardedRoutes.forEach(route => {
cy.visitAndHydrate(route);
cy.url().should('include', route);
});
});

it('should redirect back to protected page after successful login', () => {
const myAccount = new MyAccountPageObject();

cy.visit(paths.accountPersonalData);
cy.url().should('include', `${paths.authLogin}?redirect=${paths.accountPersonalData}`);

cy.intercept('/plentysystems/doLogin').as('doLogin');
myAccount.successLogin();

cy.wait('@doLogin');
cy.url().should('include', paths.accountPersonalData);
});

});
22 changes: 0 additions & 22 deletions apps/web/app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,39 +16,17 @@ const { setVsfLocale } = useLocalization();
const route = useRoute();
const { locale } = useI18n();
const { setStaticPageMeta } = useCanonical();
const { isAuthorized } = useCustomer();
const localePath = useLocalePath();
await setInitialDataSSR();
setVsfLocale(locale.value);
if (route?.meta.pageType === 'static') setStaticPageMeta();
usePageTitle();
const authOnlyRoutes = new Set([
localePath(paths.accountPersonalData),
localePath(paths.accountBillingDetails),
localePath(paths.accountShippingDetails),
localePath(paths.accountMyOrders),
localePath(paths.accountMyWishlist),
localePath(paths.accountReturns),
localePath(paths.accountNewReturn),
]);
const watchAuthRoutes = (authenticated: boolean) => {
if (authOnlyRoutes.has(localePath(route.path)) && !authenticated) navigateTo(localePath(paths.home));
};
onNuxtReady(async () => {
bodyClass.value = 'hydrated'; // Need this class for cypress testing
watchAuthRoutes(isAuthorized.value);
});
watch(
() => isAuthorized.value,
(authenticated: boolean) => watchAuthRoutes(authenticated),
);
watch(
() => locale.value,
async (locale: string) => {
Expand Down
24 changes: 24 additions & 0 deletions apps/web/middleware/auth-guard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* This middleware is used to check if the user is authorized.
*
* Use this auth guard to protect routes that require the user to be logged in.
*
* If the user is not authorized, the user will be redirected to the login page.
*/

export default defineNuxtRouteMiddleware(async (to) => {
const { isAuthorized, getSession } = useCustomer();
const localePath = useLocalePath();

await getSession();

if (!isAuthorized.value) {
const targetUrl = to.fullPath;
return navigateTo({
path: localePath(paths.authLogin),
query: {
redirect: targetUrl,
},
});
}
});
18 changes: 13 additions & 5 deletions apps/web/pages/login.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<NuxtLayout name="auth" :heading="''">
<LoginComponent v-if="isLogin" @change-view="isLogin = false" @logged-in="returnToPreviousPage" />
<Register v-else @change-view="isLogin = true" @registered="returnToPreviousPage" />
<LoginComponent v-if="isLogin" @change-view="isLogin = false" @logged-in="navigateAfterAuth" />
<Register v-else @change-view="isLogin = true" @registered="navigateAfterAuth" />
</NuxtLayout>
</template>

Expand All @@ -11,9 +11,17 @@ definePageMeta({
});
const router = useRouter();
const returnToPreviousPage = () => {
const localePath = useLocalePath();
const isLogin = ref(true);
const navigateAfterAuth = () => {
const redirectUrl = router.currentRoute.value.query.redirect as string;
if (redirectUrl) {
router.push(localePath(redirectUrl));
return;
}
router.go(-1);
};
const isLogin = ref(true);
</script>
1 change: 1 addition & 0 deletions apps/web/pages/my-account/billing-details.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ import { AddressType } from '@plentymarkets/shop-api';
definePageMeta({
layout: 'account',
pageType: 'static',
middleware: ['auth-guard'],
});
</script>
1 change: 1 addition & 0 deletions apps/web/pages/my-account/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
definePageMeta({
layout: 'account',
pageType: 'static',
middleware: ['auth-guard'],
});
</script>
1 change: 1 addition & 0 deletions apps/web/pages/my-account/my-orders.vue
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ const isDesktop = computed(() => viewport.isGreaterOrEquals('lg'));
definePageMeta({
layout: 'account',
pageType: 'static',
middleware: ['auth-guard'],
});
onMounted(() => setMaxVisiblePages(isDesktop.value));
Expand Down
6 changes: 6 additions & 0 deletions apps/web/pages/my-account/my-orders/[id].vue
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,12 @@ const { locale } = useI18n();
const { isOpen } = useDisclosure({ initialValue: true });
const { fetchOrder, data } = useCustomerOrder(route.params.id as string);
definePageMeta({
layout: 'account',
pageType: 'static',
middleware: ['auth-guard'],
});
onMounted(async () => {
// without nextTick data on first click does not load data
await nextTick();
Expand Down
1 change: 1 addition & 0 deletions apps/web/pages/my-account/new-return/[id]/[accessKey].vue
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ const { t, locale } = useI18n();
definePageMeta({
layout: 'account',
pageType: 'static',
middleware: ['auth-guard'],
});
const { currentReturnOrder, hasMinimumQuantitySelected, hasQuantityAndNoReasonsSelected, selectAll, cleanReturnData } =
useReturnOrder();
Expand Down
1 change: 1 addition & 0 deletions apps/web/pages/my-account/personal-data.vue
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ import { unrefElement } from '@vueuse/core';
definePageMeta({
layout: 'account',
pageType: 'static',
middleware: ['auth-guard'],
});
const { isOpen, open, close } = useDisclosure();
const lastActiveElement = ref();
Expand Down
1 change: 1 addition & 0 deletions apps/web/pages/my-account/returns.vue
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ import { useDisclosure, SfLoaderCircular } from '@storefront-ui/vue';
definePageMeta({
layout: 'account',
pageType: 'static',
middleware: ['auth-guard'],
});
const { data, fetchCustomerReturns, loading } = useCustomerReturns();
Expand Down
1 change: 1 addition & 0 deletions apps/web/pages/my-account/shipping-details.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ import { AddressType } from '@plentymarkets/shop-api';
definePageMeta({
layout: 'account',
pageType: 'static',
middleware: ['auth-guard'],
});
</script>
1 change: 1 addition & 0 deletions apps/web/pages/my-account/wishlist.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@
definePageMeta({
layout: 'account',
pageType: 'static',
middleware: ['auth-guard'],
});
</script>

0 comments on commit 7004420

Please sign in to comment.