@thestartupfactory/react-auth
TypeScript icon, indicating that this package has built-in type declarations

1.0.9 • Public • Published

tsf-react-auth

This library contains standard front end react componets required for auth flow. Flows supported and component included:

  • SignIn

  • Forgot Password

  • Confirm Email

  • Set Password

Usage

The library requires Auth API specified by the caller. The API is used to drive behaviour of the auth componets, as well as ensure the strong typing of the Aith models matching the application where library is used.

The Auth API contract:

signInFn: (details: SignInDetails) => Promise<ResponseState<AUTH_RESPONSE>>;
  requestPasswordResetFn: (
    request: EmailRequest
  ) => Promise<ResponseState<unknown>>;
confirmEmailFn: (
    request: ConfirmEmailRequest
  ) => Promise<ResponseState<unknown>>;
setPasswordFn: (
    request: SetPasswordRequest
  ) => Promise<ResponseState<AUTH_RESPONSE>>;
getAuthFn: () => Promise<ResponseState<AUTH_RESPONSE>>;
signOutFn: () => Promise<unknown>;

The generic type AUTH_RESPONSE must confirm to the signIn and getAuth API functions, is definted by the library user to match the auth model, e.g.:

export const authOutputModel = z.object({
  id: z.string(),
  email: z.string().min(1),
  orgId: z.string(),
  role: role,
  status: activationStatus,
  token: z.string().min(1),
});

export type AuthOutputModel = z.infer<typeof authOutputModel>;

In order to initialize the auth library, use createAuthComponents init function, passing your own AuthAPI implementation, then export the library-provided compoents and hooks:

const authComponents = createAuthComponents({
  signInFn: (details: Auth.signin.Input) =>
    client.provide('post', '/auth', details),
  requestPasswordResetFn: (request: Auth.emailrequest.Input) =>
    client.provide('post', '/auth/passwordreset', request),
  confirmEmailFn: (request: Auth.confirmemail.Input) =>
    client.provide('put', '/auth/emailconfirmation', request),
  setPasswordFn: (request: Auth.resetpassword.Input) =>
    client.provide('put', '/auth/passwordreset', request),
  getAuthFn: () => client.provide('get', '/auth', {}),
  signOutFn: () => client.provide('delete', '/auth', {}),
});

export const useAuth = authComponents.useAuth;
export const AuthProvider = authComponents.AuthProvider;
export const SignIn = authComponents.SignIn;
export const PasswordResetRequest = authComponents.PasswordResetRequest;
export const ConfirmEmail = authComponents.ConfirmEmail;
export const SetPassword = authComponents.SetPassword;

Next, wrap up authenticated routes with the AuthProvider component, to get access to useAuth hook

 {
    path: '/',
    element: <AuthProvider><AuthedRoot /></AuthProvider>),
    errorElement: <ErrorScreen />,
    children: [
      {
        path: '/organizations',
        element: <Organizations.List />,
      },

Next, set up routes for standard auth componet,s such as SignIn, ResetPassword...

const router = createBrowserRouter([
  {
    path: '/p',
    element: <UnauthedRoot />,
    errorElement: <ErrorScreen />,

    children: [
      {
        path: '/p/signin',
        element: authSettingRoute(
          <SignIn />
        ),
        errorElement: <ErrorScreen />,
      },
      {
        path: '/p/passwordresetrequest',
        element: (
          <PasswordResetRequest />
        ),
        errorElement: <ErrorScreen />,
      },
      {
        path: '/p/confirmemail/:token',
        element: <ConfirmEmail />,
        errorElement: <ErrorScreen />,
      },
      {
        path: '/p/setpassword/:token',
        element: <SetPassword/>
        ,
        errorElement: <ErrorScreen />,
      },
    ],
  },

Use useAuth hook to get the current authenticated user in the component - e.g. to render different menus depending of user role or auth status:

  const auth = useAuth();
  const role =
    auth?.user.status === 'success' ? auth.user.data.role : undefined;
  return (
    <>
      <SideBar>
        <img src={logo} alt="Coperceptuo" />
        {role && ['PLATFORM_ADMIN', 'ORG_LEAD'].includes(role) && (
          <StyledNavLink to={`dashboards/`}>Dashboards</StyledNavLink>
        )}

To handle failed authorization (redirecting the user to the SignIn Screen), use AuthAwareFailedRequest component

switch (request.status) {
    case 'loading':
      return <Loading />;
    case 'error':
      return <AuthAwareFailedRequestComponent
        message={message}
        unauthorized={message === 'Unauthorized'}
      />
    case 'success': {
      return <BarChart title={title} data={data} />;
    }
    default:
      return exhaustivenessCheck(request);
  }

Customizing components

Form style and structure

There are a number of React ElementTypes you can pass to the auth componets to override look&feel (not behaviour).

type ComponentOverrides = {
  pageContainer?: React.ElementType;
  formElement?: React.ElementType;
  submitButton?: React.ElementType;
  formRow?: React.ElementType;
  textInput?: React.ElementType;
  contactUsButton?: React.ReactElement;
};

For example, to change the top level container of auth components:

const container = styled.div`
  background-color: yellow;
`;

<SignIn
  components={{
    pageContainer: container,
  }}
/>

Custom extensions

Auth library has come extension points that allow you to add non-auth related behaviour to auth pagee. Currently only one extension is upported - adding a Contact Us button to each page, so that user can ask for help if they have trouble signing in:

const contactUs = (
  <ContactButton darker={false} onClick={() => alert('Contact us')}>
    Contact us
  </ContactButton>
);

<PasswordResetRequest components={{ contactUsButton: contactUs }} />

Navigation behaviour

By default, the library will use redirect strategy to redirect user to sign in page on auth failure or logout. This requires specific auth routes to be set, as descibed in the Usage section above.

You can override this behaviour to use different strategy: rendering sign in component in place in case of auth failure. To do this, provide signinNavStrategy config to the AuthAPI:

const authComponents = createAuthComponents({
  signInFn: (details: Auth.signin.Input) =>
    client.provide('post', '/auth', details),
  requestPasswordResetFn: (request: Auth.emailrequest.Input) =>
    client.provide('post', '/auth/passwordreset', request),
  confirmEmailFn: (request: Auth.confirmemail.Input) =>
    client.provide('put', '/auth/emailconfirmation', request),
  setPasswordFn: (request: Auth.resetpassword.Input) =>
    client.provide('put', '/auth/passwordreset', request),
  getAuthFn: () => client.provide('get', '/auth', {}),
  signOutFn: () => client.provide('delete', '/auth', {}),
  signinNavStrategy:{
    renderSigninScreenInPlace:true
  }
});

In addition, make sure to provide the SignIn component to be rendered when user is not authenticated:

  <AuthProvider
    signInComponent={<SignIn/>}
  >
    {element}
  </AuthProvider>

Readme

Keywords

none

Package Sidebar

Install

npm i @thestartupfactory/react-auth

Weekly Downloads

13

Version

1.0.9

License

MIT

Unpacked Size

163 kB

Total Files

79

Last publish

Collaborators

  • ericc_thestartupfactory.tech