This project was bootstrapped with Create React App, with additional templates:
Build a full app with authentication in ~10 minutes
- Pre-built Login Pages using Cotter
- Authentication State Management
- User Management
- Backend and database using Brev
- Step-by-step guide
Run the project
To run the project:
npm install
# or
yarn
then run
npm start
# or
yarn start
and open http://localhost:3000
Copy your Cotter API KEY ID
Go to Cotter's Dashboard, create a project, and copy your API KEY ID.
Then paste your API Key ID to src/apiKeys.js
.
Follow the tutorial on the home page
Try logging-in and then add API Endpoints using Brev following this guide.
As you can see, we've already included CotterProvider
for authentication state management. It's already included in pages/_app
to wrap over your root component.
The LoginForm
component automatically sends a verification email or SMS to the entered email or phone number, and respond with the results.
To use the LoginForm
component, you can import LoginForm
and do the following:
<LoginForm
type="EMAIL" // - EMAIL or PHONE
authMethod="MAGIC_LINK" // - OTP or MAGIC_LINK
onBegin={onSignupBegin} // - A function that runs before verification email/SMS is sent
onSuccess={onSignupSuccess} // - A function that runs after the login/signup is successful
onError={onSignupError} // - A function that runs if the login/signup encountered an error
width={340} // - Width & height of the form
height={300} // Recommended at least 300x300
additionalFields={[ // - The form includes 1 field for email/phone. Use this to add
{ // more fields.
name: "name",
label: "Full Name",
placeholder: "Enter your full name",
},
]}
styles={loginFormStyles} // - You can style the form on Cotter's Dashboard or pass in CSS here.
/>
onBegin
function:
The onBegin
function receives the user entered fields as a parameter.
- To stop submission and display an error, return a string with the error message.
- To continue submission with no error, return null.
Learn more about
onBegin
function
For example:
const myOnBeginFunction = (payload) => {
if (payload.identifier === "[email protected]") {
// If there's an error, return a string with the error message
return "Phone Number is not allowed";
}
// If there's no error, return null
return null;
}
// Payload object looks like this
var payload = {
identifier: "[email protected]",
identifier_type: "PHONE",
device_type: "BROWSER",
device_name: "Chrome ...",
client_json: { // This is available if you set up AdditionalFields
"name": "Hello World",
"address": "Street Address"
}
};
onSuccess
function:
When the user successfully login or sign up, the onSuccess
function will receive a response from Cotter that includes the user-entered fields an a JWT token to validate the user successfully verified their email or phone number.
Learn more about onSuccess
function
For example:
const onSuccessFunc = (payload) => {
// Send data to Server
axios
.post("https://slug.brev.dev/login", payload)
.then((resp) => console.log("Response From Server", resp))
.catch((err) => console.log(err));
}
// Payload object looks like this:
var payload {
"email": "[email protected]", // User's email (or phone number),
"name": "Hello World",
"address": "Street Address",
"oauth_token": {
"access_token": "eyJhbGciOiJFUzI1NiIsImt...", // Access Token to validate
"id_token": "eyJhbGciOiJFUzI1Ni...",
"refresh_token": "27805:CNf76faa8trMhjXM...",
"expires_in": 3600,
"token_type": "Bearer",
"auth_method": "OTP"
},
"user": {
"ID": "abcdefgh-abcd-abcd-abcd-af6f81fb5432", // Cotter User ID
"issuer": "<YOUR_API_KEY_ID>",
"identifier": "[email protected]"
}
}
onError
function:
When there's an error, the onError
function will be invoked. The error returned may differ based on the error, you should display an error message when this is invoked, and try it in console.log
to see the error responses.
additionalFields
:
The default form includes 1 field which is the email input or phone number input. If you want to collect more information, use this to add more fields.
For example:
const additionalFields = [
{
label: "Full Name",
name: "name",
placeholder: "Enter your full name"
},
{
label: "Address",
name: "address",
placeholder: "Enter your address"
},
{
label: "Prefilled Info",
name: "prefilled",
type: "hidden", // availabe types are the same as HTML input type.
initial_value: "autofill value"
}
]
styles
:
There are 2 ways to style your login form.
- Customize the form from the Cotter Dashboard
- Pass in a styles object
To pass in a styles object, you can find each component's class name using inspect element, then add the style for each class name like the following:
const styles = {
input_label: {
fontFamily: "Roboto",
fontSize: 15,
color: "red",
fontWeight: 700,
},
input_text_container_default: {
backgroundColor: "#fce883",
padding: "20px 60px",
},
input_text: {
backgroundColor: "#fce883",
fontFamily: "Roboto",
fontSize: 20,
},
button_container: {
borderRadius: 0,
},
button_text: {
color: "aqua",
},
}
The CotterProvider
provides you with useful authentication state and the current user information. To use it:
const { isLoading, isLoggedIn, user, logout, getCotter, apiKeyID, checkLoggedIn } = useContext(CotterContext);
isLoading
(bool): tells you if the CotterProvider is loading the necessary dataisLoggedIn
(bool): tells you if the user is logged-in or notuser
(object): gives you the user object of the currently logged-in user
const user = {
"ID": "abcdefgh-abcd-abcd-abcd-67ebae3cdfcf",
"issuer": "abcdefgh-abcd-abcd-abcd-5cc8b69051e8",
"client_user_id": "abcdefgh-abcd-abcd-abcd-67ebae3cdfcf",
"enrolled": [
"WEBAUTHN"
],
"identifier": "[email protected]"
}
logout
(async function): a function to logout the user
<div onClick={logout}>Log Out</div>
getCotter
(function): a function to get the Cotter object. Check out the docs on what you can do with the Cotter object.apiKeyID
(string): your API KEY ID that you passed-in to theCotterProvider
checkLoggedIn
(async function): a function to force theCotterProvider
to check if the user's logged-in
In the project directory, you can run:
Runs the app in the development mode.
Open http://localhost:3000 to view it in the browser.
The page will reload if you make edits.
You will also see any lint errors in the console.
Launches the test runner in the interactive watch mode.
See the section about running tests for more information.
Builds the app for production to the build
folder.
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.
Your app is ready to be deployed!
See the section about deployment for more information.
Note: this is a one-way operation. Once you eject
, you can’t go back!
If you aren’t satisfied with the build tool and configuration choices, you can eject
at any time. This command will remove the single build dependency from your project.
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except eject
will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
You don’t have to ever use eject
. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
You can learn more in the Create React App documentation.
To learn React, check out the React documentation.
This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting
This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size
This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app
This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration
This section has moved here: https://facebook.github.io/create-react-app/docs/deployment
This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify