0.4.6 • Published 3 years ago

@zestic/auth-context v0.4.6

Weekly downloads
-
License
UNLICENSED
Repository
github
Last release
3 years ago

AuthContext

Setup

This requires @zestic/app-storage. To initialize it, add the createStore() method to the top of your index.tsx after the import statements.

// index.tsx
import { ApolloClient, ApolloProvider, InMemoryCache } from '@apollo/client';
import { AuthProvider, getAuthToken } from '@zestic/auth-context';

const authLink = setContext(async (_, { headers }) => {
  const token = await getAuthToken();

  // return the headers to the context so httpLink can read them
  return {
    headers: {
      ...headers,
      Authorization: token ? `Bearer ${token}` : '',
    },
  };
});


const client = new ApolloClient({
  link: authLink.concat(uploadLink),
  cache: new InMemoryCache(),
});

ReactDOM.render(
  <AuthProvider>
    <App />
  </AuthProvider>
  document.getElementById('root'),
);

In the App.tsx file, make sure the authentication has been initialized.

// App.tsx
import { useAuth } from "@zestic/auth-context";

const App: React.FC = () => {
  const { initialize } = useAuth()!;
  const [isInitialized, setIsInitialized] = useState(false);

  useEffect(() => {
    let isMounted = true;
    (async () => {
      if (!isMounted) {
        return;
      }
      await initialize();
      setIsInitialized(true);
    })();

    return () => {
      isMounted = false;
    };
  }, []);

  return (
    <>
      <AppRoutes />
    </>
  );
};

###Route Guards

You can add guards to your routes with the <AuthenticatedRoute> and <UnauthenticatedRoute> components

// AppRoutes.tsx

const AppRoutes: React.FC = () => {
  return (
    <Switch>
      <Route exact path='/home'>
        <HomePage />
      </Route>
      <AuthenticatedRoute exact path='/settings'>
        <SettingsPage />
      </Route>
      <UnauthenticatedRoute redirectTo='/dashboard' path='/login'>
        <LoginPage />
      </UnauthenticatedRoute>
    </Switch>
  );
};

<AuthenticatedRoute> is used when the route is only accessible when the user is authentication.

<UnauthenticatedRoute> is a route only for users who are not authenticated.

###Creating a Login Page

The login page is a little more involved.

import { useFormik } from "formik";
import * as yup from "yup";

import { useApolloClient } from "@apollo/client";

import {
  AUTHENTICATE_ADMIN,
  AuthenticateAdminOutput,
} from "../../../graphql/mutations/authenticateAdmin";

interface LoginProps {
  username: string;
  password: string;
}

const validationSchema = yup.object({
  username: yup.string().required("A username is required"),
  password: yup
    .string()
    .min(6, "Password should be of minimum 8 characters length")
    .required("Password is required"),
});

const LoginPage: React.FC = () => {
  const client = useApolloClient();
  const [AuthenticatedResponse, setAuthenticatedResponse] = useState<
    AuthenticatedResponse | undefined
  >(undefined);

  const formik = useFormik({
    initialValues: {
      username: "",
      password: "",
    },
    validationSchema: validationSchema,
    onSubmit: async (values: LoginProps) => {
      await authenticateUser(values as LoginProps);
    },
  });

  const authenticateUser = async (credentials: LoginProps) => {
    try {
      const { data } = await client.mutate<AuthenticateAdminOutput>({
        mutation: AUTHENTICATE_ADMIN,
        variables: credentials,
      });

      if (!data || !data.authenticateAdmin) {
        return {
          success: false,
        };
      }
      setAuthenticatedResponse(data.authenticateAdmin);
    } catch (error) {
      return {
        success: false,
        message: "There is a system error, please try again later",
      };
    }
  };

  return (
    <>
      {AuthenticatedResponse && (
        <ProcessLogin
          AuthenticatedResponse={AuthenticatedResponse}
          redirect={"dashboard"}
        />
      )}

      <Container component="main" maxWidth="xs">
        <div>
          <Typography component="h1" variant="h5">
            Sign in
          </Typography>
          <form onSubmit={formik.handleSubmit}>
            <TextField
              required
              id="username"
              label="Username"
              name="username"
              value={formik.values.username}
              onChange={formik.handleChange}
              error={formik.touched.username && Boolean(formik.errors.username)}
              helperText={formik.touched.username && formik.errors.username}
            />
            <TextField
              required
              name="password"
              label="Password"
              type="password"
              id="password"
              value={formik.values.password}
              onChange={formik.handleChange}
              error={formik.touched.password && Boolean(formik.errors.password)}
              helperText={formik.touched.password && formik.errors.password}
            />
            <Button type="submit" fullWidth variant="contained" color="primary">
              Sign In
            </Button>
          </form>
        </div>
      </Container>
    </>
  );
};
0.4.6

3 years ago

0.4.5

3 years ago

0.4.4

3 years ago

0.4.3

3 years ago

0.4.2

3 years ago

0.4.1

3 years ago

0.4.0

3 years ago

0.3.0

3 years ago

0.2.1

3 years ago

0.2.0

3 years ago

0.1.40

3 years ago

0.1.38

3 years ago

0.1.37

3 years ago

0.1.36

3 years ago

0.1.35

3 years ago

0.1.34

3 years ago

0.1.33

3 years ago

0.1.32

3 years ago

0.1.29

3 years ago