Authenticate Users

In this tutorial, we'll explore how to authenticate users and set up conditional rendering for components using MagicJS.

The tool used in this guide is MERN.AI. You can download their free version by clicking here.

Authenticate a User

Setting Up the UI

  1. Create a file named "home" in the 'main-features' directory with the path as "/".

  2. Add a button with label "Login" and display the details of the current user using useLogin hook.

Refer the below snippet:

home.tsx
import React from "react"
import { Button } from "antd"
import { useLogin } from "@magicjs.dev/frontend"

export default function Component(props: any) {
    const { current } = useLogin()
    return (
        <div>
            <h1>{`Current User: ${current?.currentUser?.name}`}</h1>
            <Button>Login</Button>
        </div>
    )
}
Expand for Tailwind styled code.
import React from "react"
import { Button } from "antd"
import { useLogin } from "@magicjs.dev/frontend"

export default function Component(props: any) {
    const { current } = useLogin()
    return (
        <div className="flex flex-col items-center justify-center h-screen">

            <h1 className="font-semi-bold text-4xl tracking-wide pb-8">
                {`Current User: ${current?.currentUser?.name}`}
            </h1>

            <Button className="text-xl w-40 h-14">Login</Button>
        </div>
    )
}
  • The useLogin hook is imported from the '@magicjs.dev/frontend' and is used to get the current object, which contains the information about the current user's details.

  • An <h1> element is used to display the name of the current user.

  • The ?. operator is used for optional chaining, ensuring that the code does not throw an error if current or currentUser is undefined or null.

Implementing Login Functionality

Let's develop the login functionality by creating a backend file named "login".

  1. Create a backend file named "login".

The 'server.tsx' extension will be automatically appended in MERN.AI.

  1. Write the login logic within the backend file, including the input "name: string" obtained from the frontend.

  2. Call the ctx.setCurrentUser() function to set the current user.

Refer the snippet below.

login.server.tsx
import { createBackendFunction, useFunctionContext } from "@magicjs.dev/backend"

export default createBackendFunction(async function (name: string) {
    const ctx = useFunctionContext(this)

    ctx.setCurrentUser({ _id: "test-user", name })

    return "Message from server"
})
  • The function takes a single parameter name, which is expected to be a string. This parameter represents the name of the user being set as the current user.

  • Inside the function body, the useFunctionContext function is called with this as its argument.

  • The ctx.setCurrentUser method is called with an object containing _id and name properties. This method is provided by the @magicjs.dev/backend library for setting the current user within the backend function's context.

  • The function returns the string "Message from server". This is the response that will be sent back to the client when the backend function is invoked.

Triggering the Function with the onClick Prop

Refer the below code to call this function in the frontend file "home.tsx".

home.tsx
import React from "react"
import { Button } from "antd"
import { useLogin } from "@magicjs.dev/frontend"
import loginServer from "./login.server"

export default function Component(props: any) {
    const { current } = useLogin()
    return (
        <div>
            <h1>{`Current User: ${current?.currentUser?.name}`}</h1>
            
            <Button onClick={() => loginServer('John')}>
                Login
            </Button>
        </div>
    )
}
Expand for Tailwind styled code.
import React from "react"
import { Button } from "antd"
import { useLogin } from "@magicjs.dev/frontend"
import loginServer from "./login.server"

export default function Component(props: any) {
    const { current } = useLogin()
    return (
        <div className="flex flex-col items-center justify-center h-screen">
            <h1 className="font-semi-bold text-4xl tracking-wide pb-8">
                {`Current User: ${current?.currentUser?.name}`}
            </h1>

            <Button onClick={() => loginServer("John")} className="text-xl w-40 h-14">
                Login
            </Button>
        </div>
    )
  • loginServer is imported from "./login.server", it is the backend function responsible for handling the login process.

  • When the button is clicked, it triggers an arrow function that calls the loginServer function with the name 'John'.

Implementing Logout Functionality

  1. Update the UI file "home.tsx" by adding a logout button.

  2. Add the logout function near to current, and invoke it directly in the onClick prob.

Refer the snippet below.

home.tsx
import React from "react"
import { Button } from "antd"
import { useLogin } from "@magicjs.dev/frontend"
import loginServer from "./login.server"

export default function Component(props: any) {
    const { current, logout } = useLogin()
    return (
        <div>
            <h1>{`Current User: ${current?.currentUser?.name}`}</h1>

            <Button onClick={() => loginServer('John')}>
                Login
            </Button>

            <Button onClick={() => logout()}>
                Logout
            </Button>
        </div>
    )
}
Expand for Tailwind styled code.
import React from "react"
import { Button } from "antd"
import { useLogin } from "@magicjs.dev/frontend"
import loginServer from "./login.server"

export default function Component(props: any) {
    const { current, logout } = useLogin()
    return (
        <div className="flex flex-col items-center justify-center h-screen">
            <h1 className="font-semi-bold text-4xl tracking-wide pb-8">
                {`Current User: ${current?.currentUser?.name}`}
            </h1>
            <div>
                <Button onClick={() => loginServer("John")} className="text-xl w-40 h-14">
                    Login
                </Button>
                <Button onClick={() => logout()} className="text-xl w-40 h-14">
                    Logout
                </Button>
            </div>
        </div>
    )
}

The logout functionality is seamlessly integrated into the application without the need for a separate backend function. This capability is provided by the useLogin hook imported from '@magicjs.dev/frontend', simplifying the implementation of authentication features.

Implementing Conditional Rendering

Let's make the Logout button invisible when a user is not logged in, by wrapping it within a conditional statement.

Refer the snippet below.

home.tsx
import React from "react"
import { Button } from "antd"
import { useLogin } from "@magicjs.dev/frontend"
import loginServer from "./login.server"

export default function Component(props: any) {
    const { current, logout } = useLogin()
    return (
        <div>

            <h1>{`Current User: ${current?.currentUser?.name}`}</h1>

            <Button onClick={() => loginServer('John')}>
                Login
            </Button>

            {
                current.isAuthenticated === true ? (
                    <Button onClick={() => logout()}>
                        Logout
                    </Button>
                ) : null
            }
        </div>
    )
}
Expand for Tailwind styled code.
import React from "react"
import { Button } from "antd"
import { useLogin } from "@magicjs.dev/frontend"
import loginServer from "./login.server"

export default function Component(props: any) {
    const { current, logout } = useLogin()
    return (
        <div className="flex flex-col items-center justify-center h-screen">
            <h1 className="font-semi-bold text-4xl tracking-wide pb-8">
                {`Current User: ${current?.currentUser?.name}`}
            </h1>

            <div>
                <Button onClick={() => loginServer("John")} className="text-xl w-40 h-14">
                    Login
                </Button>

                {
                    current.isAuthenticated === true ? (
                        <Button onClick={() => logout()} className="text-xl w-40 h-14">
                            Logout
                        </Button>
                    ) : null
                }
            </div>
        </div>
    )
}
  • The "Logout" button is conditionally rendered using (current.isAuthenticated === true ? ... : null). This ensures that the "Logout" button is only displayed when the user is authenticated or logged in.

Implementing Backend Authentication

In addition to the frontend authentication, we'll also explore backend authentication to further enhance the security of the application. This includes securing backend APIs and endpoints to ensure that only authenticated users can access protected resources.

  1. Create a backend file named "get-my-name" in the main-features directory.

  2. Create a conditional statement to check if the user is authenticated. If authenticated, return the current user's name; otherwise, return "User not signed in".

Refer the snippet below

get-my-name.server.tsx
import { createBackendFunction, useFunctionContext } from "@magicjs.dev/backend"

export default createBackendFunction(async function () {
    const ctx = useFunctionContext(this)

    if (ctx.isAuthenticated === false) {
        return "User not signed in"
    }

    return ctx.currentUser.name;
})

Triggering the Protected Backend

In the 'home.tsx' file, import getMyNameServer in the frontend and call the function using the button.

Refer the code below:

home.tsx
import React from "react"
import { Button } from "antd"
import { useLogin } from "@magicjs.dev/frontend"
import loginServer from "./login.server"
import getMyNameServer from "./get-my-name.server"

export default function Component(props: any) {
    const { current, logout } = useLogin()
    return (
        <div>

            <h1>{`Current User: ${current?.currentUser?.name}`}</h1>

            <Button onClick={() => loginServer('John')}>
                Login
            </Button>

            <Button onClick={() => getMyNameServer().then((name) => alert(name))}>
                Call Protected Backend
            </Button>

            {
                current.isAuthenticated === true ? (
                    <Button onClick={() => logout()}>
                        Logout
                    </Button>
                ) : null
            }
        </div>
    )
}
Expand for Tailwind styled code.
import React from "react"
import { Button } from "antd"
import { useLogin } from "@magicjs.dev/frontend"
import loginServer from "./login.server"
import getMyNameServer from "./get-my-name.server"

export default function Component(props: any) {
    const { current, logout } = useLogin()
    return (
        <div className="flex flex-col items-center justify-center h-screen">
            <h1 className="font-semi-bold text-4xl tracking-wide pb-8">
                {`Current User: ${current?.currentUser?.name}`}
            </h1>

            <div>
                <Button onClick={() => loginServer("John")} className="text-xl w-40 h-14">
                    Login
                </Button>

                <Button onClick={() => getMyNameServer().then((name) => alert(name))} className="text-xl w-120 h-14">
                    Call Protected Backend
                </Button>

                {
                    current.isAuthenticated === true ? (
                        <Button onClick={() => logout()} className="text-xl w-40 h-14">
                            Logout
                        </Button>
                    ) : null
                }
            </div>
        </div>
    )
}
  • The above update in the code renders a <Button> component labelled "Call Protected Backend".

  • When this button is clicked, it triggers an asynchronous operation to call a function named getMyNameServer().

  • Upon successfully resolving the promise returned by getMyNameServer(), the function alert() is invoked to display the response from getMyNameServer().

🎉 Congratulations! You've learned how to authenticate a user seamlessly in MERN.AI using the MagicJS framework.

A pre-developed magic feature for authentication is available in mern.ai. You can simply import the feature and integrate it to your project. Visit mern.ai for more information.


Last updated