Building the Authentication Service for Gratia : A Cloud-Native Food Donation Platform


In this blog, I'd like to walk you through my process of developing the authentication microservice for Gratia : a food donation site that helps restaurant connect with needy NGOs. It's a personal project where I'm learning and implementing backend development and cloud-native concepts using Go, PostgreSQL, Docker, Kubernetes, and Terraform.
If you are a student or a newcomer interested in developing actual real-world backend services and hosting them in a cloud-like setup, this tale will provide a good idea of the process.
The Problem Gratia Solves
Waste food is a huge international problem, and restaurants tend to have leftover food that they cannot sell. Conversely, numerous NGOs have trouble finding sufficient food to give away to those who need it.
Gratia is my effort to fill this gap. Picture a restaurant that, at the end of the day, has 50 meals that haven't been sold. Instead of discarding them, the restaurant puts the excess food on Gratia. Local NGOs are able to view the posting, claim the donation, and collect it in a hurry ( all facilitated through the platform ).
This thought led to a need for a secure, trusted backend service to handle users, authenticate them, and regulate access. That's where the auth-service is involved.
Preparing the Environment — Kubernetes Without the Cost
I wanted my services to be deployed on Kubernetes, which is the de facto standard for running scalable apps in the modern era. But since I'm going solo and cost-sensitive, I sidestepped commercial managed services like AWS EKS.
Instead, I utilized k3d ( a light Kubernetes distribution that runs on top of Docker straight on my local machine ). It's free, simple to install, and ideal for development and testing.
In addition to k3d, I employed Terraform to provision infrastructure automatically and configure as code. Even if you're deploying locally, declaring your infrastructure makes good habits and ensures consistency.
Designing the Auth Service
The auth-service is written in Go, following clean architecture principles. I organized the code in a way that keeps responsibilities separated:
cmd/server/: The app entry point.
internal/config/: Loads environment variables and config.
internal/handlers/: HTTP handlers for routes.
internal/middleware/: Middleware for JWT authentication.
internal/models/: Data structures and database models.
internal/repository/: PostgreSQL data access layer.
internal/service/: Core business logic.
internal/utils/: Helpers for JWT and password hashing.
internal/docs/: OpenAPI spec skeleton.
Dockerfile: Multi-stage build for containerization.
.env: Local secrets and configs (excluded from version control).
This structure helped me write modular, maintainable code that’s easier to test and extend.
What the Service Does
The auth-service handles user registration, login, token generation, and session management. Here are the key features:
User registration: Users sign up with details like full name, email, phone number, password, and role (restaurant or NGO). Passwords are securely hashed before saving.
Login: Validates credentials and issues JWT access and refresh tokens.
Token refresh: Allows clients to get a new access token without logging in again.
Protected routes: Routes like
/me
(get user profile) and/logout
require a valid JWT, verified via middleware.Password reset placeholders: Endpoints for forgot and reset password are scaffolded for future implementation.
API documentation: An OpenAPI (Swagger) spec lives inside the project to describe all endpoints.
API Endpoints Explained
Here’s a detailed look at the main API endpoints the auth-service exposes, and how they work together to provide secure authentication and user management:
1. POST /register
This endpoint allows new users (restaurants or NGOs) to create accounts. The client sends user details like full name, email, phone number, password, and role.
The password is hashed securely before saving to the database.
If a user with the same email already exists, registration is rejected.
On success, the API returns a user ID and a success message.
This is the first step for anyone who wants to use the platform.
2. POST /login
Users provide their email and password to this endpoint to authenticate themselves.
The service verifies the password against the stored hash.
On success, it issues two tokens:
Access Token: A JWT valid for a short time (e.g., 15 minutes) used to authenticate requests.
Refresh Token: A longer-lived token used to obtain new access tokens without logging in again.
If credentials are invalid, the service returns an unauthorized error.
This approach enables stateless authentication, meaning the server doesn’t have to keep session data, improving scalability.
3. POST /refresh
Access tokens expire relatively quickly to minimize risk if compromised. Clients can call this endpoint with a valid refresh token to get a new access token without re-entering credentials.
The service validates the refresh token’s integrity and expiry.
If valid, it issues a fresh access token.
If invalid or expired, it rejects the request, requiring the user to log in again.
This mechanism keeps user sessions smooth and secure.
4. GET /me
This is a protected endpoint accessible only with a valid access token.
The service extracts the user ID from the token.
It fetches and returns the user’s profile details (excluding sensitive data like passwords).
This endpoint lets clients display current user info, like profile pages or dashboards.
5. POST /logout
Currently, this endpoint is a placeholder since JWT-based authentication is stateless, meaning logout is handled mostly client-side by deleting tokens.
In future, token blacklisting or session invalidation could be implemented here.
6. POST /forgot-password & POST /reset-password
These endpoints are stubs for now, intended to handle password recovery flows. I plan to implement them later to allow users to reset forgotten passwords securely.
Dockerizing and Running the Service
I containerized the service with a multi-stage Docker build to produce a small, efficient image:
# Build Stage
FROM golang:1.24 AS builder
WORKDIR /app
COPY . .
RUN go build -o auth-service ./cmd/server/main.go
# Final Stage
FROM alpine:latest
COPY --from=builder /app/auth-service .
EXPOSE 8081
CMD ["./auth-service"]
This container runs the Go binary on Alpine Linux, making it lightweight and suitable for Kubernetes deployment , which in my case is the local k3d cluster.
Testing the API
I created a Postman collection that groups all the endpoints to simulate real-world use:
Public endpoints:
/register
,/login
,/refresh
,/forgot-password
,/reset-password
.Protected endpoints:
/me
,/logout
.
For protected routes, Postman automatically inserts the JWT token into the Authorization
header after login, mimicking how clients securely communicate with the backend.
What I Learned Along the Way
Building this service gave me practical experience in:
Writing clean, modular Go code that’s easy to maintain.
Managing user authentication securely using JWT tokens.
Structuring a microservice with clear separation of concerns.
Containerizing applications using Docker.
Deploying and managing apps on Kubernetes — even locally with k3d.
Using Terraform to write infrastructure as code.
Testing REST APIs effectively with Postman.
What’s Next for Gratia?
The auth-service is just the start. I’m planning to build other microservices for managing food donations, messaging between restaurants and NGOs, notifications, and more.
Each service will be designed similarly, leveraging Go, Docker, Kubernetes, and Terraform — building towards a fully cloud-native and scalable food donation platform.
I’ll share updates and deep dives on these services in future posts, so stay tuned if you’re interested!
Final Thoughts
This project combines backend development with DevOps practices, teaching me how to develop real-world, scalable applications. If you're learning Go, Kubernetes, or microservices, I hope this tale inspires you to begin your own project and get hands-on experience with these technologies.
I invite you to contact me if you wish to discuss, give feedback, or collaborate.
Github : github.com/AdityaWaradkar
Subscribe to my newsletter
Read articles from Aditya Waradkar directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
