├── github
└── routing.gif
├── web
├── src
│ ├── index.css
│ ├── Pages
│ │ ├── Home
│ │ │ └── index.js
│ │ ├── Dashboard
│ │ │ └── index.js
│ │ ├── User
│ │ │ └── index.js
│ │ └── SignIn
│ │ │ └── index.js
│ ├── components
│ │ └── SomeSpinner
│ │ │ └── index.js
│ ├── hooks
│ │ ├── useAuth.js
│ │ └── useLocalStorage.js
│ ├── setupTests.js
│ ├── index.js
│ ├── services
│ │ └── auth.js
│ ├── App.js
│ ├── Routes
│ │ ├── index.js
│ │ └── PrivateRoute.js
│ ├── Layout
│ │ ├── styles.css
│ │ └── index.js
│ └── contexts
│ │ └── auth.js
├── public
│ ├── robots.txt
│ ├── favicon.ico
│ ├── manifest.json
│ └── index.html
├── .gitignore
└── package.json
├── README.md
└── LICENSE
/github/routing.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cassiopieroni/reactjs-auth/HEAD/github/routing.gif
--------------------------------------------------------------------------------
/web/src/index.css:
--------------------------------------------------------------------------------
1 | * {
2 | margin: 0;
3 | padding: 0;
4 | box-sizing: border-box;
5 | }
--------------------------------------------------------------------------------
/web/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/web/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cassiopieroni/reactjs-auth/HEAD/web/public/favicon.ico
--------------------------------------------------------------------------------
/web/src/Pages/Home/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const Home = () =>
Home Page
4 |
5 | export default Home;
--------------------------------------------------------------------------------
/web/src/components/SomeSpinner/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const SomeSpinner = () => (
4 |
5 |
8 | );
9 |
10 | export default SomeSpinner;
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # reactjs-auth
2 | Autenticação de Rotas no React.JS com Context API - sem conexão com um servidor ( apenas simulando uma autenticação).
3 |
4 | 
5 |
--------------------------------------------------------------------------------
/web/src/hooks/useAuth.js:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react';
2 |
3 | import AuthContext from '../contexts/auth';
4 |
5 | const useAuth = () => {
6 |
7 | const auth = useContext(AuthContext);
8 | return auth;
9 | }
10 |
11 | export default useAuth;
--------------------------------------------------------------------------------
/web/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom/extend-expect';
6 |
--------------------------------------------------------------------------------
/web/src/Pages/Dashboard/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 |
4 | const Dashboard = () => {
5 |
6 | return (
7 |
8 |
Dashboard Page
9 |
Private route
10 |
11 | );
12 | }
13 |
14 | export default Dashboard;
--------------------------------------------------------------------------------
/web/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 |
4 | import App from './App';
5 |
6 | import './index.css';
7 |
8 |
9 | ReactDOM.render(
10 |
11 |
12 | ,
13 | document.getElementById('root')
14 | );
15 |
--------------------------------------------------------------------------------
/web/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/web/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/web/src/services/auth.js:
--------------------------------------------------------------------------------
1 | export const signInService = () => {
2 |
3 | return new Promise( resolve => {
4 |
5 | setTimeout( () => {
6 | resolve({
7 | token: 'jk12h3j21h3jk212h3jk12h3jkh12j3kh12k123hh21g3f12f3',
8 | user: {
9 | name: 'admin',
10 | email: 'admin@authApp.com',
11 | }
12 | });
13 | }, 2000);
14 | });
15 | }
--------------------------------------------------------------------------------
/web/src/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { BrowserRouter } from "react-router-dom";
3 |
4 | import { AuthProvider } from './contexts/auth';
5 |
6 | import Layout from './Layout';
7 | import Routes from './Routes/';
8 |
9 |
10 | function App() {
11 | return (
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | );
20 | }
21 |
22 | export default App;
23 |
--------------------------------------------------------------------------------
/web/src/Routes/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Switch, Route } from "react-router-dom";
3 |
4 | import PrivateRoute from '../Routes/PrivateRoute';
5 |
6 | import Home from '../Pages/Home';
7 | import SignIn from '../Pages/SignIn';
8 | import Dashboard from '../Pages/Dashboard';
9 | import User from '../Pages/User';
10 |
11 |
12 | const Routes = () => (
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | );
21 |
22 | export default Routes;
--------------------------------------------------------------------------------
/web/src/Routes/PrivateRoute.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Redirect, Route } from 'react-router-dom';
3 | import useAuth from '../hooks/useAuth';
4 |
5 | import SomeSpinner from '../components/SomeSpinner';
6 |
7 |
8 | const PrivateRoute = ({ component: Component, ...props }) => {
9 |
10 | const { signed, loading } = useAuth();
11 |
12 | if (loading) {
13 | return
14 | }
15 |
16 | return (
17 |
18 | signed
21 | ?
22 | :
23 | }
24 | />
25 | )
26 | }
27 |
28 | export default PrivateRoute;
--------------------------------------------------------------------------------
/web/src/Pages/User/index.js:
--------------------------------------------------------------------------------
1 | import React, { useCallback } from 'react';
2 |
3 | import useAuth from '../../hooks/useAuth';
4 |
5 | import SomeSpinner from '../../components/SomeSpinner';
6 |
7 |
8 | const User = () => {
9 |
10 | const { user, signOut, loading } = useAuth();
11 |
12 | const handleSignOut = useCallback( () => {
13 | signOut();
14 | }, [signOut]);
15 |
16 |
17 | if (loading) {
18 | return (
19 | <>
20 | User page
21 | { user.name }
22 |
23 | >
24 | )
25 | }
26 |
27 | return (
28 |
29 |
User
30 | { user.name }
31 |
32 |
33 | );
34 | }
35 |
36 | export default User;
--------------------------------------------------------------------------------
/web/src/Layout/styles.css:
--------------------------------------------------------------------------------
1 | .layout {
2 | min-height: 100vh;
3 | min-width: 100%;
4 | }
5 |
6 | .layout header {
7 |
8 | padding: 20px 30px;
9 | display: flex;
10 | justify-content: space-between;
11 | border-bottom: 1px solid black;
12 | }
13 |
14 | .layout header div a {
15 |
16 | margin: 8px;
17 | }
18 |
19 | .layout main {
20 |
21 | height: 100%;
22 | padding: 100px 30px;
23 | display: flex;
24 | align-items: center;
25 | justify-content: center;
26 | }
27 |
28 | .layout main h1, .layout main h2, .layout main p {
29 |
30 | text-align: center;
31 | margin: 8px;
32 | }
33 |
34 | .layout main button {
35 |
36 | padding: 8px 16px;
37 | text-align: center;
38 | margin: 8px auto;
39 | cursor: pointer;
40 | margin-left: 12px;
41 | }
42 |
43 | .layout main .loading {
44 | display: flex;
45 | flex-direction: column;
46 | }
--------------------------------------------------------------------------------
/web/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "teste",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^4.2.4",
7 | "@testing-library/react": "^9.3.2",
8 | "@testing-library/user-event": "^7.1.2",
9 | "react": "^16.13.1",
10 | "react-dom": "^16.13.1",
11 | "react-router-dom": "^5.2.0",
12 | "react-scripts": "3.4.1"
13 | },
14 | "scripts": {
15 | "start": "react-scripts start",
16 | "build": "react-scripts build",
17 | "test": "react-scripts test",
18 | "eject": "react-scripts eject"
19 | },
20 | "eslintConfig": {
21 | "extends": "react-app"
22 | },
23 | "browserslist": {
24 | "production": [
25 | ">0.2%",
26 | "not dead",
27 | "not op_mini all"
28 | ],
29 | "development": [
30 | "last 1 chrome version",
31 | "last 1 firefox version",
32 | "last 1 safari version"
33 | ]
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/web/src/Layout/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-router-dom';
3 |
4 | import useAuth from '../hooks/useAuth';
5 |
6 | import './styles.css';
7 |
8 |
9 | const Layout = ({ children }) => {
10 |
11 | const { signed, user } = useAuth();
12 |
13 | return (
14 |
15 |
16 |
29 |
30 |
31 |
32 | { children }
33 |
34 |
35 |
36 | )
37 | }
38 |
39 | export default Layout;
--------------------------------------------------------------------------------
/web/src/Pages/SignIn/index.js:
--------------------------------------------------------------------------------
1 | import React, { useCallback } from 'react';
2 |
3 | import { Redirect } from 'react-router-dom';
4 |
5 | import useAuth from '../../hooks/useAuth';
6 |
7 | import SomeSpinner from '../../components/SomeSpinner';
8 |
9 |
10 | const SignIn = () => {
11 |
12 | const { signed, signIn, loading } = useAuth();
13 |
14 | const handleSignIn = useCallback( () => {
15 | signIn();
16 | }, [signIn]);
17 |
18 |
19 | if (loading) {
20 | return (
21 |
22 |
SignIn Page
23 |
24 |
25 | )
26 | }
27 |
28 |
29 | return (
30 |
31 |
32 | { signed ? (
33 |
34 | ) : (
35 | <>
36 |
SignIn Page
37 |
38 | >
39 | )}
40 |
41 | );
42 | }
43 | export default SignIn;
--------------------------------------------------------------------------------
/web/src/hooks/useLocalStorage.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect, useCallback } from 'react';
2 |
3 |
4 | const useLocalStorage = (key, defaultValue = null) => {
5 |
6 | const storageValue = localStorage.getItem(key);
7 | const initValue = storageValue ? JSON.parse(storageValue) : null;
8 |
9 | const [value, setValue] = useState(initValue);
10 | useEffect( () => {
11 |
12 | if (defaultValue) {
13 | localStorage.setItem(key, JSON.stringify(defaultValue))
14 | setValue(defaultValue)
15 | }
16 | }, []);
17 |
18 |
19 | const updatingValue = useCallback( newValue => {
20 |
21 | localStorage.setItem(key, JSON.stringify(newValue));
22 | return setValue(newValue);
23 | }, [key]);
24 |
25 |
26 | const removingValue = useCallback( () => {
27 |
28 | localStorage.removeItem(key);
29 | return setValue(null);
30 | }, [key]);
31 |
32 |
33 | return [
34 | value,
35 | valueToUp => updatingValue(valueToUp),
36 | () => removingValue()
37 | ]
38 | }
39 |
40 | export default useLocalStorage;
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Cássio Pieroni
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/web/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
16 |
17 |
26 | AuthApp
27 |
28 |
29 |
30 |
31 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/web/src/contexts/auth.js:
--------------------------------------------------------------------------------
1 | import React, { createContext, useState, useEffect, useCallback } from 'react';
2 | import { signInService } from '../services/auth';
3 | import useLocalStorage from '../hooks/useLocalStorage';
4 |
5 |
6 | const AuthContext = createContext({});
7 |
8 |
9 | export const AuthProvider = ({ children }) => {
10 |
11 | const [storageUser, setStorageUser, removeStorageUser] = useLocalStorage('@authApp: user');
12 | const [storageToken, setStorageToken, removeStorageToken] = useLocalStorage('@authApp: token');
13 |
14 | const [user, setUser] = useState({});
15 | const [loading, setLoading] = useState(true);
16 |
17 |
18 | useEffect(() => {
19 |
20 | if (storageUser && storageToken) {
21 | setUser(storageUser);
22 | // api.defaults.headers.Authorization = `Baerer ${storagedToken}`;
23 | }
24 | setLoading(false);
25 | }, []);
26 |
27 |
28 | const signIn = useCallback( async () => {
29 |
30 | setLoading(true);
31 | const response = await signInService();
32 | setUser(response.user);
33 | // api.defaults.headers.Authorization = `Baerer ${response.token}`;
34 | setStorageUser(response.user);
35 | setStorageToken(response.token);
36 | setLoading(false);
37 | }, []);
38 |
39 |
40 | const signOut = useCallback( () => {
41 |
42 | setLoading(true);
43 | removeStorageUser();
44 | removeStorageToken()
45 | setUser({});
46 | setLoading(false);
47 | }, []);
48 |
49 |
50 | return (
51 |
52 |
59 | { children }
60 |
61 | )
62 | }
63 |
64 |
65 | export default AuthContext;
--------------------------------------------------------------------------------