Skip to main content

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​

JAQ Stack is Now a Docker-Sponsored Open Source Project

Β· 2 min read
Suren K
Contributor

We’re thrilled to announce that JAQ Stack has been officially accepted into the Docker-Sponsored Open Source (DSOS) Program!
This partnership recognizes JAQ Stack as a valuable open-source initiative supporting developers worldwide.


πŸŽ‰ What This Means​

As part of the program, JAQ Stack now benefits from:

  • βœ… Free Docker Team Subscription for the organization
  • 🧩 Docker Scout access for image insights and supply-chain security
  • βš™οΈ Free autobuilds for automatic image publishing
  • πŸš€ Rate-limit removal for all users pulling JAQ Stack public images
  • 🏷️ Official open-source badging on Docker Hub (visible within two weeks)

All official JAQ Stack images can be found here:
πŸ‘‰ hub.docker.com/u/jaqstack

For ready-to-run examples:
πŸ“¦ jaqstack/jaqstack-examples


πŸ’‘ Why It Matters​

This sponsorship allows us to:

  • Keep our Helidon / Angular / SQL-NoSQL containers freely available
  • Deliver faster, more reliable builds for the community
  • Enhance security and transparency through Docker Scout integration
  • Maintain open, rate-limit-free access for developers everywhere

It’s another step toward our mission β€” making full-stack development faster, simpler, and open for everyone.


🧭 Our Commitment​

JAQ Stack will continue to embody its founding principles:

  • πŸ†“ Open Source β€” MIT-licensed and freely available
  • 🀝 Community-Driven β€” guided by your ideas and contributions
  • πŸ” Transparent Development β€” open discussions, roadmap, and feedback

With Docker’s support, we’re even better positioned to maintain consistent, reliable builds and grow our open-source ecosystem.


πŸ™Œ Thank You​

This milestone wouldn’t have been possible without the community β€” contributors, testers, and supporters who’ve believed in JAQ Stack since its early days.
Your feedback and collaboration keep this project evolving.

If you’d like to get involved:


🐳 About the Docker-Sponsored Open Source Program​

The Docker-Sponsored Open Source Program supports projects that:

  • Are public and open source under OSI-approved licenses
  • Are actively maintained
  • Have no commercial pathway (donations allowed)
  • Benefit the wider developer community

Membership provides Docker Pro/Team features, rate-limit removal, and recognition on Docker Hub.
JAQ Stack’s inclusion highlights its growing impact in the open-source ecosystem.


⏱️ Build Fast. Scale Smart.
Proudly part of the Docker-Sponsored Open Source Program.


JAQ Stack is Now a GitLab Supported Open Source Project 🦊

Β· 2 min read
Suren K
Contributor

We’re proud to share that JAQ Stack has been officially approved as part of the GitLab Supported Open Source Program.
This recognition reinforces our commitment to open, transparent, and community-driven software development.


πŸŽ‰ What This Means​

As part of the program, JAQ Stack now receives free access to GitLab’s Ultimate Plan, which includes:

  • βœ… GitLab Ultimate features for advanced DevSecOps capabilities
  • 🧩 Unlimited private & public repositories under our organization
  • 🧠 Integrated CI/CD pipelines for automated testing and builds
  • πŸ” Built-in security, compliance, and vulnerability scanning
  • πŸ“ˆ Project analytics and insights for better collaboration

JAQ Stack is also officially listed on GitLab’s open-source directory, showcasing our contribution to the broader developer ecosystem:

πŸ‘‰ View JAQ Stack on GitLab


πŸ’‘ Why It Matters​

GitLab’s sponsorship strengthens JAQ Stack’s open-source mission by:

  • Enabling faster continuous integration across our examples and templates
  • Improving code quality and security with GitLab’s built-in analysis tools
  • Supporting a scalable DevSecOps workflow for contributors and maintainers
  • Ensuring visibility and validation within GitLab’s open-source ecosystem

Together with our existing Docker-Sponsored Open Source support, this expands JAQ Stack’s open infrastructure for the community.


🧭 Our Commitment​

JAQ Stack continues to stand for:

  • πŸ†“ Open Source MIT-licensed and freely available
  • 🀝 Community-Driven guided by your feedback and collaboration
  • πŸ” Transparent Development every step visible, every decision open

GitLab’s support ensures we can maintain a secure, collaborative environment for developers building on top of JAQ Stack.


πŸ™Œ Thank You​

Huge thanks to GitLab’s Open Source Program team for recognizing JAQ Stack and for supporting open collaboration across the software ecosystem.
We also want to thank our contributors β€” your efforts make programs like these possible.

If you’d like to get involved:


🦊 About the GitLab Supported Open Source Program​

The GitLab Supported Open Source Program provides free access to GitLab Ultimate for projects that:

  • Are publicly available and open source under OSI-approved licenses
  • Are actively maintained and regularly updated
  • Provide transparent contribution guidelines
  • Do not operate for commercial profit

The program empowers open-source maintainers by giving them enterprise-grade DevSecOps capabilities to improve quality, security, and collaboration.


⏱️ Build Fast. Scale Smart.
Proudly part of the GitLab Supported Open Source Program.