Logging In & Out
In the previous step, we've added the <Authenticated />
component to our src/App.tsx
file to protect our content from unauthenticated users. Now, we'll be implementing the login
and logout
methods in our auth provider to enable our users to login and logout.
Implementing the login
Method
The login
method will be used to authenticate the user and other related operations such as storing the token etc. It should return a Promise
which resolves to an object. The object should contain success
property to indicate whether the login operation was successful or not.
Our fake REST API requires us to send a POST
request to /auth/login
endpoint with the email
and password
parameters in the request body. It will return a token
in the response body.
We'll also be storing the token
in the localStorage
for later use.
Update your src/providers/auth-provider.ts
file by adding the following lines:
import { AuthProvider } from "@refinedev/core";
export const authProvider: AuthProvider = {
// login method receives an object with all the values you've provided to the useLogin hook.
login: async ({ email, password }) => {
const response = await fetch(
"https://api.fake-rest.refine.dev/auth/login",
{
method: "POST",
body: JSON.stringify({ email, password }),
headers: {
"Content-Type": "application/json",
},
},
);
const data = await response.json();
if (data.token) {
localStorage.setItem("my_access_token", data.token);
return { success: true };
}
return { success: false };
},
check: async () => {
const token = localStorage.getItem("my_access_token");
return { authenticated: Boolean(token) };
},
logout: async () => {
throw new Error("Not implemented");
},
onError: async (error) => {
throw new Error("Not implemented");
},
// ...
};
Using the useLogin
Hook
After implementing the login
method, we'll be able to call useLogin
hook and login our users. Let's create a component called Login
and mount it inside our <Refine />
component.
Then, we'll mount our <Login />
component and pass it to the <Authenticated />
component as the fallback
prop in our src/App.tsx
file.
Update your src/App.tsx
file by adding the following lines:
import { Refine, Authenticated } from "@refinedev/core";
import { dataProvider } from "./providers/data-provider";
import { authProvider } from "./providers/auth-provider";
import { ShowProduct } from "./pages/products/show";
import { EditProduct } from "./pages/products/edit";
import { ListProducts } from "./pages/products/list";
import { CreateProduct } from "./pages/products/create";
import { Login } from "./pages/login";
export default function App(): JSX.Element {
return (
<Refine dataProvider={dataProvider} authProvider={authProvider}>
<Authenticated
key="protected"
fallback={<Login />}
>
{/* <ShowProduct /> */}
{/* <EditProduct /> */}
<ListProducts />
{/* <CreateProduct /> */}
</Authenticated>
</Refine>
);
}
Finally, we'll import useLogin
hook and use it inside our Login
component to login our users.
Update your src/pages/login.tsx
file by adding the following lines:
import React from "react";
import { useLogin } from "@refinedev/core";
export const Login = () => {
const { mutate, isLoading } = useLogin();
const onSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
// Using FormData to get the form values and convert it to an object.
const data = Object.fromEntries(new FormData(event.target).entries());
// Calling mutate to submit with the data we've collected from the form.
mutate(data);
};
return (
<div>
<h1>Login</h1>
<form onSubmit={onSubmit}>
<label htmlFor="email">Email</label>
<input
type="email"
id="email"
name="email"
// We're providing default values for demo purposes.
defaultValue="demo@demo.com"
/>
<label htmlFor="password">Password</label>
<input
type="password"
id="password"
name="password"
// We're providing default values for demo purposes.
defaultValue="demodemo"
/>
{isLoading && <span>loading...</span>}
<button type="submit" disabled={isLoading}>
Submit
</button>
</form>
</div>
);
};
Implementing the logout
Method
The logout
method will be used to logout the user and other related operations such as removing the token etc. It should return a Promise
which resolves to an object. The object should contain success
property to indicate whether the logout operation was successful or not.
Our fake REST API doesn't require us to send any request to logout the user. We'll just be removing the token
from the localStorage
.
Update your src/providers/auth-provider.ts
file by adding the following lines:
import { AuthProvider } from "@refinedev/core";
export const authProvider: AuthProvider = {
logout: async () => {
localStorage.removeItem("my_access_token");
// We're returning success: true to indicate that the logout operation was successful.
return { success: true };
},
// login method receives an object with all the values you've provided to the useLogin hook.
login: async ({ email, password }) => {
const response = await fetch(
"https://api.fake-rest.refine.dev/auth/login",
{
method: "POST",
body: JSON.stringify({ email, password }),
headers: {
"Content-Type": "application/json",
},
},
);
const data = await response.json();
if (data.token) {
localStorage.setItem("my_access_token", data.token);
return { success: true };
}
return { success: false };
},
check: async () => {
const token = localStorage.getItem("my_access_token");
return { authenticated: Boolean(token) };
},
onError: async (error) => {
throw new Error("Not implemented");
},
// ...
};
Using the useLogout
Hook
After implementing the logout
method, we'll be able to call useLogout
hook and logout our users. Let's create a component called Header
to add a logout button and mount it inside our <Refine />
component.
Then, we'll mount our <Header />
component and pass it to the <Authenticated />
component as children in our src/App.tsx
file.
Update your src/App.tsx
file by adding the following lines:
import { Refine, Authenticated } from "@refinedev/core";
import { dataProvider } from "./providers/data-provider";
import { authProvider } from "./providers/auth-provider";
import { ShowProduct } from "./pages/products/show";
import { EditProduct } from "./pages/products/edit";
import { ListProducts } from "./pages/products/list";
import { CreateProduct } from "./pages/products/create";
import { Login } from "./pages/login";
import { Header } from "./components/header";
export default function App(): JSX.Element {
return (
<Refine dataProvider={dataProvider} authProvider={authProvider}>
<Authenticated key="protected" fallback={<Login />}>
<Header />
{/* <ShowProduct /> */}
{/* <EditProduct /> */}
<ListProducts />
{/* <CreateProduct /> */}
</Authenticated>
</Refine>
);
}
Finally, we'll import useLogout
hook and use it inside our Header
component to logout our users.
Update your src/components/header.tsx
file by adding the following lines:
import React from "react";
import { useLogout } from "@refinedev/core";
export const Header = () => {
const { mutate, isLoading } = useLogout();
return (
<>
<h2>Welcome!</h2>
<button
type="button"
disabled={isLoading}
onClick={mutate}
>
Logout
</button>
</>
);
};
Now, we'll be able to login and logout our users.
Notice that after logging in, the <Authenticated />
component will render our content instead of the fallback
prop. Same also applies to logging out. Refine will handle the invalidation of the check
method for us, so we don't need to worry about it.
In the next step, we'll be learning about the user identity and how to use it in our application.
import { Refine, Authenticated } from "@refinedev/core"; import { dataProvider } from "./providers/data-provider"; import { authProvider } from "./providers/auth-provider"; import { ShowProduct } from "./pages/products/show"; import { EditProduct } from "./pages/products/edit"; import { ListProducts } from "./pages/products/list"; import { CreateProduct } from "./pages/products/create"; export default function App(): JSX.Element { return ( <Refine dataProvider={dataProvider} authProvider={authProvider} > <Authenticated key="protected" fallback={<div>Not authenticated</div>} > {/* <ShowProduct /> */} {/* <EditProduct /> */} <ListProducts /> {/* <CreateProduct /> */} </Authenticated> </Refine> ); }