Skip to content

Commit d0eefc6

Browse files
committed
implement user authentication with auth0
1 parent c0eda0f commit d0eefc6

18 files changed

+1704
-142
lines changed

NOTICE

Lines changed: 1402 additions & 71 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 4 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,11 @@
1-
# React/JavaScript: Starter SPA Code Sample
1+
# React/JavaScript: Basic User Authentication Code Sample
22

3-
This JavaScript code sample demonstrates how to build a Single-Page Application (SPA) using React. This React code sample builds the API server using the React Router 6 library.
3+
This JavaScript code sample demonstrates **how to implement user authentication** in React applications using Auth0. This React code sample builds the Single-Page Application (SPA) using the React Router 6 library.
44

5-
Visit the ["React/JavaScript Code Samples: SPA Security in Action"](https://developer.auth0.com/resources/code-samples/spa/react) section of the ["Auth0 Developer Resources"](https://developer.auth0.com/resources) to explore how you can secure React applications written in JavaScript by implementing endpoint protection and authorization with Auth0.
5+
This code sample is part of the ["Auth0 Developer Resources"](https://developer.auth0.com/resources), a place where you can explore the authentication and authorization features of the Auth0 Identity Platform.
66

7-
[![React/JavaScript Code Samples: SPA Security in Action](https://cdn.auth0.com/blog/hub/code-samples/spa/react-javascript.png)](https://developer.auth0.com/resources/code-samples/spa/react)
7+
Visit the ["React/JavaScript + React Router 6 Code Sample: User Authentication For Basic Apps"](https://developer.auth0.com/resources/code-samples/spa/react/basic-authentication) page for instructions on how to configure and run this code sample and how to integrate it with an API server of your choice to [create a full-stack code sample](https://developer.auth0.com/resources/code-samples/full-stack/hello-world/basic-access-control/spa).
88

99
## Why Use Auth0?
1010

1111
Auth0 is a flexible drop-in solution to add authentication and authorization services to your applications. Your team and organization can avoid the cost, time, and risk that come with building your own solution to authenticate and authorize users. We offer tons of guidance and SDKs for you to get started and [integrate Auth0 into your stack easily](https://developer.auth0.com/resources/code-samples/full-stack).
12-
13-
## Set Up and Run the React Project
14-
15-
Install the project dependencies:
16-
17-
```bash
18-
npm install
19-
```
20-
21-
The starter React project offers a functional application that consumes data from an external API to hydrate the user interface. For simplicity and convenience, the starter project simulates the external API locally using [`json-server`](https://github.com/typicode/json-server).
22-
23-
However, you can also integrate this starter project with any of the ["Hello World" API code samples, which are available in multiple backend frameworks and programming languages](https://github.com/orgs/auth0-developer-hub/repositories?language=&q=api+hello-world&sort=&type=public).
24-
25-
The compatible API server runs on `http://localhost:6060` by default. As such, to connect your React application with that API server, create a `.env` file under the root project directory and populate it with the following environment variables:
26-
27-
```bash
28-
REACT_APP_API_SERVER_URL=http://localhost:6060
29-
```
30-
31-
Next, execute the following command to run the JSON server API:
32-
33-
```bash
34-
npm run api
35-
```
36-
37-
Finally, open another terminal tab and execute this command to run your React application:
38-
39-
```bash
40-
npm start
41-
```
42-
43-
Visit [`http://localhost:4040/`](http://localhost:4040/) to access the starter application.
44-
45-
In the starter project, all the starter React application routes are public. However, you can use Auth0 to get an ID token to hydrate the user profile information present on the `/profile` page with information from a real user. With Auth0, you can also get an access token to make a secure call to an external API to hydrate the messages present in the `/protected` and `/admin` pages.

package-lock.json

Lines changed: 37 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"version": "0.1.0",
44
"private": true,
55
"dependencies": {
6+
"@auth0/auth0-react": "^2.0.0",
67
"axios": "^0.27.2",
78
"react": "^18.2.0",
89
"react-dom": "^18.2.0",
@@ -46,4 +47,3 @@
4647
"prettier": "^2.7.1"
4748
}
4849
}
49-

src/app.js

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,44 @@
1+
import { useAuth0 } from "@auth0/auth0-react";
12
import React from "react";
23
import { Route, Routes } from "react-router-dom";
4+
import { PageLoader } from "./components/page-loader";
5+
import { AuthenticationGuard } from "./components/authentication-guard";
36
import { AdminPage } from "./pages/admin-page";
7+
import { CallbackPage } from "./pages/callback-page";
48
import { HomePage } from "./pages/home-page";
59
import { NotFoundPage } from "./pages/not-found-page";
610
import { ProfilePage } from "./pages/profile-page";
711
import { ProtectedPage } from "./pages/protected-page";
812
import { PublicPage } from "./pages/public-page";
913

1014
export const App = () => {
15+
const { isLoading } = useAuth0();
16+
17+
if (isLoading) {
18+
return (
19+
<div className="page-layout">
20+
<PageLoader />
21+
</div>
22+
);
23+
}
24+
1125
return (
1226
<Routes>
1327
<Route path="/" element={<HomePage />} />
14-
<Route path="/profile" element={<ProfilePage />} />
28+
<Route
29+
path="/profile"
30+
element={<AuthenticationGuard component={ProfilePage} />}
31+
/>
1532
<Route path="/public" element={<PublicPage />} />
16-
<Route path="/protected" element={<ProtectedPage />} />
17-
<Route path="/admin" element={<AdminPage />} />
33+
<Route
34+
path="/protected"
35+
element={<AuthenticationGuard component={ProtectedPage} />}
36+
/>
37+
<Route
38+
path="/admin"
39+
element={<AuthenticationGuard component={AdminPage} />}
40+
/>
41+
<Route path="/callback" element={<CallbackPage />} />
1842
<Route path="*" element={<NotFoundPage />} />
1943
</Routes>
2044
);
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { Auth0Provider } from "@auth0/auth0-react";
2+
import React from "react";
3+
import { useNavigate } from "react-router-dom";
4+
5+
export const Auth0ProviderWithNavigate = ({ children }) => {
6+
const navigate = useNavigate();
7+
8+
const domain = process.env.REACT_APP_AUTH0_DOMAIN;
9+
const clientId = process.env.REACT_APP_AUTH0_CLIENT_ID;
10+
const redirectUri = process.env.REACT_APP_AUTH0_CALLBACK_URL;
11+
12+
const onRedirectCallback = (appState) => {
13+
navigate(appState?.returnTo || window.location.pathname);
14+
};
15+
16+
if (!(domain && clientId && redirectUri)) {
17+
return null;
18+
}
19+
20+
return (
21+
<Auth0Provider
22+
domain={domain}
23+
clientId={clientId}
24+
authorizationParams={{
25+
redirect_uri: redirectUri,
26+
}}
27+
onRedirectCallback={onRedirectCallback}
28+
>
29+
{children}
30+
</Auth0Provider>
31+
);
32+
};
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { withAuthenticationRequired } from "@auth0/auth0-react";
2+
import React from "react";
3+
import { PageLoader } from "./page-loader";
4+
5+
export const AuthenticationGuard = ({ component }) => {
6+
const Component = withAuthenticationRequired(component, {
7+
onRedirecting: () => (
8+
<div className="page-layout">
9+
<PageLoader />
10+
</div>
11+
),
12+
});
13+
14+
return <Component />;
15+
};
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { useAuth0 } from "@auth0/auth0-react";
2+
import React from "react";
3+
4+
export const LoginButton = () => {
5+
const { loginWithRedirect } = useAuth0();
6+
7+
const handleLogin = async () => {
8+
await loginWithRedirect({
9+
appState: {
10+
returnTo: "/profile",
11+
},
12+
authorizationParams: {
13+
prompt: "login",
14+
},
15+
});
16+
};
17+
18+
return (
19+
<button className="button__login" onClick={handleLogin}>
20+
Log In
21+
</button>
22+
);
23+
};
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { useAuth0 } from "@auth0/auth0-react";
2+
import React from "react";
3+
4+
export const LogoutButton = () => {
5+
const { logout } = useAuth0();
6+
7+
const handleLogout = () => {
8+
logout({
9+
logoutParams: {
10+
returnTo: window.location.origin,
11+
},
12+
});
13+
};
14+
15+
return (
16+
<button className="button__logout" onClick={handleLogout}>
17+
Log Out
18+
</button>
19+
);
20+
};
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { useAuth0 } from "@auth0/auth0-react";
2+
import React from "react";
3+
4+
export const SignupButton = () => {
5+
const { loginWithRedirect } = useAuth0();
6+
7+
const handleSignUp = async () => {
8+
await loginWithRedirect({
9+
appState: {
10+
returnTo: "/profile",
11+
},
12+
authorizationParams: {
13+
prompt: "login",
14+
screen_hint: "signup",
15+
},
16+
});
17+
};
18+
19+
return (
20+
<button className="button__sign-up" onClick={handleSignUp}>
21+
Sign Up
22+
</button>
23+
);
24+
};

0 commit comments

Comments
 (0)