Creating a login page

In order for users to sign up for an account and eventually log in, we need a page. We are going to create that in a new services/web/src/routes/auth/login.svelte file given below. Click Run and navigate to services/web:

cd services/web

Adding a Sign In Button

Let’s add a simple button to sign in and a click handler to the login.svelte file given below:

<script>
  const signIn = () => {
    // Open FirebaseUI
  }
</script>

<button on:click={signIn}>Sign In</button>

Adding a menu item for Sign In page

To reach this page, we are going to add a new menu item to the navbar in the src/components/Nav.svelte file given below:

<script>
  ...
  import AuthNavItem from "./auth-nav-item.svelte";
  ...
</script>

<style>
  ...
  li:last-child {
    float: right;
  }
  ...
</style>
...
    <li><AuthNavItem/></li>
  </ul>
</nav>

Authenticating navbar item

An auth-nav-item.svelte component is created in the same directory as the Nav.svelte component we updated just now. Add the following content to it:

<style>
  a {
    /* Matches the styles in Nav.svelte */
    text-decoration: none;
    padding: 1em 0.5em;
    display: block;
  }
</style>

<a href="/auth/login">Sign In</a>

At this point, you can start the dev serverrunApp and you will see a new “Sign In” navigation menu item on the right side of the screen. A click on this link brings you to the login page with a button that doesn’t do anything. Time to change that!

Failing tests

If you were to run your tests now in another terminal with npm run cy:run, they would fail. Can you guess why? Try it out,. Start the dev server and run the tests to see what went wrong.

That makes sense, as we added an additional menu item to the navbar, but one of our test cases did not expect that. Have a look at the test suite in the file services/web/cypress/components/nav.spec.js given below and update the failing test to make sure it expects four menu items.

Creating a FirebaseUI configuration object

Back in the src/routes/auth/login.svelte page, what we need is to create a FirebaseUI configuration object. This is where we configure which authentication providers to display and also what to do once a user successfully registers an account and/or signs in.

Update the <script> tag to match the following:

<script>
  import { goto } from '@sapper/app'; // Function to go to a specific URL
  import { onMount } from "svelte"; // Function to perform specific operations when a component mounts
  
  export let firebaseAuthUiConfig = {};
  
  onMount(() => {
    firebaseAuthUiConfig = {
      callbacks: {
        signInSuccessWithAuthResult: () => {
          goto("/admin");
          return false;
        }
      },
      credentialHelper: firebaseui.auth.CredentialHelper.NONE,
      signInFlow: "popup",
      signInOptions: [firebase.auth.EmailAuthProvider.PROVIDER_ID]
    };
  });

  const signIn = () => {
    firebaseAuthUi.start("#firebaseui-auth-container", firebaseAuthUiConfig);
  };
</script>

A few things to note:

  • We specify the firebaseAuthUiConfig in the onMount() Svelte lifecycle methodlifecycle. This is because the firebase and firebaseui variables are only available to the client.
  • In the signInSuccessWithAuthResult callback, we navigate to the /admin page. We will configure that shortly. This is whatever URL you choose that is only available to authenticated users. The return false tells FirebaseUI that we take care of the redirect when authentication succeeds.
  • The signIn click handler specifies a #firebaseui-auth-container container. This is where FirebaseUI is going to render its user interface.

Below the sign in button in login.svelte, add the following empty div:

<div id="firebaseui-auth-container"></div>

Go back to your web application and click the Sign In navigation link to bring up the FirebaseUI screen. Provide your email address and click “Next”. It will show an error message.

Create account vs. sign in

The first time you enter your email address and click “Next”, FirebaseUI checks its user database and notices you don’t yet have an account. The UI updates on-the-fly to prompt for your name and password.

Going forward, once you enter your email address, FirebaseUI recognizes the email address and will prompt for your password.

The given sign-in provider is disabled

Oops… Firebase does not allow us to create an email and password account because we have not yet enabled this authentication provider.

To enable the Email/Password sign-in method:

  1. Open the Firebase web console.
  2. Click on “Authentication” in the navigation.
  3. Select the “Sign-in method” tab.
  4. Click “Email/Password” at the top.
  5. Click the first “Enable” toggle. To learn about the second “Enable” toggle, please refer to this link.
  6. Click “Save”.

Back on the login page, click “Next” again and type your first & last name, choose a password, and click “Save”.

You’ll get the error message “404 Not found”. Perfect, that’s what we are looking for, at the moment. Note the URL you’re on: it’s /admin, exactly as we configured it in the FirebaseUI config object earlier.

Hiding the Sign In button

Before we create the admin page, we want to hide the “Sign In” button when the user is signing in. To do that, we update the src/routes/auth/login.svelte page:

  1. Add the following code above the onMount() function.
let isSignInUiShown = false;
  1. Add a uiShown callback to the firebaseAuthUiConfig object, so it looks like this:
firebaseAuthUiConfig = {
  callbacks: {
    signInSuccessWithAuthResult: () => {
      goto("/admin");
      return false;
     },
     uiShown: () => {
       isSignInUiShown = true;
     }
   },
   credentialHelper: firebaseui.auth.CredentialHelper.NONE,
   signInFlow: "popup",
   signInOptions:[firebase.auth.EmailAuthProvider.PROVIDER_ID]
};
  1. Wrap the Sign In button in an {#if !isSignInUiShown} statement, as:
{#if !isSignInUiShown}
<button on:click={signIn}>Sign In</button>
{/if}

Get hands-on with 1200+ tech skills courses.