In-Class exercise: Authentication and Authorization

The goal for this exercise is to add authentication and authorization to the People app.

Authentication

  1. We'll be using the following GitHub project:
    1. If you haven't yet already cloned that project, follow the instructions.
    2. If you have already cloned that project:
      1. Pull the latest changes (git pull from terminal, or Git/Pull... menu from WebStorm).
      2. Checkout the specified branch above
  2. Rename App to SignedInApp (leave it in App.js for convenience). Then, don't modify it for the Authentication portion of this exercise.

    Create a new component App that calls useAuthState, and, if the user is not signed in, includes a new component, SignIn, in App that has a button to sign in via Google.

    If the user is signed in, display their email address.

    Test to ensure that the SignIn component is displayed, and that when the user signs in via Google, they are signed in and their email address is displayed.

    Hint:

    import { useAuthState } from 'react-firebase-hooks/auth';
    
  3. Add a "Sign out" button to App that allows the user to logout.

    Test to ensure it works.

  4. Skip down to the Authorization section and, once you've finished with everything but the challenge, come back to this step.
  5. Add a SignUp page with email and password text fields and a "Sign up" button.

    Modify the App component to display either:

    Test to ensure signing up works.

    Hint:

    import { useCreateUserWithEmailAndPassword } from 'react-firebase-hooks/auth';
    
  6. Add to the SignIn page email and password text fields and a "Sign in" button.

    Test to ensure signing in by email works.

    Hint:

    import { useSignInWithEmailAndPassword } from 'react-firebase-hooks/auth';
    
  7. Modify the App component to display a "Validate email" button if the user doesn't have a validated email (check user.emailVerified).

    Test to verifying email works.

    Hint: user.sendEmailVerification() will send an email.

At this point, the user can see data in the app only if they are signed in.

Authorization

Now it's time to add controls so that the user can see only their own data.

Here are the rules for the database we're using:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    function signedIn() {
      return request.auth.uid != null;
    }

    function isDocOwner() {
      return request.auth.uid == resource.data.owner;
    }

    function updatedDocHasCorrectOwner() {
      return request.auth.uid == request.resource.data.owner;
    }

    function updatedDocHasSameOwner() {
      return resource.data.owner == request.resource.data.owner;
    }

    function updatedDocHasSameSharing() {
      return resource.data.sharedWith == request.resource.data.sharedWith;
    }

    function isSharedWithMe() {
      return request.auth.token.email in resource.data.sharedWith;
    }

    match /People-NoAuthenticationNeeded/{person} {
        allow read, write: if request.time >u timestamp.date(2022, 6, 1);
    }

    match /People-AuthenticationRequired/{person} {
      allow read: if signedIn() && isDocOwner();
      allow create: if signedIn() && updatedDocHasCorrectOwner();
      allow update: if signedIn() && isDocOwner() &&
        updatedDocHasCorrectOwner();
      allow delete: if signedIn() && isDocOwner();
    }

    match /People-SharingAllowed/{person} {
      allow read: if signedIn() && isSharedWithMe();
      allow create: if signedIn() && updatedDocHasCorrectOwner();
      allow update: if signedIn() && isSharedWithMe() &&
        updatedDocHasSameOwner() && updateDocHasSameSharing();
      allow delete: if signedIn() && isDocOwner();
    }

  }
}
Note that the People-NoAuthenticationNeeded collection has very lax rules.
  1. If you were to use a collection other than the three specified in the rules, what would the permissions be?
  2. Currently, the app uses the People-NoAuthenticationNeeded collection which has wide-open rules. Switch to using the People-AuthenticationRequired collection.

    Try the app.

    Why do you get a Missing or insufficient permissions error?

  3. Modify the useCollectionData query to get rid of the permissions error.

    Verify that there is no error when you display the empty list of people.

  4. Now, create a person.

    Why do you get a Missing or insufficient permissions error (check the console)?

  5. Modify handleAddPerson to get rid of the permissions error.

    Verify that there is no error when you create a new person.

  6. Now, edit a person.

    What part of the Firestore rules prevents your client from editing data not created by your app?

    If that rule weren't there, how could your client modify some other user's data?

  7. Now, delete a person.

    What part of the Firestore rules prevents your client from deleting data not created by your app?

    If that rule weren't there, how could your client delete some other user's data?

  8. Challenge: Support sharing people with other users.

    Hints: