DEV Community

Cover image for Building a Modern Authentication System with Supabase, Ballerina, and Next.js
Kavishka Dinajara
Kavishka Dinajara

Posted on

Building a Modern Authentication System with Supabase, Ballerina, and Next.js

In this article, I'll share my journey of integrating Supabase with Ballerina as a backend and Next.js as the frontend framework to create a modern authentication system. This approach not only allows for a streamlined user experience but also leverages the power of JWT (JSON Web Tokens) for secure authentication.

Overview of the Tech Stack

  1. Supabase: An open-source Firebase alternative that provides a backend-as-a-service, including authentication, database management, and real-time functionalities.
  2. Ballerina: A programming language specifically designed for cloud-native application development, making it perfect for handling API integrations and microservices.
  3. Next.js: A React-based framework that enables server-side rendering and static site generation, providing an excellent user experience.

Setting Up the Project

1. Initializing Supabase

I started by creating a Supabase project and setting up the database. I defined a users table with fields for the username and password. The password would be stored securely as a hash.

2. Connecting Ballerina to Supabase

In Ballerina, I created a service to handle user registration and login. Here’s a snippet of the code to establish a connection to the Supabase PostgreSQL database:

import ballerina/http;
import ballerinax/postgresql;

// Supabase DB connection config
configurable string host = "your_host";
configurable int port = 5432;
configurable string username = "your_username";
configurable string password = "your_password";
configurable string databaseName = "your_database";

service /auth on new http:Listener(9090) {
    final postgresql:Client dbClient;

    public function init() returns error? {
        self.dbClient = check new (host, username, password, databaseName, port);
    }
}
Enter fullscreen mode Exit fullscreen mode

3. User Registration and Hashing Passwords

For user registration, I utilized the crypto module in Ballerina to hash the passwords securely. Here's how I handled user registration:

resource function post register(http:Caller caller, http:Request req) returns error? {
    json payload = check req.getJsonPayload();
    string username = (check payload.username).toString();
    string plainPassword = (check payload.password).toString();

    // Hash the password
    byte[] hashedPassword = check crypto:hashSha256(plainPassword.toBytes());

    // Insert the user into the database
    sql:ExecutionResult result = check self.dbClient->execute(
        `INSERT INTO users (username, password) VALUES (${username}, ${hashedPassword.toBase16String()})`
    );

    if result.affectedRowCount == 1 {
        check caller->respond({ message: "Registration successful" });
    } else {
        check caller->respond({ message: "Registration failed" });
    }
}
Enter fullscreen mode Exit fullscreen mode

4. User Login and JWT Generation

For the login functionality, I queried the database to retrieve the hashed password, compared it with the entered password, and generated a JWT token upon successful authentication:

resource function post login(http:Caller caller, http:Request req) returns error {
    json payload = check req.getJsonPayload();
    string username = (check payload.username).toString();
    string plainPassword = (check payload.password).toString();

    // Retrieve the user from the database
    stream<record {|anydata...;|}, sql:Error?> resultStream = self.dbClient->query(
        `SELECT password FROM users WHERE username = ${username}`
    );

    // Verify the password and generate JWT token
    // (Code to validate the password and generate JWT)
}
Enter fullscreen mode Exit fullscreen mode

5. Implementing the Frontend with Next.js

On the frontend, I built a registration page using React and styled it with Tailwind CSS. The form captures the username and password, sending the data to the Ballerina backend for processing.

Here’s a snippet of the registration form component:

import React, { useState } from 'react';

const RegisterPage = () => {
  const [username, setUsername] = useState("");
  const [password, setPassword] = useState("");

  const handleSubmit = async (event) => {
    event.preventDefault();
    const response = await fetch('/auth/register', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ username, password }),
    });
    const data = await response.json();
    // Handle response...
  };

  return (
    <form onSubmit={handleSubmit}>
      <input type="text" value={username} onChange={(e) => setUsername(e.target.value)} placeholder="Username" />
      <input type="password" value={password} onChange={(e) => setPassword(e.target.value)} placeholder="Password" />
      <button type="submit">Register</button>
    </form>
  );
};

export default RegisterPage;
Enter fullscreen mode Exit fullscreen mode

Conclusion

This project has taught me the intricacies of handling authentication securely and effectively using modern technologies. Integrating Supabase with Ballerina and Next.js has provided me with a robust solution for user management.

I hope this article inspires you to explore similar integrations and improve your skills in building modern web applications. If you have any questions or feedback, feel free to reach out!


Image description

Top comments (0)