diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..5282678 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: rwieruch +patreon: # rwieruch +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: # Replace with a single custom sponsorship URL diff --git a/.travis.yml b/.travis.yml index 1968f21..1d8352c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,10 @@ language: node_js node_js: - - stable + - '10' install: - npm install script: - - npm test \ No newline at end of file + - npm test diff --git a/README.md b/README.md index ad1923b..d0b9d6d 100644 --- a/README.md +++ b/README.md @@ -2,15 +2,23 @@ [![Build Status](https://travis-ci.org/the-road-to-react-with-firebase/react-redux-firebase-authentication.svg?branch=master)](https://travis-ci.org/the-road-to-react-with-firebase/react-redux-firebase-authentication) [![Slack](https://slack-the-road-to-learn-react.wieruch.com/badge.svg)](https://slack-the-road-to-learn-react.wieruch.com/) [![Greenkeeper badge](https://badges.greenkeeper.io/the-road-to-react-with-firebase/react-redux-firebase-authentication.svg)](https://greenkeeper.io/) -* [Tutorial](https://www.robinwieruch.de/complete-firebase-authentication-react-tutorial/) -* [Live Version of half of the Tutorial](https://react-redux-firebase-authentication.wieruch.com/) +* [React + Firebase Tutorial](https://www.robinwieruch.de/complete-firebase-authentication-react-tutorial/) +* [React + Firebase + Redux Tutorial](https://www.robinwieruch.de/react-firebase-redux-tutorial) + +## Variations + +* [Only React Version](https://github.com/the-road-to-react-with-firebase/react-firebase-authentication) +* [MobX Version](https://github.com/the-road-to-react-with-firebase/react-mobx-firebase-authentication) +* [Gatsby Version](https://github.com/the-road-to-react-with-firebase/react-gatsby-firebase-authentication) +* [Firestore Version](https://github.com/the-road-to-react-with-firebase/react-firestore-authentication) +* [Semantic UI Version](https://github.com/the-road-to-react-with-firebase/react-semantic-ui-firebase-authentication) ## Features * uses: * only React (create-react-app) - * firebase 5 - * react-router 4 + * firebase + * react-router * **redux** * features: * Sign In @@ -26,17 +34,104 @@ * Auth Persistence with Local Storage * Database with Users and Messages +## License + +### Commercial license + +If you want to use this starter project to develop commercial sites, themes, projects, and applications, the Commercial license is the appropriate license. With this option, your source code is kept proprietary. Purchase an commercial license for different team sizes: + +* [1 Developer](https://gum.co/react-with-firebase-starter-pack-developer) +* [Team of up to 8 Developers](https://gum.co/react-with-firebase-starter-pack-team) +* [Unlimited Developers of an Organization](https://gum.co/react-with-firebase-starter-pack-organization) + +It grants you also access to the other starter projects in this GitHub organization. + +### Open source license + +If you are creating an open source application under a license compatible with the [GNU GPL license v3](https://www.gnu.org/licenses/gpl-3.0.html), you may use this starter project under the terms of the GPLv3. + ## Installation * `git clone git@github.com:the-road-to-react-with-firebase/react-redux-firebase-authentication.git` * `cd react-redux-firebase-authentication` * `npm install` * `npm start` -* visit http://localhost:3000/ -* Use your own Firebase Credentials +* visit http://localhost:3000 + +Get an overview of Firebase, how to create a project, what kind of features Firebase offers, and how to navigate through the Firebase project dashboard in this [visual tutorial for Firebase](https://www.robinwieruch.de/firebase-tutorial/). + +### Firebase Configuration + +* copy/paste your configuration from your Firebase project's dashboard into one of these files + * *src/components/Firebase/firebase.js* file + * *.env* file + * *.env.development* and *.env.production* files + +The *.env* or *.env.development* and *.env.production* files could look like the following then: + +``` +REACT_APP_API_KEY=AIzaSyBtxZ3phPeXcsZsRTySIXa7n33NtQ +REACT_APP_AUTH_DOMAIN=react-firebase-s2233d64f8.firebaseapp.com +REACT_APP_DATABASE_URL=https://react-firebase-s2233d64f8.firebaseio.com +REACT_APP_PROJECT_ID=react-firebase-s2233d64f8 +REACT_APP_STORAGE_BUCKET=react-firebase-s2233d64f8.appspot.com +REACT_APP_MESSAGING_SENDER_ID=701928454501 +``` + +### Activate Sign-In Methods + +![firebase-enable-google-social-login_640](https://user-images.githubusercontent.com/2479967/49687774-e0a31e80-fb42-11e8-9d8a-4b4c794134e6.jpg) + +* Email/Password +* [Google](https://www.robinwieruch.de/react-firebase-social-login/) +* [Facebook](https://www.robinwieruch.de/firebase-facebook-login/) +* [Twitter](https://www.robinwieruch.de/firebase-twitter-login/) +* [Troubleshoot](https://www.robinwieruch.de/react-firebase-social-login/) + +### Activate Verification E-Mail + +* add a redirect URL for redirecting a user after an email verification into one of these files + * *src/components/Firebase/firebase.js* file + * *.env* file + * *.env.development* and *.env.production* files + +The *.env* or *.env.development* and *.env.production* files could look like the following then (excl. the Firebase configuration). + +**Development:** + +``` +REACT_APP_CONFIRMATION_EMAIL_REDIRECT=http://localhost:3000 +``` + +**Production:** + +``` +REACT_APP_CONFIRMATION_EMAIL_REDIRECT=https://mydomain.com +``` -### Use your own Firebase Credentials +### Security Rules -* visit https://firebase.google.com and create a Firebase App -* copy and paste your Credentials from your Firebase App into *src/components/Firebase/firebase.js* file or in .env file -* activate Email/Password, Google, Facebook and Twitter Sign-In Methods for your Firebase App +``` +{ + "rules": { + ".read": false, + ".write": false, + "users": { + "$uid": { + ".read": "$uid === auth.uid || root.child('users/'+auth.uid).child('roles').hasChildren(['ADMIN'])", + ".write": "$uid === auth.uid || root.child('users/'+auth.uid).child('roles').hasChildren(['ADMIN'])" + }, + ".read": "root.child('users/'+auth.uid).child('roles').hasChildren(['ADMIN'])", + ".write": "root.child('users/'+auth.uid).child('roles').hasChildren(['ADMIN'])" + }, + "messages": { + ".indexOn": ["createdAt"], + "$uid": { + ".write": "data.exists() ? data.child('userId').val() === auth.uid : newData.child('userId').val() === auth.uid" + }, + ".read": "auth != null", + ".write": "auth != null", + }, + } +} +``` diff --git a/src/components/Account/index.js b/src/components/Account/index.js index 9963006..60d06b9 100644 --- a/src/components/Account/index.js +++ b/src/components/Account/index.js @@ -1,11 +1,8 @@ import React, { Component } from 'react'; +import { connect } from 'react-redux'; import { compose } from 'recompose'; -import { - AuthUserContext, - withAuthorization, - withEmailVerification, -} from '../Session'; +import { withAuthorization, withEmailVerification } from '../Session'; import { withFirebase } from '../Firebase'; import { PasswordForgetForm } from '../PasswordForget'; import PasswordChangeForm from '../PasswordChange'; @@ -29,17 +26,13 @@ const SIGN_IN_METHODS = [ }, ]; -const AccountPage = () => ( - - {authUser => ( -
-

Account: {authUser.email}

- - - -
- )} -
+const AccountPage = ({ authUser }) => ( +
+

Account: {authUser.email}

+ + + +
); class LoginManagementBase extends Component { @@ -223,9 +216,14 @@ class DefaultLoginToggle extends Component { const LoginManagement = withFirebase(LoginManagementBase); +const mapStateToProps = state => ({ + authUser: state.sessionState.authUser, +}); + const condition = authUser => !!authUser; export default compose( + connect(mapStateToProps), withEmailVerification, withAuthorization(condition), )(AccountPage); diff --git a/src/components/Admin/index.js b/src/components/Admin/index.js index cd0c7b6..1038e5e 100644 --- a/src/components/Admin/index.js +++ b/src/components/Admin/index.js @@ -20,7 +20,7 @@ const AdminPage = () => ( ); const condition = authUser => - authUser && authUser.roles.includes(ROLES.ADMIN); + authUser && !!authUser.roles[ROLES.ADMIN]; export default compose( withEmailVerification, diff --git a/src/components/Firebase/firebase.js b/src/components/Firebase/firebase.js index b791945..719e6dc 100644 --- a/src/components/Firebase/firebase.js +++ b/src/components/Firebase/firebase.js @@ -73,7 +73,7 @@ class Firebase { // default empty roles if (!dbUser.roles) { - dbUser.roles = []; + dbUser.roles = {}; } // merge auth and db user diff --git a/src/components/Home/index.js b/src/components/Home/index.js index c37755c..75510b2 100644 --- a/src/components/Home/index.js +++ b/src/components/Home/index.js @@ -1,52 +1,21 @@ -import React, { Component } from 'react'; -import { connect } from 'react-redux'; +import React from 'react'; import { compose } from 'recompose'; import { withAuthorization, withEmailVerification } from '../Session'; -import { withFirebase } from '../Firebase'; import Messages from '../Messages'; -class HomePage extends Component { - componentDidMount() { - this.props.firebase.users().on('value', snapshot => { - this.props.onSetUsers(snapshot.val()); - }); - } +const HomePage = () => ( +
+

Home Page

+

The Home Page is accessible by every signed in user.

- componentWillUnmount() { - this.props.firebase.users().off(); - } - - render() { - const { users } = this.props; - - return ( -
-

Home Page

-

The Home Page is accessible by every signed in user.

- - -
- ); - } -} - -const mapStateToProps = state => ({ - users: state.userState.users, -}); - -const mapDispatchToProps = dispatch => ({ - onSetUsers: users => dispatch({ type: 'USERS_SET', users }), -}); + +
+); const condition = authUser => !!authUser; export default compose( - withFirebase, - connect( - mapStateToProps, - mapDispatchToProps, - ), withEmailVerification, withAuthorization(condition), )(HomePage); diff --git a/src/components/Messages/MessageItem.js b/src/components/Messages/MessageItem.js index 78a9bb2..b92f954 100644 --- a/src/components/Messages/MessageItem.js +++ b/src/components/Messages/MessageItem.js @@ -28,7 +28,7 @@ class MessageItem extends Component { }; render() { - const { message, onRemoveMessage } = this.props; + const { authUser, message, onRemoveMessage } = this.props; const { editMode, editText } = this.state; return ( @@ -41,29 +41,31 @@ class MessageItem extends Component { /> ) : ( - - {message.user.username || message.user.userId} - {' '} - {message.text} {message.editedAt && (Edited)} + {message.userId} {message.text} + {message.editedAt && (Edited)} )} - {editMode ? ( + {authUser.uid === message.userId && ( - - - - ) : ( - - )} + {editMode ? ( + + + + + ) : ( + + )} - {!editMode && ( - + {!editMode && ( + + )} + )} ); diff --git a/src/components/Messages/MessageList.js b/src/components/Messages/MessageList.js index 4e0c49b..fb18585 100644 --- a/src/components/Messages/MessageList.js +++ b/src/components/Messages/MessageList.js @@ -3,6 +3,7 @@ import React from 'react'; import MessageItem from './MessageItem'; const MessageList = ({ + authUser, messages, onEditMessage, onRemoveMessage, @@ -10,6 +11,7 @@ const MessageList = ({