Vert.x Auth is a comprehensive authentication and authorization framework built for the Vert.x toolkit, providing asynchronous, non-blocking security solutions for reactive applications.
Vert.x Auth Overview
Vert.x Auth provides:
- Multiple authentication providers (JWT, OAuth2, Shiro, JDBC, MongoDB)
- Asynchronous, non-blocking operations
- Role-Based Access Control (RBAC)
- Permission-based authorization
- Token-based authentication
- Integration with Vert.x Web
Dependencies and Setup
Maven Configuration:
<dependencies> <!-- Vert.x Core --> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-core</artifactId> <version>4.4.6</version> </dependency> <!-- Vert.x Web --> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-web</artifactId> <version>4.4.6</version> </dependency> <!-- Vert.x Auth --> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-auth-common</artifactId> <version>4.4.6</version> </dependency> <!-- JWT Auth --> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-auth-jwt</artifactId> <version>4.4.6</version> </dependency> <!-- OAuth2 --> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-auth-oauth2</artifactId> <version>4.4.6</version> </dependency> <!-- JDBC Auth --> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-auth-jdbc</artifactId> <version>4.4.6</version> </dependency> <!-- MongoDB Auth --> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-auth-mongo</artifactId> <version>4.4.6</version> </dependency> <!-- PostgreSQL Client --> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-pg-client</artifactId> <version>4.4.6</version> </dependency> <!-- Config --> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-config</artifactId> <version>4.4.6</version> </dependency> </dependencies>
Configuration Management
AuthConfig.java:
public class AuthConfig { private final JsonObject config; public AuthConfig() { this.config = loadConfig(); } private JsonObject loadConfig() { return new JsonObject() .put("http", new JsonObject() .put("port", 8080) .put("host", "localhost")) .put("auth", new JsonObject() .put("jwt", new JsonObject() .put("secret", "your-jwt-secret-key-here") .put("algorithm", "HS256") .put("expiresInMinutes", 60)) .put("oauth2", new JsonObject() .put("clientId", "your-client-id") .put("clientSecret", "your-client-secret") .put("site", "https://oauth-provider.com")) .put("database", new JsonObject() .put("url", "jdbc:postgresql://localhost:5432/authdb") .put("user", "auth_user") .put("password", "auth_password") .put("driver_class", "org.postgresql.Driver")) .put("mongodb", new JsonObject() .put("connection_string", "mongodb://localhost:27017") .put("db_name", "authdb"))); } public JsonObject getJwtConfig() { return config.getJsonObject("auth").getJsonObject("jwt"); } public JsonObject getOAuth2Config() { return config.getJsonObject("auth").getJsonObject("oauth2"); } public JsonObject getDatabaseConfig() { return config.getJsonObject("auth").getJsonObject("database"); } public JsonObject getMongoConfig() { return config.getJsonObject("auth").getJsonObject("mongodb"); } public int getHttpPort() { return config.getJsonObject("http").getInteger("port"); } public String getHttpHost() { return config.getJsonObject("http").getString("host"); } } JWT Authentication Provider
JwtAuthProvider.java:
public class JwtAuthProvider { private final JWTAuth jwtAuth; private final AuthConfig config; public JwtAuthProvider(Vertx vertx, AuthConfig config) { this.config = config; this.jwtAuth = createJwtAuth(vertx); } private JWTAuth createJwtAuth(Vertx vertx) { JsonObject jwtConfig = new JsonObject() .put("keyStore", new JsonObject() .put("type", "secret") .put("secret", config.getJwtConfig().getString("secret"))); return JWTAuth.create(vertx, jwtConfig); } public JWTAuth getProvider() { return jwtAuth; } public String generateToken(User user, List<String> permissions, List<String> roles) { JsonObject claims = new JsonObject() .put("sub", user.getUsername()) .put("iss", "vertx-auth-service") .put("exp", System.currentTimeMillis() / 1000 + config.getJwtConfig().getInteger("expiresInMinutes") * 60) .put("iat", System.currentTimeMillis() / 1000) .put("permissions", new JsonArray(permissions)) .put("roles", new JsonArray(roles)) .put("userId", user.getId()) .put("email", user.getEmail()); return jwtAuth.generateToken(claims); } public Future<User> authenticate(String token) { Promise<User> promise = Promise.promise(); jwtAuth.authenticate(new JsonObject().put("jwt", token)) .onSuccess(user -> { // Convert to application User object User appUser = convertToAppUser(user); promise.complete(appUser); }) .onFailure(promise::fail); return promise.future(); } private User convertToAppUser(io.vertx.ext.auth.User authUser) { JsonObject principal = authUser.principal(); return new User( principal.getString("userId"), principal.getString("sub"), principal.getString("email"), principal.getJsonArray("permissions").getList(), principal.getJsonArray("roles").getList() ); } public Future<Boolean> authorize(io.vertx.ext.auth.User user, String permission) { Promise<Boolean> promise = Promise.promise(); user.isAuthorized(permission) .onSuccess(promise::complete) .onFailure(promise::fail); return promise.future(); } public Future<Boolean> hasRole(io.vertx.ext.auth.User user, String role) { Promise<Boolean> promise = Promise.promise(); user.isAuthorized("role:" + role) .onSuccess(promise::complete) .onFailure(promise::fail); return promise.future(); } } OAuth2 Authentication Provider
OAuth2AuthProvider.java:
public class OAuth2AuthProvider { private final OAuth2Auth oauth2; private final AuthConfig config; public OAuth2AuthProvider(Vertx vertx, AuthConfig config) { this.config = config; this.oauth2 = createOAuth2Provider(vertx); } private OAuth2Auth createOAuth2Provider(Vertx vertx) { OAuth2Options options = new OAuth2Options() .setClientId(config.getOAuth2Config().getString("clientId")) .setClientSecret(config.getOAuth2Config().getString("clientSecret")) .setSite(config.getOAuth2Config().getString("site")) .setTokenPath("/oauth/token") .setAuthorizationPath("/oauth/authorize") .setUserInfoPath("/userinfo") .setScope("profile email"); return OAuth2Auth.create(vertx, options); } public String getAuthorizationUrl(String redirectUri, String state) { return oauth2.authorizeURL(new JsonObject() .put("redirect_uri", redirectUri) .put("scope", "profile email") .put("state", state)); } public Future<OAuth2Token> getToken(String code, String redirectUri) { JsonObject tokenConfig = new JsonObject() .put("code", code) .put("redirect_uri", redirectUri); return oauth2.authenticate(tokenConfig); } public Future<User> getUserInfo(OAuth2Token token) { Promise<User> promise = Promise.promise(); oauth2.userInfo(token) .onSuccess(userInfo -> { User user = convertOAuth2User(userInfo); promise.complete(user); }) .onFailure(promise::fail); return promise.future(); } private User convertOAuth2User(JsonObject userInfo) { return new User( userInfo.getString("sub"), userInfo.getString("name", userInfo.getString("preferred_username")), userInfo.getString("email"), new ArrayList<>(), new ArrayList<>() ); } public OAuth2Auth getProvider() { return oauth2; } } JDBC Authentication Provider
JdbcAuthProvider.java:
public class JdbcAuthProvider { private final JDBCAuth jdbcAuth; private final PostgreSQLPool client; public JdbcAuthProvider(Vertx vertx, AuthConfig config) { this.client = createPgPool(vertx, config); this.jdbcAuth = createJdbcAuth(vertx); } private PostgreSQLPool createPgPool(Vertx vertx, AuthConfig config) { JsonObject dbConfig = config.getDatabaseConfig(); PoolOptions poolOptions = new PoolOptions() .setMaxSize(10); PostgreSQLConnectOptions connectOptions = new PostgreSQLConnectOptions() .setPort(5432) .setHost("localhost") .setDatabase("authdb") .setUser(dbConfig.getString("user")) .setPassword(dbConfig.getString("password")); return PostgreSQLPool.pool(vertx, connectOptions, poolOptions); } private JDBCAuth createJdbcAuth(Vertx vertx) { JDBCAuth auth = JDBCAuth.create(vertx, client); // Define authentication query strategy HashStrategy strategy = HashStrategy.load(); // Set authentication query auth.setAuthenticationQuery( "SELECT password, salt FROM users WHERE username = ?"); // Set permissions query auth.setPermissionsQuery( "SELECT permission FROM user_permissions WHERE username = ?"); // Set roles query auth.setRolesQuery( "SELECT role FROM user_roles WHERE username = ?"); return auth; } public Future<io.vertx.ext.auth.User> authenticate(String username, String password) { JsonObject authInfo = new JsonObject() .put("username", username) .put("password", password); return jdbcAuth.authenticate(authInfo); } public Future<Void> createUser(User user, String password) { Promise<Void> promise = Promise.promise(); HashStrategy strategy = HashStrategy.load(); // Generate salt and hash password String salt = strategy.generateSalt(); String hashedPassword = strategy.computeHash(password, salt); // Insert user client.preparedQuery( "INSERT INTO users (id, username, email, password, salt, created_at) " + "VALUES ($1, $2, $3, $4, $5, $6)") .execute(Tuple.of( user.getId(), user.getUsername(), user.getEmail(), hashedPassword, salt, java.time.Instant.now())) .onSuccess(result -> promise.complete()) .onFailure(promise::fail); return promise.future(); } public Future<Void> addUserPermission(String username, String permission) { return client.preparedQuery( "INSERT INTO user_permissions (username, permission) VALUES ($1, $2)") .execute(Tuple.of(username, permission)) .mapEmpty(); } public Future<Void> addUserRole(String username, String role) { return client.preparedQuery( "INSERT INTO user_roles (username, role) VALUES ($1, $2)") .execute(Tuple.of(username, role)) .mapEmpty(); } public JDBCAuth getProvider() { return jdbcAuth; } public PostgreSQLPool getClient() { return client; } } MongoDB Authentication Provider
MongoAuthProvider.java:
public class MongoAuthProvider { private final MongoAuth mongoAuth; private final MongoClient mongoClient; public MongoAuthProvider(Vertx vertx, AuthConfig config) { this.mongoClient = createMongoClient(vertx, config); this.mongoAuth = createMongoAuth(vertx); } private MongoClient createMongoClient(Vertx vertx, AuthConfig config) { JsonObject mongoConfig = config.getMongoConfig(); return MongoClient.create(vertx, mongoConfig); } private MongoAuth createMongoAuth(Vertx vertx) { JsonObject authProperties = new JsonObject() .put("collection_name", "users") .put("username_field", "username") .put("password_field", "password") .put("role_field", "roles") .put("permission_field", "permissions") .put("username_credential_field", "username") .put("password_credential_field", "password"); return MongoAuth.create(vertx, mongoClient, authProperties); } public Future<io.vertx.ext.auth.User> authenticate(String username, String password) { JsonObject authInfo = new JsonObject() .put("username", username) .put("password", password); return mongoAuth.authenticate(authInfo); } public Future<String> createUser(User user, String password) { JsonObject userDocument = new JsonObject() .put("_id", user.getId()) .put("username", user.getUsername()) .put("email", user.getEmail()) .put("password", password) .put("roles", new JsonArray(user.getRoles())) .put("permissions", new JsonArray(user.getPermissions())) .put("createdAt", java.time.Instant.now().toString()) .put("enabled", true); return mongoClient.save("users", userDocument) .map(user.getId()); } public Future<Void> addUserRole(String userId, String role) { JsonObject query = new JsonObject().put("_id", userId); JsonObject update = new JsonObject() .put("$push", new JsonObject().put("roles", role)); return mongoClient.updateCollection("users", query, update) .mapEmpty(); } public Future<Void> addUserPermission(String userId, String permission) { JsonObject query = new JsonObject().put("_id", userId); JsonObject update = new JsonObject() .put("$push", new JsonObject().put("permissions", permission)); return mongoClient.updateCollection("users", query, update) .mapEmpty(); } public MongoAuth getProvider() { return mongoAuth; } } Authentication Service
AuthService.java:
public class AuthService { private final JwtAuthProvider jwtAuthProvider; private final OAuth2AuthProvider oauth2Provider; private final JdbcAuthProvider jdbcAuthProvider; private final MongoAuthProvider mongoAuthProvider; private final Vertx vertx; public AuthService(Vertx vertx, AuthConfig config) { this.vertx = vertx; this.jwtAuthProvider = new JwtAuthProvider(vertx, config); this.oauth2Provider = new OAuth2AuthProvider(vertx, config); this.jdbcAuthProvider = new JdbcAuthProvider(vertx, config); this.mongoAuthProvider = new MongoAuthProvider(vertx, config); } public Future<AuthResult> loginWithCredentials(String username, String password) { Promise<AuthResult> promise = Promise.promise(); // Try JDBC authentication first jdbcAuthProvider.authenticate(username, password) .compose(user -> { // Convert to application user and generate JWT User appUser = extractUserInfo(user); String token = jwtAuthProvider.generateToken( appUser, appUser.getPermissions(), appUser.getRoles()); return Future.succeededFuture(new AuthResult(token, appUser)); }) .onFailure(jdbcFailure -> { // Fallback to MongoDB authentication mongoAuthProvider.authenticate(username, password) .compose(user -> { User appUser = extractUserInfo(user); String token = jwtAuthProvider.generateToken( appUser, appUser.getPermissions(), appUser.getRoles()); return Future.succeededFuture(new AuthResult(token, appUser)); }) .onSuccess(promise::complete) .onFailure(promise::fail); }) .onSuccess(promise::complete); return promise.future(); } public Future<AuthResult> loginWithOAuth2(String code, String redirectUri) { return oauth2Provider.getToken(code, redirectUri) .compose(token -> oauth2Provider.getUserInfo(token)) .compose(user -> { // Create or update user in local database return createOrUpdateUser(user) .compose(updatedUser -> { String jwtToken = jwtAuthProvider.generateToken( updatedUser, updatedUser.getPermissions(), updatedUser.getRoles()); return Future.succeededFuture(new AuthResult(jwtToken, updatedUser)); }); }); } public Future<AuthResult> validateToken(String token) { return jwtAuthProvider.authenticate(token) .map(user -> new AuthResult(token, user)); } public Future<Boolean> authorize(String token, String permission) { return jwtAuthProvider.authenticate(token) .compose(user -> jwtAuthProvider.authorize( convertToAuthUser(user), permission)); } public Future<Boolean> hasRole(String token, String role) { return jwtAuthProvider.authenticate(token) .compose(user -> jwtAuthProvider.hasRole( convertToAuthUser(user), role)); } public Future<User> createUser(User user, String password, AuthProvider provider) { switch (provider) { case JDBC: return jdbcAuthProvider.createUser(user, password) .map(v -> user); case MONGODB: return mongoAuthProvider.createUser(user, password) .map(id -> user); default: return Future.failedFuture("Unsupported provider: " + provider); } } private User extractUserInfo(io.vertx.ext.auth.User authUser) { JsonObject principal = authUser.principal(); List<String> permissions = principal.getJsonArray("permissions", new JsonArray()) .stream() .map(Object::toString) .collect(Collectors.toList()); List<String> roles = principal.getJsonArray("roles", new JsonArray()) .stream() .map(Object::toString) .collect(Collectors.toList()); return new User( principal.getString("userId", principal.getString("sub")), principal.getString("username", principal.getString("sub")), principal.getString("email"), permissions, roles ); } private io.vertx.ext.auth.User convertToAuthUser(User user) { JsonObject principal = new JsonObject() .put("sub", user.getUsername()) .put("userId", user.getId()) .put("email", user.getEmail()) .put("permissions", new JsonArray(user.getPermissions())) .put("roles", new JsonArray(user.getRoles())); return io.vertx.ext.auth.User.create(principal); } private Future<User> createOrUpdateUser(User oauthUser) { // Check if user exists return jdbcAuthProvider.getClient() .preparedQuery("SELECT id FROM users WHERE id = $1") .execute(Tuple.of(oauthUser.getId())) .compose(result -> { if (result.size() == 0) { // Create new user return jdbcAuthProvider.createUser(oauthUser, UUID.randomUUID().toString()) .map(v -> oauthUser); } else { // Update existing user return Future.succeededFuture(oauthUser); } }); } public static class AuthResult { private final String token; private final User user; public AuthResult(String token, User user) { this.token = token; this.user = user; } public String getToken() { return token; } public User getUser() { return user; } } public enum AuthProvider { JDBC, MONGODB, JWT, OAUTH2 } } Vert.x Web Router with Auth
AuthRouter.java:
public class AuthRouter { private final Router router; private final AuthService authService; private final JwtAuthProvider jwtAuthProvider; public AuthRouter(Vertx vertx, AuthService authService, JwtAuthProvider jwtAuthProvider) { this.router = Router.router(vertx); this.authService = authService; this.jwtAuthProvider = jwtAuthProvider; setupRoutes(); } private void setupRoutes() { // JWT Auth handler AuthHandler jwtAuthHandler = JWTAuthHandler.create(jwtAuthProvider.getProvider()); // Public routes router.post("/api/auth/login").handler(this::handleLogin); router.post("/api/auth/register").handler(this::handleRegister); router.get("/api/auth/oauth2/authorize").handler(this::handleOAuth2Authorize); router.get("/api/auth/oauth2/callback").handler(this::handleOAuth2Callback); router.post("/api/auth/refresh").handler(this::handleTokenRefresh); // Protected routes router.route("/api/protected/*").handler(jwtAuthHandler); router.get("/api/protected/profile").handler(this::handleGetProfile); router.put("/api/protected/profile").handler(this::handleUpdateProfile); router.get("/api/protected/users").handler(this::handleGetUsers); // Role-based routes router.route("/api/admin/*").handler(jwtAuthHandler); router.route("/api/admin/*").handler(this::requireRole("ADMIN")); router.get("/api/admin/users").handler(this::handleAdminGetUsers); router.post("/api/admin/users").handler(this::handleAdminCreateUser); // Permission-based routes router.route("/api/secure/*").handler(jwtAuthHandler); router.route("/api/secure/*").handler(this::requirePermission("SECURE_ACCESS")); } private void handleLogin(RoutingContext ctx) { JsonObject body = ctx.getBodyAsJson(); String username = body.getString("username"); String password = body.getString("password"); authService.loginWithCredentials(username, password) .onSuccess(authResult -> { JsonObject response = new JsonObject() .put("token", authResult.getToken()) .put("user", toJson(authResult.getUser())) .put("expiresIn", 3600); ctx.response() .putHeader("Content-Type", "application/json") .end(response.encode()); }) .onFailure(err -> { ctx.response() .setStatusCode(401) .end(new JsonObject().put("error", "Invalid credentials").encode()); }); } private void handleRegister(RoutingContext ctx) { JsonObject body = ctx.getBodyAsJson(); String username = body.getString("username"); String email = body.getString("email"); String password = body.getString("password"); User user = new User( UUID.randomUUID().toString(), username, email, Arrays.asList("user:read", "user:write:self"), Arrays.asList("USER") ); authService.createUser(user, password, AuthService.AuthProvider.JDBC) .onSuccess(createdUser -> { ctx.response() .setStatusCode(201) .end(new JsonObject().put("message", "User created successfully").encode()); }) .onFailure(err -> { ctx.response() .setStatusCode(400) .end(new JsonObject().put("error", err.getMessage()).encode()); }); } private void handleOAuth2Authorize(RoutingContext ctx) { String redirectUri = ctx.request().getParam("redirect_uri"); String state = UUID.randomUUID().toString(); String authUrl = authService.oauth2Provider.getAuthorizationUrl(redirectUri, state); ctx.response() .putHeader("Location", authUrl) .setStatusCode(302) .end(); } private void handleOAuth2Callback(RoutingContext ctx) { String code = ctx.request().getParam("code"); String state = ctx.request().getParam("state"); String redirectUri = ctx.request().getParam("redirect_uri"); authService.loginWithOAuth2(code, redirectUri) .onSuccess(authResult -> { // Redirect to frontend with token String frontendUrl = redirectUri + "?token=" + authResult.getToken(); ctx.response() .putHeader("Location", frontendUrl) .setStatusCode(302) .end(); }) .onFailure(err -> { ctx.response() .setStatusCode(400) .end(new JsonObject().put("error", "OAuth2 authentication failed").encode()); }); } private void handleTokenRefresh(RoutingContext ctx) { String authHeader = ctx.request().getHeader("Authorization"); if (authHeader != null && authHeader.startsWith("Bearer ")) { String token = authHeader.substring(7); authService.validateToken(token) .compose(authResult -> { // Generate new token with extended expiry User user = authResult.getUser(); String newToken = jwtAuthProvider.generateToken( user, user.getPermissions(), user.getRoles()); return Future.succeededFuture(newToken); }) .onSuccess(newToken -> { JsonObject response = new JsonObject() .put("token", newToken) .put("expiresIn", 3600); ctx.response() .putHeader("Content-Type", "application/json") .end(response.encode()); }) .onFailure(err -> { ctx.response() .setStatusCode(401) .end(new JsonObject().put("error", "Invalid token").encode()); }); } else { ctx.response() .setStatusCode(401) .end(new JsonObject().put("error", "Authorization header required").encode()); } } private void handleGetProfile(RoutingContext ctx) { User user = extractUserFromContext(ctx); JsonObject profile = new JsonObject() .put("id", user.getId()) .put("username", user.getUsername()) .put("email", user.getEmail()) .put("roles", new JsonArray(user.getRoles())) .put("permissions", new JsonArray(user.getPermissions())); ctx.response() .putHeader("Content-Type", "application/json") .end(profile.encode()); } private void handleUpdateProfile(RoutingContext ctx) { User user = extractUserFromContext(ctx); JsonObject body = ctx.getBodyAsJson(); // Update user logic here ctx.response() .end(new JsonObject().put("message", "Profile updated").encode()); } private void handleGetUsers(RoutingContext ctx) { // Only return users if user has permission String token = extractToken(ctx); authService.authorize(token, "user:read:all") .onSuccess(authorized -> { if (authorized) { // Return all users JsonArray users = new JsonArray() .add(new JsonObject().put("id", "1").put("username", "user1")) .add(new JsonObject().put("id", "2").put("username", "user2")); ctx.response() .putHeader("Content-Type", "application/json") .end(users.encode()); } else { ctx.response() .setStatusCode(403) .end(new JsonObject().put("error", "Insufficient permissions").encode()); } }) .onFailure(err -> { ctx.response() .setStatusCode(403) .end(new JsonObject().put("error", "Authorization failed").encode()); }); } private void handleAdminGetUsers(RoutingContext ctx) { // Admin-only endpoint JsonArray users = new JsonArray() .add(new JsonObject().put("id", "1").put("username", "admin")) .add(new JsonObject().put("id", "2").put("username", "user")); ctx.response() .putHeader("Content-Type", "application/json") .end(users.encode()); } private void handleAdminCreateUser(RoutingContext ctx) { JsonObject body = ctx.getBodyAsJson(); // Admin create user logic ctx.response() .setStatusCode(201) .end(new JsonObject().put("message", "User created by admin").encode()); } private Handler<RoutingContext> requireRole(String role) { return ctx -> { String token = extractToken(ctx); authService.hasRole(token, role) .onSuccess(hasRole -> { if (hasRole) { ctx.next(); } else { ctx.response() .setStatusCode(403) .end(new JsonObject().put("error", "Insufficient role").encode()); } }) .onFailure(err -> { ctx.response() .setStatusCode(403) .end(new JsonObject().put("error", "Role check failed").encode()); }); }; } private Handler<RoutingContext> requirePermission(String permission) { return ctx -> { String token = extractToken(ctx); authService.authorize(token, permission) .onSuccess(authorized -> { if (authorized) { ctx.next(); } else { ctx.response() .setStatusCode(403) .end(new JsonObject().put("error", "Insufficient permissions").encode()); } }) .onFailure(err -> { ctx.response() .setStatusCode(403) .end(new JsonObject().put("error", "Permission check failed").encode()); }); }; } private User extractUserFromContext(RoutingContext ctx) { io.vertx.ext.auth.User authUser = ctx.user(); JsonObject principal = authUser.principal(); return new User( principal.getString("userId"), principal.getString("sub"), principal.getString("email"), principal.getJsonArray("permissions").getList(), principal.getJsonArray("roles").getList() ); } private String extractToken(RoutingContext ctx) { String authHeader = ctx.request().getHeader("Authorization"); if (authHeader != null && authHeader.startsWith("Bearer ")) { return authHeader.substring(7); } return null; } private JsonObject toJson(User user) { return new JsonObject() .put("id", user.getId()) .put("username", user.getUsername()) .put("email", user.getEmail()) .put("roles", new JsonArray(user.getRoles())) .put("permissions", new JsonArray(user.getPermissions())); } public Router getRouter() { return router; } } Main Verticle
AuthVerticle.java:
public class AuthVerticle extends AbstractVerticle { private AuthService authService; private JwtAuthProvider jwtAuthProvider; private HttpServer server; @Override public void start(Promise<Void> startPromise) { try { AuthConfig config = new AuthConfig(); // Initialize auth providers this.jwtAuthProvider = new JwtAuthProvider(vertx, config); this.authService = new AuthService(vertx, config); // Create router AuthRouter authRouter = new AuthRouter(vertx, authService, jwtAuthProvider); // Create HTTP server this.server = vertx.createHttpServer(); server.requestHandler(authRouter.getRouter()) .listen(config.getHttpPort(), config.getHttpHost()) .onSuccess(http -> { log.info("HTTP server started on port {}", config.getHttpPort()); startPromise.complete(); }) .onFailure(startPromise::fail); } catch (Exception e) { startPromise.fail(e); } } @Override public void stop(Promise<Void> stopPromise) { if (server != null) { server.close() .onSuccess(v -> log.info("HTTP server stopped")) .onFailure(stopPromise::fail) .onComplete(v -> stopPromise.complete()); } else { stopPromise.complete(); } } private static final Logger log = LoggerFactory.getLogger(AuthVerticle.class); } User Model
User.java:
public class User { private final String id; private final String username; private final String email; private final List<String> permissions; private final List<String> roles; public User(String id, String username, String email, List<String> permissions, List<String> roles) { this.id = id; this.username = username; this.email = email; this.permissions = permissions != null ? permissions : new ArrayList<>(); this.roles = roles != null ? roles : new ArrayList<>(); } // Getters public String getId() { return id; } public String getUsername() { return username; } public String getEmail() { return email; } public List<String> getPermissions() { return permissions; } public List<String> getRoles() { return roles; } public void addPermission(String permission) { this.permissions.add(permission); } public void addRole(String role) { this.roles.add(role); } public boolean hasPermission(String permission) { return permissions.contains(permission); } public boolean hasRole(String role) { return roles.contains(role); } } Application Runner
Application.java:
public class Application { public static void main(String[] args) { Vertx vertx = Vertx.vertx(new VertxOptions() .setEventLoopPoolSize(4) .setWorkerPoolSize(20)); // Deploy auth verticle vertx.deployVerticle(new AuthVerticle()) .onSuccess(deploymentId -> { System.out.println("Auth verticle deployed with ID: " + deploymentId); }) .onFailure(err -> { System.err.println("Failed to deploy auth verticle: " + err.getMessage()); err.printStackTrace(); }); // Add shutdown hook Runtime.getRuntime().addShutdownHook(new Thread(() -> { vertx.close(); })); } } Best Practices
- Use environment variables for sensitive configuration
- Implement rate limiting on authentication endpoints
- Use secure password hashing with proper salts
- Implement token blacklisting for logout functionality
- Use HTTPS in production environments
- Monitor authentication attempts for security auditing
- Implement proper error handling without leaking sensitive information
Conclusion
Vert.x Auth provides a comprehensive, asynchronous authentication and authorization framework that:
- Supports multiple authentication providers (JWT, OAuth2, JDBC, MongoDB)
- Provides role-based and permission-based authorization
- Offers non-blocking, reactive operations
- Integrates seamlessly with Vert.x Web
- Supports token-based authentication with JWT
- Enables OAuth2 integration for social login
This implementation demonstrates how to build a secure, scalable authentication service using Vert.x that can handle various authentication scenarios while maintaining high performance and reliability.