Official competition entry for WSO2 Ballerina Competition 2025 β a nationally recognized developer challenge organized by WSO2, one of the world's leading open-source integration software companies. TabWallet is a secure, encrypted link-management platform with role-based access control, dark mode, and real-time admin tooling β built with cutting-edge cloud-native technologies.
- Competition Context
- Project Overview
- System Architecture
- UI & Screenshots
- Key Features
- Security Implementation
- Tech Stack
- Project Structure
- Getting Started
- API Reference
- What I Learned
| Event | Organizer | Year |
|---|---|---|
| WSO2 Ballerina Competition 2025 | WSO2 Inc. (Global Open-Source Integration Leader) | 2025 |
TabWallet was built as an official entry for the WSO2 Ballerina Competition 2025 β a developer challenge that tests the ability to build secure, production-grade cloud-native applications using Ballerina, WSO2's modern programming language purpose-built for network-distributed services.
What made this technically challenging: Ballerina is not a mainstream language. Picking it up, designing a secure multi-role API system, and shipping a full-stack application with Angular within the competition window demonstrates the ability to rapidly learn and apply unfamiliar, industry-relevant technologies.
TabWallet is a full-stack link-management platform that enables users to securely store, organize, and manage personal or professional links in categorized collections β with encrypted storage, JWT-protected APIs, and a role-aware admin dashboard.
| Role | Capabilities |
|---|---|
| User | Register, verify email, manage links & categories, update profile |
| Admin | Full platform visibility β manage all users, links, categories, and system health |
Core Problem Solved: Bookmarks are scattered across browsers, devices, and platforms. TabWallet centralizes link management with security-first architecture β links are encrypted at rest, access is token-gated, and every operation is role-validated.
Full system architecture designed and documented as part of the engineering process β illustrating the complete request lifecycle from the Angular SPA through Ballerina's API layer, encryption engine, and down to the MongoDB data layer.
The system is structured across three distinct layers, each with clearly defined responsibilities and security boundaries:
β Client Layer β Angular 17 SPA (TypeScript)
The frontend is a Single Page Application built with Angular 17 and TypeScript. Navigation is protected at the routing layer β Angular Route Guards intercept every route change, validate the stored JWT token, and redirect unauthenticated or unauthorised users before any page renders and before any API call is even made. This is security enforced at the UI routing level, not just hidden buttons. The HTTP Service Layer handles all communication with the Ballerina backend via Angular's HttpClient, with an HTTP interceptor that automatically attaches the Authorization: Bearer <token> header on every outbound request. The Admin Route Guard performs an additional role claim check β only tokens carrying the Admin role claim are permitted to load the Admin Dashboard. The UI includes a custom Angular Material SCSS theme supporting full dark mode, a monthly bar chart for analytics, and a responsive filter and search experience.
β‘ Backend Layer β Ballerina Cloud-Native API Server
The backend is built entirely in Ballerina β WSO2's purpose-built, cloud-native programming language designed specifically for networked services. The server listens on port 9090 and is modularized into three focused files: Auth.bal owns all authentication logic (registration, login, email verification, password hashing, and JWT issuance), home.bal owns all user-facing operations (CRUD on links and categories), and Admin.bal owns all admin-level operations with an additional role gate. Every request to a protected resource passes through the JWT Validator which verifies the token signature, checks the exp expiry claim, and extracts the role. The RBAC Enforcer then gates access β a User token is structurally incapable of reaching an Admin endpoint regardless of what the frontend does. Before any data reaches MongoDB, it passes through the Encryption Layer β an AES encryption engine that encrypts link URLs and category data with a server-managed key before writing ciphertext to the database, and decrypts transparently on read.
β’ Data Layer β MongoDB
MongoDB stores four collections. The Users collection stores only hashed passwords β never plaintext. The Links and Categories collections store AES-encrypted ciphertext β raw database access reveals nothing meaningful about user data. The Verification Codes collection holds time-bound email codes that are invalidated after a single use.
External Services
Gmail SMTP handles outbound verification emails during account registration. The verification code is generated server-side, stored temporarily, and invalidated once confirmed β ensuring one-time use only.
| Decision | Rationale |
|---|---|
| Ballerina over Node/Spring | Purpose-built for networked APIs; native JWT, HTTP, and JSON primitives reduce boilerplate and shrink the attack surface |
| Route Guards at Angular router level | Auth enforcement happens before rendering β prevents flash-of-unauthorized-content and enforces least-privilege UI |
| Field-level AES encryption | Password hashing alone is insufficient β link data is personally sensitive; encrypting at the application layer means DB-level breaches expose only ciphertext |
| Modular Ballerina files | Auth.bal, Admin.bal, home.bal are bounded contexts β each owns its routes, authorization logic, and business rules independently |
| MongoDB document model | Flexible schema accommodates per-user variable link/category structures without schema migrations or relational joins |
Built with Angular + custom SCSS theming β includes full dark mode support.
- Secure registration with SMTP Gmail email verification β inactive accounts cannot access the system
- JWT-based authentication β stateless, scalable, and industry-standard
- Role-Based Access Control (RBAC) β User and Admin roles enforced at both the API level and the Angular routing layer
- Add, edit, and delete personal or professional links
- Organize links into categories for structured, searchable collections
- All link and category data is AES-encrypted at rest β even database-level access doesn't expose raw user data
- Full CRUD operations with sanitized inputs and safe database query patterns
- Update username and password securely at any time
- Password changes re-validated against security policies before update
- Full dark mode support with custom Angular Material SCSS theme
- Responsive, mobile-aware layout
- Monthly bar chart analytics for link activity visualization
- Complete visibility into all users, links, and categories across the platform
- Admin-level CRUD β create, edit, delete any resource
- User monitoring and management tooling
Security is the cornerstone of TabWallet. Every layer of the stack has deliberate, independent security controls β this is defense in depth, not security theatre.
User passwords are never stored in plaintext. Before persisting to MongoDB, passwords are run through a cryptographic hashing algorithm with salt β meaning even a full database breach exposes no usable credentials.
Registration: plainPassword β hash(password + salt) β store hash only
Login: inputPassword β hash(input + storedSalt) β compare with stored hash
Match β JWT issued | No Match β 401 Unauthorized
Unlike most link managers that store URLs as plaintext, TabWallet encrypts all link and category data at rest using AES encryption. This means even if someone gains direct database access, they see only ciphertext.
User adds link β Encrypt(linkData, serverKey) β Store ciphertext in MongoDB
User reads link β Fetch ciphertext from MongoDB β Decrypt(ciphertext, serverKey) β Return plaintext to client
This goes significantly beyond what most junior developers implement β field-level encryption is a production-grade security pattern used in financial and healthcare systems.
All protected routes require a valid, unexpired JWT signed with the server's secret key.
POST /auth/login β JWT issued: { userId, role, iat, exp }
Signed with HMAC-SHA256 (secret never leaves backend)
Protected request:
Authorization: Bearer <token>
β Ballerina middleware validates HMAC signature
β Checks exp claim β rejects stale tokens with 401
β Extracts role claim β passed to RBAC enforcer
Token Expiration ensures compromised tokens have a limited validity window β short-lived tokens minimize the blast radius of any credential leak.
Ballerina resource functions enforce role checks server-side. Admin endpoints are structurally inaccessible to User-role tokens β enforced in the API, independent of what the frontend does.
// Only Admin role tokens can reach this resource
resource function get admin/users(http:Caller caller, http:Request req) returns error? {
// Role extracted from JWT claims and validated before any logic executes
}Beyond API-level RBAC, Angular Route Guards provide a second independent enforcement layer at the UI routing level:
// Redirects to login before the component even renders
canActivate(): boolean {
if (!this.authService.isAuthenticated()) {
this.router.navigate(['/login']);
return false;
}
return true;
}All user inputs are validated and sanitized before reaching the database layer β protecting against injection attacks and malformed data corrupting the system.
| Layer | Technology | Purpose |
|---|---|---|
| Frontend Framework | Angular 17 | SPA with component-based UI architecture |
| Frontend Language | TypeScript | Type-safe client-side code |
| Styling | SCSS + Angular Material | Custom theming, dark mode, responsive layout |
| Backend Language | Ballerina (WSO2) | Cloud-native, network-centric API server |
| API Style | RESTful HTTP | Stateless resource-oriented endpoints |
| Database | MongoDB | Encrypted document storage |
| Data Encryption | AES (field-level) | Link & category encryption at rest |
| Authentication | JWT (HMAC-SHA256) | Stateless token-based auth |
| Email Verification | SMTP (Gmail) | Account activation flow |
| Dev Tools | VS Code, Postman, Git/GitHub | Development, API testing, version control |
TabWallet/
βββ README.md
βββ architecture-tabwallet.svg # System architecture diagram
βββ angular.json # Angular workspace config
βββ package.json
βββ tsconfig.json
β
βββ frontend/ # Angular SPA
β βββ src/
β βββ app/
β β βββ guard/ # Route guards (JWT + role auth checks)
β β βββ service/ # HTTP service layer (API calls)
β β βββ model/ # TypeScript interfaces/models
β β βββ home/ # Main dashboard view
β β βββ landingpage/ # Public landing page
β β βββ panel/ # Link/category management panel
β β βββ profile/ # User profile management
β β βββ user-list/ # Admin user management view
β β βββ filter-bar/ # Search & filter UI
β β βββ search-bar/ # Search component
β β βββ monthly-bar-chart/ # Analytics chart component
β β βββ shared/ # Shared components & utilities
β βββ environments/ # Environment config (dev/prod)
β βββ custom-theme.scss # Angular Material dark/light theme
β βββ styles.css
β
βββ backend/ # Ballerina API Server
βββ main.bal # Entry point & server bootstrap
βββ Auth.bal # Authentication routes & JWT logic
βββ Admin.bal # Admin-only endpoints (RBAC enforced)
βββ home.bal # User dashboard endpoints
βββ db_config.bal # MongoDB connection configuration
βββ Ballerina.toml # Project metadata
βββ config.toml # Environment configuration
βββ Dependencies.toml # Dependency lock file
| Tool | Version |
|---|---|
| Ballerina | Swan Lake 2201.x+ |
| Node.js | 18+ |
| Angular CLI | 17+ |
| MongoDB | 6.0+ (local or Atlas) |
git clone https://github.com/your-username/TabWallet.git
cd TabWalletEdit backend/config.toml:
[database]
connectionString = "mongodb://localhost:27017"
databaseName = "TabWallet"
[jwt]
secret = "your-secret-key-minimum-32-characters"
expiryInSeconds = 86400
[smtp]
host = "smtp.gmail.com"
port = 587
username = "your-email@gmail.com"
password = "your-app-password"cd backend
bal runβ Backend API starts on:
http://localhost:9090Ensure MongoDB is running and reachable before starting.
cd frontend
npm install
ng serveπ Application available at:
http://localhost:4200
| Method | Endpoint | Role | Description |
|---|---|---|---|
POST |
/auth/register |
Public | Create new account |
POST |
/auth/login |
Public | Login, receive JWT |
POST |
/auth/verify |
Public | Email verification |
GET |
/home/links |
User | Get user's links (decrypted) |
POST |
/home/links |
User | Add new link (AES-encrypted at rest) |
PUT |
/home/links/{id} |
User | Update link |
DELETE |
/home/links/{id} |
User | Delete link |
GET |
/home/categories |
User | Get user's categories |
POST |
/home/categories |
User | Create category |
PUT |
/profile/username |
User | Update username |
PUT |
/profile/password |
User | Change password |
GET |
/admin/users |
Admin | List all users |
GET |
/admin/links |
Admin | View all links |
DELETE |
/admin/users/{id} |
Admin | Remove a user |
All protected endpoints require: Authorization: Bearer <JWT_TOKEN>
Building TabWallet stretched my technical boundaries in deliberate ways:
- Ballerina as a new paradigm β Learning a purpose-built network language from scratch under competition pressure taught me that strong fundamentals (HTTP, REST, security) transfer across languages faster than expected
- Encryption beyond authentication β Most tutorials stop at hashing passwords. Implementing field-level AES encryption for link data required understanding encryption primitives, key management, and the performance trade-offs of encrypting at the application layer
- Angular Route Guards in production patterns β Building auth guards that intercept routing decisions (not just hide buttons) showed the real difference between security theatre and actual access control
- Defense in depth β Designing a system where the database, API, and UI all independently enforce the same security rules means no single layer failure compromises the whole system
- Separation of concerns in Ballerina β Modularizing into
Auth.bal,Admin.bal,home.balmirrors the microservice mindset β each file is a bounded context that owns its routes, logic, and authorization policy independently - System architecture documentation β Producing a full architecture diagram alongside the codebase helped communicate design decisions clearly and will serve as a reference for any future contributor
Developed for the WSO2 Ballerina Competition 2025 A nationally recognized developer challenge by WSO2 β a global leader in open-source integration and API management.
"Built with a language most developers haven't used β because learning under pressure is how real engineers grow."







