DEV Community

Cover image for Build a serverless chat app with Svelte and Firebase (PART 2)
arnu515
arnu515

Posted on

Build a serverless chat app with Svelte and Firebase (PART 2)

In the last part, we initialised our application. In this part, we'll look at authentication using Firebase.

Make sure you've gone through the first part before continuing!

The github code for this project is available here

We've already setup authentication on firebase, so let's do the setup on our app!

Email and password authentication

We'll mostly be focusing on one file, src/components/Auth.svelte.

Register

Let's first add register functionality! We'll only need to update the register() function.

<!-- src/components/Auth.svelte -->

<script lang="ts">
    // ...
    import {auth} from "../services/firebase";
    import {createEventDispatcher} from "svelte";

    const d = createEventDispatcher();

    // ...

    function register() {
        // if you're using js, remove the "as HTMLInputElement" for each of them
        const email = (document.getElementById("r-email") as HTMLInputElement).value
        const password = (document.getElementById("r-password") as HTMLInputElement).value
        const cpassword = (document.getElementById("r-cpassword") as HTMLInputElement).value

        // form validation
        if (!email || !password || !cpassword) {
            err = "Fill out all fields!"
            return;
        }
        if (password !== cpassword) {
            err = "Passwords don't match!"
            return;
        }
        err = "";

        // creating the user
        auth.createUserWithEmailAndPassword(email, password).then(() => {d("done"); d("auth")}).catch(e => {
            err = `(${e.code}) ${e.message}`
        })
    }

    // ...
</script>

<!-- ... -->
Enter fullscreen mode Exit fullscreen mode

Time to test it out! Run your app and open http://localhost:3000. Now, you can register an account and you should see that account appear in the firebase auth page on the firebase console

Firebase auth page

Login

Now for login! Just like register, we only have to edit the login() function.

<!-- src/components/Auth.svelte -->

<script lang="ts">
    // ...

    function login() {
        // again, remove "as HTMLInputElement" if using js
        const email = (document.getElementById("l-email") as HTMLInputElement).value
        const password = (document.getElementById("l-password") as HTMLInputElement).value

        // basic form validation
        if (!email || !password) {
            err = "Fill out all fields!"
            return;
        }
        err = "";

        // sign in using firebase
        auth.signInWithEmailAndPassword(email, password).then(() => {d("done"); d("auth")}).catch(e => {
            err = `(${e.code}) ${e.message}`
        })
    }

    // ...
</script>

<!-- ... -->
Enter fullscreen mode Exit fullscreen mode

Now, once we test that, you should hopefully see a green checkmark! Yay! We can now login and register with an email and a password.

Google signin

Now, let's address the easier one, signing in with Google. Firebase makes it as easy as one method!

<!-- src/components/Auth.svelte -->

<script lang="ts">
    // ...

    function google() {
        auth.signInWithPopup(googleAuth).then(() => {
            d("auth");
            d("done");
        }).catch(e => {
            err = `(${e.code}) ${e.message}`
        })
    }

    // ...
</script>

<!-- ... -->
Enter fullscreen mode Exit fullscreen mode

Now, when you click the signin with Google button, it should allow you to sign in! Hurrah

Logout

Let's also add functionality to the logout button. Again, we only need to change the logout() method, and again, firebase makes it super easy!

<!-- src/components/Auth.svelte -->

<script lang="ts">
    // ...

    function logout() {
        if (auth.currentUser) {
            auth.signOut().then(() => {d("done"); d("logout")}).catch(e => {
                throw new Error(e)
            });
        }
    }

    // ...
</script>

<!-- ... -->
Enter fullscreen mode Exit fullscreen mode

Now you may wonder why I'm dispatching events left and right everywhere. This is because we can use this Auth component in web pages and get to know when the person logs in. One such implementation is the index.svelte route:

<!-- src/routes/index.svelte -->
<script lang="ts">
    import Auth from "../components/Auth.svelte";
</script>

<h1 class="w3-jumbo w3-center">Serverless Chat</h1>
<p class="w3-large w3-center">Please log in</p>

<Auth on:auth={() => window.location.href = "/chat"} />
Enter fullscreen mode Exit fullscreen mode

I also created an auth.svelte route, so that users can directly login, but, this time, we can specify which action we want to do, instead of register at first, and also where to go next after we've logged in.

<!-- src/routes/auth.svelte -->

<script lang="ts">
    import Auth from "../components/Auth.svelte";

    export let qs;

    qs.action = qs.action || "register"
    if (!["login", "register"].includes(qs.action)) qs.action = "register"
</script>

<h1 class="w3-center w3-jumbo">Authenticate to Serverless Chat</h1>
<Auth authMode={qs.action} on:auth={() => window.location.href=qs.next || "/"} />
Enter fullscreen mode Exit fullscreen mode

Let's add a route to this page in App.svelte. While we're there, let's also add a 404 route.

<!-- src/App.svelte -->
<script lang="ts">
    import router from "page";
    import Auth from "./routes/auth.svelte";
    import Index from "./routes/index.svelte";
    import NotFound from "./routes/notfound.svelte";
    import { queryString } from "./services/util";

    let page;
    let params = {};
    let qs = {};

    router("/", () => page = Index)
    router("/404/:msg", (ctx: {params: {}, querystring: string}, next) => {
        params = ctx.params;
        next();
    }, () => page = NotFound)
    router("/auth", (ctx, next) => {
        qs = queryString(ctx.querystring)
        next();
    }, () => page = Auth)
    router("/login", () => router.redirect("/auth"))
    router("/register", () => router.redirect("/auth"))

    router("/*", () => page = NotFound)
    router.start();
</script>

<svelte:component this={page} {params} {qs} />
Enter fullscreen mode Exit fullscreen mode

src/services/util.ts:

<!-- src/services/util.ts -->

export function queryString(unparsed: string): {} {
    const separated = unparsed.replace("?", "").split("&");
    if (!separated) return {};
    const query = {};
    for (let item of separated) {
        const x = item.split("=");
        query[x[0]] = x[1];
    }
    return query;
}
Enter fullscreen mode Exit fullscreen mode

src/routes/notfound.svelte

<!-- src/routes/notfound.svelte -->

<script lang="ts">
    export let params = {msg: ""}
</script>

<h1 class="w3-jumbo w3-center">404</h1>
<p class="w3-large w3-center">Not found</p>
<p class="w3-center w3-text-gray">{params.msg || "The page you were looking for was not found."}<br> <a href="/">Home</a></p>
Enter fullscreen mode Exit fullscreen mode

And there! That wraps up the authentication part! Stay tuned for the next part, which is the chat functionality itself! The link to the 3rd part is here

Top comments (2)

Collapse
 
hk7math profile image
hk7math

Did you catch the d('done') event? I search on:done in your repo and found no result..

Collapse
 
arnu515 profile image
arnu515

Nope, I don't think I used that code anywhere, but I think I planned to so so and forgot about it. Oops :P