├── .gitignore ├── Dockerfile ├── Dockerfile-prod ├── README.md ├── package.json ├── public ├── favicon.ico └── index.html └── src ├── App.test.js ├── components ├── Home.js ├── Login.js ├── Register.js ├── index.js └── protected │ └── Dashboard.js ├── config └── constants.js ├── helpers └── auth.js ├── index.css └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # testing 7 | coverage 8 | 9 | # production 10 | build 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | npm-debug.log 16 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:8-alpine as builder 2 | 3 | WORKDIR /code/ 4 | 5 | EXPOSE 3000 6 | 7 | COPY . . 8 | 9 | RUN npm install 10 | 11 | ENTRYPOINT npm run start 12 | -------------------------------------------------------------------------------- /Dockerfile-prod: -------------------------------------------------------------------------------- 1 | FROM node:8-alpine as builder 2 | 3 | WORKDIR /code/ 4 | 5 | EXPOSE 3000 6 | 7 | COPY . . 8 | 9 | RUN npm install --unsafe-perm 10 | 11 | RUN npm run build 12 | 13 | 14 | FROM nginx 15 | 16 | WORKDIR /usr/share/nginx/html 17 | 18 | COPY --from=builder /code/build . 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Auth with React Router V4 and Firebase V3 2 | This is an example repo for authenticating with Firebase and React Router. 3 | 4 | For more info, visit [Protected routes and authentication with React Router v4](https://tylermcginnis.com/react-router-protected-routes-authentication/) 5 | 6 | *Using React 15.4.0, React Router 4, and Firebase 3.6.1* 7 | 8 | #### Features: 9 | * Protected Routes with React Router 10 | * Register new users with Firebase 11 | * Add new users to ```/users``` in your Firebase database 12 | * Login/Logout Functionality 13 | * Simple Boostrap UI 14 | 15 | #### Instructions: 16 | * Swap out the firebase config in ```config/constants``` with your own 17 | * ```npm install``` 18 | * ```npm start``` 19 | * Visit ```localhost:3000``` 20 | 21 | #### Try it out in a [Docker](https://www.docker.com/) container: 22 | * Run a container running the prod version: `docker run -p 8080:80 -d allthethings/react-router-firebase-auth` 23 | * **Or** build a dev version, locally: `docker build -t react-router-firebase-auth .` 24 | * Then run the image (listens for changes to src): `docker run -v "$(pwd)/src:/code/src" -p 3000:3000 -d --name react-router-firebase-auth react-router-firebase-auth` 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-router-firebase-auth", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "react-scripts": "0.9.5" 7 | }, 8 | "dependencies": { 9 | "bootstrap": "^3.3.7", 10 | "firebase": "^3.6.1", 11 | "react": "^15.4.0", 12 | "react-dom": "^15.4.0", 13 | "react-router-dom": "^4.0.0-beta.8" 14 | }, 15 | "scripts": { 16 | "start": "react-scripts start", 17 | "build": "react-scripts build", 18 | "test": "react-scripts test --env=jsdom", 19 | "eject": "react-scripts eject" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tylermcginnis/react-router-firebase-auth/c999cd9a5c80236aefefaf3a87b2f3c8194a30f0/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 16 | React App 17 | 18 | 19 |
20 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './components'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | }); 9 | -------------------------------------------------------------------------------- /src/components/Home.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | 3 | export default class Home extends Component { 4 | render () { 5 | return ( 6 |
7 | Home. Not Protected. Anyone can see this. 8 |
9 | ) 10 | } 11 | } -------------------------------------------------------------------------------- /src/components/Login.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { login, resetPassword } from '../helpers/auth' 3 | 4 | function setErrorMsg(error) { 5 | return { 6 | loginMessage: error 7 | } 8 | } 9 | 10 | export default class Login extends Component { 11 | state = { loginMessage: null } 12 | handleSubmit = (e) => { 13 | e.preventDefault() 14 | login(this.email.value, this.pw.value) 15 | .catch((error) => { 16 | this.setState(setErrorMsg('Invalid username/password.')) 17 | }) 18 | } 19 | resetPassword = () => { 20 | resetPassword(this.email.value) 21 | .then(() => this.setState(setErrorMsg(`Password reset email sent to ${this.email.value}.`))) 22 | .catch((error) => this.setState(setErrorMsg(`Email address not found.`))) 23 | } 24 | render () { 25 | return ( 26 |
27 |

Login

28 |
29 |
30 | 31 | this.email = email} placeholder="Email"/> 32 |
33 |
34 | 35 | this.pw = pw} /> 36 |
37 | { 38 | this.state.loginMessage && 39 |
40 | 41 | Error: 42 |  {this.state.loginMessage} Forgot Password? 43 |
44 | } 45 | 46 |
47 |
48 | ) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/components/Register.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { auth } from '../helpers/auth' 3 | 4 | function setErrorMsg(error) { 5 | return { 6 | registerError: error.message 7 | } 8 | } 9 | 10 | export default class Register extends Component { 11 | state = { registerError: null } 12 | handleSubmit = (e) => { 13 | e.preventDefault() 14 | auth(this.email.value, this.pw.value) 15 | .catch(e => this.setState(setErrorMsg(e))) 16 | } 17 | render () { 18 | return ( 19 |
20 |

Register

21 |
22 |
23 | 24 | this.email = email} placeholder="Email"/> 25 |
26 |
27 | 28 | this.pw = pw} /> 29 |
30 | { 31 | this.state.registerError && 32 |
33 | 34 | Error: 35 |  {this.state.registerError} 36 |
37 | } 38 | 39 |
40 |
41 | ) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/components/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import 'bootstrap/dist/css/bootstrap.css' 3 | import { Route, BrowserRouter, Link, Redirect, Switch } from 'react-router-dom' 4 | import Login from './Login' 5 | import Register from './Register' 6 | import Home from './Home' 7 | import Dashboard from './protected/Dashboard' 8 | import { logout } from '../helpers/auth' 9 | import { firebaseAuth } from '../config/constants' 10 | 11 | function PrivateRoute ({component: Component, authed, ...rest}) { 12 | return ( 13 | authed === true 16 | ? 17 | : } 18 | /> 19 | ) 20 | } 21 | 22 | function PublicRoute ({component: Component, authed, ...rest}) { 23 | return ( 24 | authed === false 27 | ? 28 | : } 29 | /> 30 | ) 31 | } 32 | 33 | export default class App extends Component { 34 | state = { 35 | authed: false, 36 | loading: true, 37 | } 38 | componentDidMount () { 39 | this.removeListener = firebaseAuth().onAuthStateChanged((user) => { 40 | if (user) { 41 | this.setState({ 42 | authed: true, 43 | loading: false, 44 | }) 45 | } else { 46 | this.setState({ 47 | authed: false, 48 | loading: false 49 | }) 50 | } 51 | }) 52 | } 53 | componentWillUnmount () { 54 | this.removeListener() 55 | } 56 | render() { 57 | return this.state.loading === true ?

Loading

: ( 58 | 59 |
60 | 88 |
89 |
90 | 91 | 92 | 93 | 94 | 95 |

No Match

} /> 96 |
97 |
98 |
99 |
100 |
101 | ); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/components/protected/Dashboard.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | 3 | export default class Dashboard extends Component { 4 | render () { 5 | return ( 6 |
7 | Dashboard. This is a protected route. You can only see this if you're authed. 8 |
9 | ) 10 | } 11 | } -------------------------------------------------------------------------------- /src/config/constants.js: -------------------------------------------------------------------------------- 1 | import firebase from 'firebase' 2 | 3 | const config = { 4 | apiKey: "AIzaSyDHL6JFTyBcaV60WpE4yXfeO0aZbzA9Xbk", 5 | authDomain: "practice-auth.firebaseapp.com", 6 | databaseURL: "https://practice-auth.firebaseio.com", 7 | } 8 | 9 | firebase.initializeApp(config) 10 | 11 | export const ref = firebase.database().ref() 12 | export const firebaseAuth = firebase.auth -------------------------------------------------------------------------------- /src/helpers/auth.js: -------------------------------------------------------------------------------- 1 | import { ref, firebaseAuth } from '../config/constants' 2 | 3 | export function auth (email, pw) { 4 | return firebaseAuth().createUserWithEmailAndPassword(email, pw) 5 | .then(saveUser) 6 | } 7 | 8 | export function logout () { 9 | return firebaseAuth().signOut() 10 | } 11 | 12 | export function login (email, pw) { 13 | return firebaseAuth().signInWithEmailAndPassword(email, pw) 14 | } 15 | 16 | export function resetPassword (email) { 17 | return firebaseAuth().sendPasswordResetEmail(email) 18 | } 19 | 20 | export function saveUser (user) { 21 | return ref.child(`users/${user.uid}/info`) 22 | .set({ 23 | email: user.email, 24 | uid: user.uid 25 | }) 26 | .then(() => user) 27 | } 28 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './components'; 4 | import './index.css'; 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ); 10 | --------------------------------------------------------------------------------