Authentication
In this section we add Login, Logout, and SignUp for out website.
Next steps of this tutorial require a logged-in user.
1. Create Login, Logout, and SignUp hooks
These hooks are wrappers around ReactSDK hooks. They handle the logic of authentication, and we handle
the behavior of our application. We use localStorage
to store the apiKey
.
- useLogin.ts
- useLogout.ts
- useSignUp.ts
import {useLogin as useReactSDKLogin} from "@tribeplatform/react-sdk/hooks";
import {useCallback} from "react";
export const useLogin = () => {
const {login} = useReactSDKLogin()
return useCallback((email: string, password: string) => {
login({
variables: {input: {usernameOrEmail: email, password}}
}
).then((token) => {
localStorage.setItem('apiKey', token.accessToken)
window.location.href = '/react-sdk-tutorial'
})
}, [login])
}
import {useCallback} from "react";
export const useLogout = () => {
return useCallback(() => {
localStorage.removeItem('apiKey')
window.location.href = '/react-sdk-tutorial'
}, [])
}
import {useJoinNetwork} from "@tribeplatform/react-sdk/hooks";
import {useCallback} from "react";
import {useNavigate} from "react-router-dom";
export const useSignUp = () => {
const {mutateAsync: signup} = useJoinNetwork({fields: 'basic'})
const navigate = useNavigate()
return useCallback((email: string, name: string, password: string) => {
signup({
input: {
email,
name,
password,
}
}).then(() => {
navigate('/login')
})
}, [signup, navigate])
}
2. Create Login and SignUp pages
We use the hooks above to create simple Login and SignUp pages.
- Login.tsx
- SignUp.tsx
import {useState} from "react";
import {useLogin} from "../hooks/useLogin";
export const Login = () => {
const [email, setEmail] = useState<string>('')
const [password, setPassword] = useState<string>('')
const login = useLogin()
return (
<div className="w-1/2 mx-auto mt-5">
<h1 className="text-2xl mb-5">Login</h1>
<label className="block text-gray-700 text-sm font-bold mb-2">
Email
</label>
<input
className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline mb-5"
type="email" placeholder="[email protected]" onChange={event => setEmail(event.target.value)}
/>
<label className="block text-gray-700 text-sm font-bold mb-2">
Password
</label>
<input
className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline mb-5"
type="password" placeholder="********" onChange={event => setPassword(event.target.value)}
/>
<button
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded mt-5"
onClick={() => {
login(email, password)
}}
>
Login
</button>
</div>
)
}
import {useSignUp} from "../hooks/useSignUp";
import {useState} from "react";
export const SignUp = () => {
const [email, setEmail] = useState<string>('')
const [name, setName] = useState<string>('')
const [password, setPassword] = useState<string>('')
const signup = useSignUp()
return (
<div className="w-1/2 mx-auto mt-5">
<h1 className="text-2xl mb-5">Sign Up</h1>
<label className="block text-gray-700 text-sm font-bold mb-2">
Email
</label>
<input
className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline mb-5"
type="email" placeholder="[email protected]" onChange={event => setEmail(event.target.value)}
/>
<label className="block text-gray-700 text-sm font-bold mb-2">
Name
</label>
<input
className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline mb-5"
type="text" placeholder="John" onChange={event => setName(event.target.value)}
/>
<label className="block text-gray-700 text-sm font-bold mb-2">
Password
</label>
<input
className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline mb-5"
type="password" placeholder="********" onChange={event => setPassword(event.target.value)}
/>
<div className="bg-blue-100 border-t border-b border-blue-500 text-blue-700 px-4 py-3" role="alert">
<p className="text-sm">
Password should have 1 lowercase letter, 1 uppercase letter, 1 number, 1 special character and be at
least 10 characters long.
</p>
</div>
<button
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded mt-5"
onClick={() => {
signup(email, name, password)
}}
>
Sign Up
</button>
</div>
)
}
3. Routes and Buttons
Now we need to add the authentication routes and a header to ease the flow of authentication.
We use useAuthMember
hook to retrieve the current authenticated user.
import React from 'react';
import {Routes, Route, Link} from "react-router-dom";
import NewsList from "./components/NewsList";
import {NewsPage} from "./components/NewsPage";
import {Login} from "./components/Login";
import {useLogout} from "./hooks/useLogout";
import {useAuthMember} from "@tribeplatform/react-sdk/hooks";
import {SignUp} from "./components/SignUp";
function App() {
const logout = useLogout()
const {data: user} = useAuthMember()
return (
<>
<nav className="flex justify-center lg:mt-2">
<div className="flex items-center justify-between flex-wrap lg:w-3/4 w-full p-2 bg-hacker-header">
<div className="flex items-center flex-shrink-0 text-black mr-6">
<Link to="/">
<span className="font-semibold text-xl tracking-tight">ReactSDK Tutorial</span>
</Link>
</div>
<div className="w-full block flex-grow lg:flex lg:items-center lg:w-auto text-sm">
<div className="lg:flex-grow">
<Link to="/"
className="block mt-4 lg:inline-block lg:mt-0 text-black hover:text-white mr-4">
News
</Link>
<Link to="/submit"
className="block mt-4 lg:inline-block lg:mt-0 text-black hover:text-white mr-4">
Submit
</Link>
</div>
<div>
{!user && (
<>
<Link to="/login"
className="block mt-4 lg:inline-block lg:mt-0 text-black hover:text-white mr-4">
Login
</Link>
<Link to="/signup"
className="block mt-4 lg:inline-block lg:mt-0 text-black hover:text-white mr-4">
Sign Up
</Link>
</>
)}
{user && (
<>
<span className="block mt-4 lg:inline-block lg:mt-0 text-black mr-4">
Hello, {user.name}
</span>
<div
className="block mt-4 lg:inline-block lg:mt-0 text-black hover:text-white mt-4 cursor-pointer"
onClick={() => {
logout()
}}
>
Logout
</div>
</>
)}
</div>
</div>
</div>
</nav>
<Routes>
<Route path="/" element={<NewsList/>}/>
<Route path="/login" element={<Login/>}/>
<Route path="/signup" element={<SignUp/>}/>
<Route path="/:postId" element={<NewsPage/>}/>
</Routes>
</>
);
}
export default App;
4. Modify the BettermodeProvide
The last step is to tell the BettermodeProvide to read the accessToken from the localStorage
.
import React from 'react';
import ReactDOM from 'react-dom';
import {BrowserRouter} from "react-router-dom";
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import {Provider as BettermodeProvider} from '@tribeplatform/react-sdk'
ReactDOM.render(
<React.StrictMode>
<BettermodeProvider config={{
baseUrl: 'https://api.bettermode.com',
networkDomain: 'react-sdk-tutorial.tribeplatform.com',
accessToken: localStorage.getItem('apiKey')
}}>
<BrowserRouter basename="/react-sdk-tutorial">
<App/>
</BrowserRouter>
</BettermodeProvider>
</React.StrictMode>,
document.getElementById('root')
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
If your community is hosted on a region other than the US region (us-east-1), you should use a different GraphQL URL (baseUrl
) as stated here under the GraphQL Endpoint section.
You can see the final result here.