Skip to main content

3 posts tagged with "docusaurus"

View All Tags

Simple Blog

ยท 7 min read
JaqStack Team
Development Team

Published: November 2025

As a developer who has worked with countless technology stacks over the years, I'm always on the lookout for combinations that offer the perfect balance of performance, developer experience, and modern best practices. Recently, I embarked on a journey to build a simple blog application using what I've come to call the JAQ Stack - Java, Angular, Helidon, and MongoDB. The results were impressive, and I'd like to share my experience with you.

Why the JAQ Stack?โ€‹

Before diving into the implementation details, let me explain why I chose this particular combination:

  • Java 21: With its modern features like virtual threads, pattern matching, and record classes, Java 21 brings enterprise-grade reliability with cutting-edge capabilities
  • Helidon MP 4.3.1: A lightweight, cloud-native MicroProfile implementation that starts fast and runs efficiently
  • Angular 20.0.0: The latest version of Angular brings improved performance, better developer experience, and enhanced type safety
  • MongoDB 3.9.1: A NoSQL database that's perfect for document-based applications like blogs

Together, these technologies form a stack that's both powerful and pragmatic.

Architecture Overviewโ€‹

The Simple Blog application follows a clean, layered architecture:

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Angular 20 Frontend (Port 4200) โ”‚
โ”‚ - Home Component โ”‚
โ”‚ - Blog Detail Component โ”‚
โ”‚ - Blog Service โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
โ”‚ HTTP/REST
โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Helidon MP Backend (Port 8080) โ”‚
โ”‚ - BlogResource (REST Endpoints) โ”‚
โ”‚ - BlogService (Business Logic) โ”‚
โ”‚ - DataService (Data Access) โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ MongoDB Database โ”‚
โ”‚ - Database: jaqstack โ”‚
โ”‚ - Collection: blogposts โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Backend Implementation with Helidon MPโ€‹

Setting Up Helidon MPโ€‹

Helidon MP makes it incredibly easy to create RESTful services. The setup is straightforward:

<parent>
<groupId>io.helidon.applications</groupId>
<artifactId>helidon-mp</artifactId>
<version>4.3.1</version>
</parent>

With just this parent POM, you get a complete MicroProfile implementation including:

  • JAX-RS for REST APIs
  • CDI for dependency injection
  • Config for configuration management
  • Health checks and metrics

Creating REST Endpointsโ€‹

The blog API is implemented using JAX-RS resources. Here's a simplified version of our BlogResource:

@Path("/blog")
@RequestScoped
public class BlogResource {

private final BlogService blogService;

@Inject
public BlogResource(BlogService blogService) {
this.blogService = blogService;
}

@GET
@Path("/posts")
public Response getAllBlogPosts() {
List<BlogPost> blogPosts = blogService.getAllBlogPosts();
return addCorsHeaders(Response.ok(blogPosts));
}

@POST
@Path("/post")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response createBlogPost(BlogPost blogPost) {
String message = blogService.addBlogPost(blogPost);
Message responseMessage = new Message(message);
return addCorsHeaders(Response.ok(responseMessage));
}
}

Handling CORSโ€‹

One challenge when building full-stack applications is Cross-Origin Resource Sharing (CORS). Since our Angular frontend runs on port 4200 and the backend on port 8080, we need to handle CORS properly.

I implemented a helper method to add CORS headers to all responses:

private Response addCorsHeaders(Response.ResponseBuilder response) {
return response
.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
.header("Access-Control-Allow-Headers", "Content-Type, Authorization")
.header("Access-Control-Max-Age", "3600")
.build();
}

Additionally, I added OPTIONS handlers for preflight requests, which are essential for browsers to make cross-origin requests.

MongoDB Integrationโ€‹

The MongoDB integration is clean and straightforward. Using the MongoDB Java Driver 3.9.1, we can easily perform CRUD operations:

@ApplicationScoped
public class DataService {

public List<BlogPost> getAllBlogPosts() {
MongoClient mongoClient = MongoClients.create();
MongoDatabase database = mongoClient.getDatabase("jaqstack");
MongoCollection<Document> collection = database.getCollection("blogposts");

List<Document> documents = collection.find().into(new ArrayList<>());
// Convert documents to BlogPost objects
// ...

mongoClient.close();
return blogPostsList;
}
}

The document-based nature of MongoDB makes it perfect for blog posts, where each post can have varying structures without schema migrations.

Frontend Implementation with Angular 20โ€‹

Why Angular 20?โ€‹

Angular 20 represents a significant leap forward in the framework's evolution. Key improvements include:

  • Better Performance: Improved change detection and rendering
  • Enhanced Type Safety: Stricter type checking and better IDE support
  • Modern JavaScript: Support for ES2022 features
  • Improved Developer Experience: Better error messages and debugging tools

Component Architectureโ€‹

The Angular application follows a simple but effective component structure:

app/
โ”œโ”€โ”€ app.component.* # Root component with footer
โ”œโ”€โ”€ home/ # Home page with blog list
โ”‚ โ”œโ”€โ”€ home.component.ts
โ”‚ โ”œโ”€โ”€ home.component.html
โ”‚ โ””โ”€โ”€ home.component.css
โ”œโ”€โ”€ blog-detail/ # Individual blog post view
โ”‚ โ”œโ”€โ”€ blog-detail.component.ts
โ”‚ โ”œโ”€โ”€ blog-detail.component.html
โ”‚ โ””โ”€โ”€ blog-detail.component.css
โ””โ”€โ”€ blog.service.ts # API communication service

Service Layerโ€‹

The BlogService handles all communication with the backend:

@Injectable({
providedIn: 'root'
})
export class BlogService {
private baseUrl: string = environment.BACKEND_URL + "/blog";

constructor(private http: HttpClient) { }

getAllBlogPosts(): Observable<any> {
return this.http.get(`${this.baseUrl}/posts`);
}

getBlogPostById(id: string): Observable<any> {
return this.http.get(`${this.baseUrl}/post/${id}`);
}

createBlogPost(blogPost: any): Observable<any> {
return this.http.post(`${this.baseUrl}/post`, blogPost);
}
}

Using RxJS Observables provides a reactive approach to handling asynchronous operations, making the code more maintainable and testable.

Routingโ€‹

Angular's router makes navigation seamless:

const appRoutes: Routes = [
{ path: '', component: HomeComponent },
{ path: 'blog/:id', component: BlogDetailComponent }
];

The route parameters allow us to easily pass blog post IDs to the detail component.

Development Experienceโ€‹

Hot Reloadingโ€‹

One of the best parts of this stack is the development experience. Both Angular and Helidon support hot reloading:

  • Angular: ng serve provides instant feedback on code changes
  • Helidon: With proper IDE configuration, you can debug and see changes immediately

Build Processโ€‹

The build process is streamlined:

  1. Backend: mvn package creates an executable JAR with all dependencies
  2. Frontend: ng build creates optimized production bundles
  3. Integration: Maven automatically copies the Angular build into the JAR

This means you can deploy a single JAR file that contains both the backend and frontend!

Performance Considerationsโ€‹

Backend Performanceโ€‹

Helidon MP is known for its excellent performance characteristics:

  • Fast Startup: The server starts in under 3 seconds
  • Low Memory Footprint: Minimal overhead compared to traditional application servers
  • High Throughput: Efficient handling of concurrent requests

Frontend Performanceโ€‹

Angular 20's improvements shine in production:

  • Tree Shaking: Unused code is eliminated during build
  • Lazy Loading: Components can be loaded on demand
  • AOT Compilation: Templates are compiled ahead of time for better performance

Lessons Learnedโ€‹

What Worked Wellโ€‹

  1. Helidon's Simplicity: The framework gets out of your way and lets you focus on business logic
  2. Angular's Type Safety: TypeScript caught many errors at compile time
  3. MongoDB's Flexibility: No schema migrations needed when adding new fields
  4. CORS Handling: Once properly configured, cross-origin requests work seamlessly

Challenges Overcomeโ€‹

  1. CORS Preflight Requests: Initially struggled with OPTIONS requests, but adding explicit handlers solved it
  2. Angular Version Compatibility: Upgraded from Angular 5 to Angular 20 for Node.js v22 compatibility
  3. Build Integration: Ensuring the Angular build is properly included in the JAR required careful Maven configuration

Best Practices Appliedโ€‹

  1. Separation of Concerns: Clear separation between presentation, business logic, and data access
  2. Dependency Injection: Using CDI in the backend and Angular's DI in the frontend
  3. Error Handling: Proper error responses and user-friendly error messages
  4. Code Organization: Logical package structure and component organization

Future Enhancementsโ€‹

While the current implementation is functional, there are several enhancements I'm considering:

  1. Authentication: Add user authentication and authorization
  2. Comments: Allow users to comment on blog posts
  3. Search: Implement full-text search for blog posts
  4. Categories/Tags: Add categorization and tagging functionality
  5. Rich Text Editor: Integrate a WYSIWYG editor for content creation
  6. Image Upload: Support for image uploads and management
  7. Pagination: Implement pagination for the blog post list
  8. Caching: Add Redis for caching frequently accessed posts

Conclusionโ€‹

Building the Simple Blog application with the JAQ Stack has been an enlightening experience. The combination of Java 21, Helidon MP, Angular 20, and MongoDB provides a modern, efficient, and developer-friendly stack for building web applications.

The stack offers:

  • Performance: Fast startup and efficient runtime
  • Developer Experience: Great tooling and hot reloading
  • Scalability: Can easily scale both horizontally and vertically
  • Maintainability: Clean architecture and separation of concerns

Whether you're building a simple blog or a complex enterprise application, the JAQ Stack provides a solid foundation that balances modern best practices with proven technologies.

Resourcesโ€‹


This blog post was written as part of the Simple Blog application demonstration. The complete source code is available in the repository.

Built with JAQ Stack - Java 21, Helidon MP 4.3.1, Angular 20.0.0, MongoDB 3.9.1

Referencesโ€‹

Basic Authentication

ยท 9 min read
JaqStack Team
Development Team

Published: November 2025

Authentication is one of the most critical aspects of any web application. In this blog post, I'll walk you through building a secure authentication system using the JAQ Stack - Java, Angular, Jersey, and MongoDB - with JSON Web Tokens (JWT) as the authentication mechanism.

Why JWT Authentication?โ€‹

Before diving into the implementation, let's understand why JWT is an excellent choice for modern web applications:

  • Stateless: No need to store session data on the server
  • Scalable: Works seamlessly across multiple servers
  • Secure: Tokens are cryptographically signed
  • Standard: Based on RFC 7519, widely supported
  • Flexible: Can contain custom claims and user information

Technology Stackโ€‹

Our authentication system is built with:

  • Java 8: Enterprise-grade reliability with proven stability
  • Jersey 2.26: JAX-RS reference implementation for RESTful services
  • Angular 5: Modern frontend framework with excellent tooling
  • MongoDB 3.9.1: NoSQL database perfect for user management
  • JJWT 0.10.5: Java library for creating and parsing JWTs
  • Weld 3.0.5: CDI implementation for dependency injection
  • Tomcat: Servlet container for WAR deployment

Architecture Overviewโ€‹

The authentication system follows a clean, layered architecture:

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Angular 5 Frontend (Port 4200) โ”‚
โ”‚ - Login Component โ”‚
โ”‚ - Registration Component โ”‚
โ”‚ - Auth Service โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
โ”‚ HTTP/REST + JWT
โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Jersey REST API (Port 8080) โ”‚
โ”‚ - Login Resource (/auth/login) โ”‚
โ”‚ - Register Resource (/auth/register)โ”‚
โ”‚ - User Resource (/auth/users) โ”‚
โ”‚ - JWT Token Service โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ MongoDB Database โ”‚
โ”‚ - Database: jaqstack โ”‚
โ”‚ - Collection: users โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Backend Implementationโ€‹

JWT Token Serviceโ€‹

The heart of our authentication system is the AuthenticationTokenService, which handles JWT creation:

@RequestScoped
public class AuthenticationTokenService implements Serializable {

public String issueToken(String username) {
Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);
String jws = Jwts.builder()
.setSubject(username)
.signWith(key)
.compact();
return jws;
}
}

Key Points:

  • Uses HS256 (HMAC-SHA256) algorithm for signing
  • Generates a secure random key for each token
  • Sets the username as the subject claim
  • Returns a compact JWT string

Authentication Resourceโ€‹

The Login resource class handles authentication endpoints:

@Path("/auth")
@RequestScoped
public class Login {

@Inject
AuthenticationTokenService authenticationTokenService;

@Inject
UserService userService;

@POST
@Path("/login")
@PermitAll
public Response authenticate(UserCredentials userCredentials) {
// Validate credentials against database
User user = userService.validateCredentials(userCredentials);

// Issue JWT token
String token = authenticationTokenService.issueToken(userCredentials.getUsername());

AuthenticationToken authenticationToken = new AuthenticationToken();
authenticationToken.setToken(token);

return Response.ok(authenticationToken).build();
}

@POST
@Path("/register")
public Response registerUser(User user) {
String message = userService.addUser(user);
return Response.ok(message).build();
}

@GET
@Path("/users")
public Response getAllUsers() {
List<User> users = userService.allUsers();
return Response.ok(users).build();
}
}

User Validationโ€‹

The UserService handles user validation logic:

@ApplicationScoped
public class UserService implements Serializable {

@Inject
DataService dataService;

public User validateCredentials(UserCredentials userCredentials) {
User user = findUser(userCredentials);

if (user == null) {
throw new AuthenticationException("Bad credentials.");
}

return user;
}

private User findUser(UserCredentials userCredentials) {
return dataService.findUsernamePassword(userCredentials);
}
}

Security Considerations:

  • Throws AuthenticationException for invalid credentials
  • Does not reveal whether username or password is incorrect (security best practice)
  • Uses CDI for dependency injection

MongoDB Integrationโ€‹

User data is stored in MongoDB using the DataService:

@ApplicationScoped
public class DataService implements Serializable {

public User findUsernamePassword(UserCredentials userCredentials) {
MongoClient mongoClient = MongoClients.create();
MongoDatabase database = mongoClient.getDatabase("jaqstack");
MongoCollection<Document> collection = database.getCollection("users");

BasicDBObject query = new BasicDBObject("username", userCredentials.getUsername())
.append("password", userCredentials.getPassword());

Document doc = collection.find(query).first();

if (doc == null) {
mongoClient.close();
return null;
}

User user = new User();
user.setUsername(doc.getString("username"));
user.setPassword(doc.getString("password"));
// ... map other fields

mongoClient.close();
return user;
}

public String addUser(User user) {
MongoClient mongoClient = MongoClients.create();
MongoDatabase database = mongoClient.getDatabase("jaqstack");
MongoCollection<Document> collection = database.getCollection("users");

Document doc = new Document("username", user.getUsername())
.append("password", user.getPassword())
.append("firstname", user.getFirstname())
.append("lastname", user.getLastname());

collection.insertOne(doc);
mongoClient.close();

return "User " + user.getFirstname() + " added successfully.";
}
}

Frontend Implementationโ€‹

Authentication Serviceโ€‹

The Angular frontend uses a service to handle authentication:

@Injectable()
export class AuthService {
private baseUrl: string = environment.BACKEND_URL + "/service/auth";

constructor(private http: HttpClient) { }

login(credentials: any): Observable<any> {
return this.http.post(`${this.baseUrl}/login`, credentials);
}

register(user: any): Observable<any> {
return this.http.post(`${this.baseUrl}/register`, user);
}

getUsers(): Observable<any> {
return this.http.get(`${this.baseUrl}/users`);
}
}

Token Storageโ€‹

After successful login, the JWT token should be stored securely:

login(credentials: any): void {
this.authService.login(credentials).subscribe(
(response) => {
// Store token in localStorage or sessionStorage
localStorage.setItem('authToken', response.token);
// Redirect to protected area
},
(error) => {
console.error('Login failed:', error);
}
);
}

Security Best Practicesโ€‹

1. Password Storageโ€‹

Important: In production, never store passwords in plain text! Always use password hashing:

// Production-ready approach (not implemented in this example)
import org.mindrot.jbcrypt.BCrypt;

public String hashPassword(String plainPassword) {
return BCrypt.hashpw(plainPassword, BCrypt.gensalt());
}

public boolean checkPassword(String plainPassword, String hashedPassword) {
return BCrypt.checkpw(plainPassword, hashedPassword);
}

2. JWT Secret Key Managementโ€‹

The current implementation generates a new key for each token. In production:

  • Use a fixed, secure secret key stored in configuration
  • Rotate keys periodically
  • Never commit keys to version control
// Production approach
@ApplicationScoped
public class AuthenticationTokenService {
private static final String SECRET_KEY = System.getenv("JWT_SECRET_KEY");
private static final Key key = Keys.hmacShaKeyFor(SECRET_KEY.getBytes());

public String issueToken(String username) {
return Jwts.builder()
.setSubject(username)
.setExpiration(new Date(System.currentTimeMillis() + 3600000)) // 1 hour
.signWith(key)
.compact();
}
}

3. Token Expirationโ€‹

Always set expiration times on tokens:

Date expiration = new Date(System.currentTimeMillis() + 3600000); // 1 hour
String jws = Jwts.builder()
.setSubject(username)
.setExpiration(expiration)
.signWith(key)
.compact();

4. HTTPS Onlyโ€‹

In production, always use HTTPS to prevent token interception:

  • Configure Tomcat for SSL/TLS
  • Use reverse proxy (nginx, Apache) with SSL certificates
  • Set secure cookie flags if storing tokens in cookies

CDI and Dependency Injectionโ€‹

The application uses CDI (Contexts and Dependency Injection) with Weld:

// beans.xml configuration
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
bean-discovery-mode="all">
</beans>

Benefits:

  • Loose coupling between components
  • Easy testing with mock objects
  • Lifecycle management (@ApplicationScoped, @RequestScoped)
  • Interceptor support for cross-cutting concerns

Deployment Architectureโ€‹

WAR Deploymentโ€‹

The application is packaged as a WAR file for deployment to Tomcat:

<packaging>war</packaging>

Build Process:

  1. Compile Java sources
  2. Build Angular frontend
  3. Copy Angular dist to WAR resources
  4. Package everything into basicauth.war

Tomcat Configurationโ€‹

The application uses Tomcat's servlet container with:

  • Jersey servlet for REST endpoints
  • Weld listener for CDI
  • CORS filter for cross-origin requests

API Endpointsโ€‹

Authentication Endpointsโ€‹

  1. POST /basicauth/service/auth/login

    • Authenticates user and returns JWT token
    • Request: {"username":"user", "password":"pass"}
    • Response: {"token":"eyJhbGciOiJIUzI1NiJ9..."}
  2. POST /basicauth/service/auth/register

    • Registers a new user
    • Request: {"username":"user", "password":"pass", "firstname":"John", "lastname":"Doe"}
    • Response: "User John added successfully."
  3. GET /basicauth/service/auth/users

    • Retrieves all users (should be protected in production)
    • Response: [{"username":"user1", ...}, {"username":"user2", ...}]

Frontend Integrationโ€‹

Login Flowโ€‹

  1. User enters credentials in Angular form
  2. Frontend sends POST request to /auth/login
  3. Backend validates credentials against MongoDB
  4. Backend generates JWT token
  5. Frontend stores token (localStorage/sessionStorage)
  6. Frontend includes token in subsequent requests

Protected Routesโ€‹

To protect routes in Angular, implement a route guard:

@Injectable()
export class AuthGuard implements CanActivate {
canActivate(): boolean {
const token = localStorage.getItem('authToken');
if (token) {
return true;
}
// Redirect to login
this.router.navigate(['/login']);
return false;
}
}

Testing the Authentication Systemโ€‹

Manual Testing with cURLโ€‹

Register a User:

curl -X POST http://localhost:8080/basicauth/service/auth/register \
-H "Content-Type: application/json" \
-d '{"username":"testuser","password":"test123","firstname":"Test","lastname":"User"}'

Login:

curl -X POST http://localhost:8080/basicauth/service/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"testuser","password":"test123"}'

Use Token in Requests:

curl -X GET http://localhost:8080/basicauth/service/auth/users \
-H "Authorization: Bearer YOUR_JWT_TOKEN_HERE"

Challenges and Solutionsโ€‹

Challenge 1: CORS Configurationโ€‹

Problem: Angular app on port 4200 couldn't access backend on port 8080.

Solution: Configured CORS filter in web.xml:

<filter>
<filter-name>CorsFilter</filter-name>
<filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
<init-param>
<param-name>cors.allowed.origins</param-name>
<param-value>*</param-value>
</init-param>
</filter>

Challenge 2: CDI Discoveryโ€‹

Problem: Weld wasn't discovering CDI beans.

Solution:

  • Added beans.xml with bean-discovery-mode="all"
  • Configured Weld listener in web.xml
  • Used proper CDI annotations (@ApplicationScoped, @RequestScoped)

Challenge 3: Jersey Auto-Discoveryโ€‹

Problem: REST endpoints weren't being discovered.

Solution:

  • Configured Jersey servlet in web.xml
  • Used package scanning for JAX-RS resources
  • Ensured proper annotations (@Path, @GET, @POST)

Production Considerationsโ€‹

1. Password Hashingโ€‹

Critical: Implement password hashing before production deployment:

  • Use BCrypt or Argon2
  • Never store plain text passwords
  • Implement password strength requirements

2. Token Validationโ€‹

Implement token validation middleware:

@Provider
public class JwtFilter implements ContainerRequestFilter {
@Override
public void filter(ContainerRequestContext requestContext) {
// Extract token from Authorization header
// Validate token signature and expiration
// Set user context
}
}

3. Rate Limitingโ€‹

Implement rate limiting to prevent brute force attacks:

  • Limit login attempts per IP
  • Implement exponential backoff
  • Use Redis for distributed rate limiting

4. Logging and Monitoringโ€‹

  • Log authentication attempts (success and failure)
  • Monitor for suspicious patterns
  • Set up alerts for multiple failed login attempts

Lessons Learnedโ€‹

What Worked Wellโ€‹

  1. JWT Simplicity: JWT tokens are easy to implement and understand
  2. CDI Integration: Dependency injection made the code clean and testable
  3. MongoDB Flexibility: Easy to add new user fields without migrations
  4. Jersey REST: Clean REST API implementation with JAX-RS

Areas for Improvementโ€‹

  1. Password Security: Need to implement password hashing
  2. Token Refresh: Should implement refresh token mechanism
  3. Role-Based Access: Add role-based authorization
  4. Token Revocation: Implement token blacklisting for logout

Future Enhancementsโ€‹

  1. OAuth2 Integration: Support for social login (Google, GitHub, etc.)
  2. Multi-Factor Authentication: Add 2FA support
  3. Password Reset: Implement password reset flow with email
  4. Account Lockout: Lock accounts after multiple failed attempts
  5. Session Management: Track active sessions and allow logout from all devices
  6. Audit Logging: Comprehensive audit trail for security events

Conclusionโ€‹

Building a JWT-based authentication system with the JAQ Stack has been a valuable learning experience. The combination of Java, Jersey, Angular, and MongoDB provides a solid foundation for secure authentication.

Key Takeaways:

  • JWT provides a stateless, scalable authentication mechanism
  • CDI makes the codebase maintainable and testable
  • MongoDB's flexibility is perfect for user management
  • Security must be considered at every layer

While this implementation provides a good starting point, remember that security is an ongoing process. Always:

  • Keep dependencies updated
  • Follow security best practices
  • Conduct regular security audits
  • Stay informed about new vulnerabilities

Resourcesโ€‹


This blog post was written as part of the Basic Authentication application demonstration. The complete source code is available in the repository.

Built with JAQ Stack - Java 8, Jersey 2.26, Angular 5, MongoDB 3.9.1, JWT (JJWT 0.10.5)

Referencesโ€‹