ekino / v6y

Vitality is a tool crafted to maintain and optimize the health and performance of codebase and applications.
MIT License
4 stars 1 forks source link

[FEATURE]: Authentication (Frontend & Frontend BO) #7

Open helabenkhalfallah opened 1 month ago

helabenkhalfallah commented 1 month ago

✨ Description

Authentication interfaces for both the Vitality frontend application and the frontend of the Backoffice (BO), with a focus on protecting source code privacy.

🚀 Motivation

By controlling access to projects, the authentication system ensures that only authorized users can configure and view sensitive source code and reports, preserving their confidentiality and integrity.

📝 Proposed Solution

The Frontend and Frontend BO are the only components directly exposed to users, the authentication layer should be implemented primarily on the BFF side.

How the authentication flow would work:

Verify that all Frontend & Frontend BO routes are protected.

Along with Apollo Server Authentication, we can use passportjs:

const server = new ApolloServer({
  typeDefs,
  resolvers,
  context: async ({ req }) => {
    // Authenticate the user using Passport.js
    let user = null;
    try {
      user = await new Promise((resolve, reject) => {
        passport.authenticate('jwt', { session: false }, (err, user) => {
          if (err) reject(err);
          resolve(user);
        })(req);
      });
    } catch (error) {
      console.error('Authentication error:', error); 
    }

    return { user }; 
  },
});

🔗 Relevant Links (if any)

helabenkhalfallah commented 1 month ago

Frontend:

Frontend BO:

Final result: All Frontend and Frontend BO screens should been protected with authentication access.

Inspirational examples: https://github.com/ekino/v6y/blob/main/src/v6y-front-bo/src/app/page.tsx#L11 https://github.com/ekino/v6y/blob/main/src/v6y-front-bo/src/app/v6y-faqs/layout.tsx#L6 https://gitlab.ekino.com/renault/mfs/myze-battery/-/blob/main/apps/front/components/ProtectedRoute.tsx#L7

"use client";

import { useRouter } from "next/navigation";
import { useEffect } from "react";
import { useLogin } from "../hooks/useAuth";

export function ProtectedRoute({ children }: React.PropsWithChildren<{}>) {
  const { isLoggedIn, isLoginLoading } = useLogin();
  const router = useRouter();

  useEffect(() => {
    if (!isLoggedIn && !isLoginLoading) {
      router.replace("/");
    }
  }, [isLoggedIn, isLoginLoading, router]);

  return isLoggedIn ? children : null;
}
import { PropsWithChildren } from "react";
import { ProtectedRoute } from "./ProtectedRoute";
import RouteErrorHandler from "./errors/RouteErrorHandler";

type Props = {
  error?: Error | null;
};

export default function RouteWrapper({
  error,
  children,
}: PropsWithChildren<Props>) {
  return (
    <ProtectedRoute>
      <RouteErrorHandler error={error}>{children}</RouteErrorHandler>
    </ProtectedRoute>
  );
}
// https://gitlab.ekino.com/renault/mfs/myze-battery/-/blob/main/apps/front/app/profile/layout.tsx

import { PropsWithChildren } from "react";
import RouteWrapper from "../../components/RouteWrapper";

export default function ProfileLayout({ children }: PropsWithChildren) {
  return (
    <RouteWrapper>
      <div className="flex flex-col items-center h-full pt-10 pb-16 lg:pt-12 lg:pb-16 lg:px-20 bg-backgroundMain">
        <section className="bg-backgroundPrimary flex flex-col px-6 py-8 w-full lg:py-15 lg:px-17">
          {children}
        </section>
      </div>
    </RouteWrapper>
  );
}
"use client";

import { useQuery } from "@tanstack/react-query";
import { useRouter } from "next/navigation";
import { getLogoutUrl, logoutJWT, retrieveJWT } from "@myze/fo-sdk";
import { getFOSDKConfig } from "../helpers/getFOSDKConfig";
import { useStorage } from "./useStorage";

const queryKey = ["jwt"];
const logoutUrl = getLogoutUrl(getFOSDKConfig());

export const useLogin = () => {
  const { isSuccess: isLoggedIn, isLoading: isLoginLoading } = useQuery({
    queryKey,
    queryFn: () => retrieveJWT(window.gigya!),
  });

  return {
    isLoggedIn,
    isLoginLoading,
  };
};

export const useLogout = () => {
  const { replace } = useRouter();
  const { clearStorage } = useStorage();

  const logout = async (): Promise<void> => {
    await logoutJWT(window.gigya!);
    clearStorage();
    replace(logoutUrl);
  };

  return { logout };
};

Dans le frontend:

const { isLoading, data }: VitalityFaqQueryType = useClientQuery({
        queryCacheKey: ['getFaqListByPageAndParams'],
        queryBuilder: async () =>
            buildClientQuery({
                queryBaseUrl: VitalityApiConfig.VITALITY_BFF_URL,
                query: GetFaqListByPageAndParams,
                variables: {},
            }),
    });
helabenkhalfallah commented 1 month ago

Mutation example:

Frontend:

const CreateOrEditApplication = gql`
    mutation CreateOrEditAccount($accountInput: ApplicationCreateOrEditInput!) {
        createOrEditApplication(applicationInput: $applicationInput) {
            _id
        }
    }
`;

Create useMutationAdapter: https://github.com/ekino/v6y/tree/main/src/v6y-front/src/infrastructure/adapters/api

Inside useMutationAdapter, we use TanStackQuery Mutation: https://tanstack.com/query/latest/docs/framework/react/reference/useMutation

export const useClientMutation = <TData = unknown,>({
    mutationCacheKey,
    mutationBuilder,
}: UseClientMutationParams<TData>) => {
    return useMutation<TData, Error>(params);
};

Consume Mutation:

    const {
        isLoading,
        data,
    } = useClientMutation(params);

BFF: 1) Create a folder account 2) Create a type: AccountType 3) Add AccountType to VitalityTypes 4) Add type : CreateOrEditAccountType 5) Add AccountType to CreateOrEditAccountType 6) Add AccountMutationsType 7) Add type : AccountMutationsType 8) Create resolver for each AccountMutationsType function

const createOrEditApplication = async (
    _: unknown,
    params: { applicationInput: ApplicationInputType },
) => {
    try {

9) Add Database model and Provider in v6y-commons/database then export on v6y-commons/index.js 10) Use exposed model and provider in the resolver.

helabenkhalfallah commented 1 month ago

Tasks:

maelaubert56 commented 1 month ago

User Account Management

Super Admin:

Admin:

User: