Building a Modern Authentication System with Supabase, Ballerina, and Next.js
Kavishka Dinajara
Posted on October 10, 2024
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
- Supabase: An open-source Firebase alternative that provides a backend-as-a-service, including authentication, database management, and real-time functionalities.
- Ballerina: A programming language specifically designed for cloud-native application development, making it perfect for handling API integrations and microservices.
- 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);
}
}
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" });
}
}
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)
}
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;
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!
Posted on October 10, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 29, 2024