@zestic/auth-context v0.4.6
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>
</>
);
};
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago