google-site-verification: google61fe8ba583a51912.html
Secure User Management: Implementing Firebase Authentication in Java Applications

Firebase Authentication provides backend services, easy-to-use SDKs, and ready-made UI libraries to authenticate users to your app. This guide covers complete integration of Firebase Auth in Java applications including server-side verification, user management, and security rules.

Architecture Overview

Java Application → Firebase Admin SDK → Firebase Auth Service → Identity Providers ↑ (JWT Verification / User Management)

Step 1: Dependencies Setup

Maven Dependencies

<!-- pom.xml --> <dependencies> <!-- Firebase Admin SDK --> <dependency> <groupId>com.google.firebase</groupId> <artifactId>firebase-admin</artifactId> <version>9.2.0</version> </dependency> <!-- JWT Processing --> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>4.4.0</version> </dependency> <!-- JSON Processing --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> <!-- HTTP Client --> <dependency> <groupId>org.apache.httpcomponents.client5</groupId> <artifactId>httpclient5</artifactId> </dependency> <!-- Spring Boot --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Spring Security --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!-- Validation --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> <!-- Caching --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> </dependencies>

Step 2: Configuration Classes

Firebase Configuration

// src/main/java/com/company/firebase/config/FirebaseConfig.java package com.company.firebase.config; import com.google.auth.oauth2.GoogleCredentials; import com.google.firebase.FirebaseApp; import com.google.firebase.FirebaseOptions; import com.google.firebase.auth.FirebaseAuth; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import javax.annotation.PostConstruct; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; @Configuration @Slf4j public class FirebaseConfig { @Value("${firebase.database.url:}") private String databaseUrl; @Value("${firebase.project.id:}") private String projectId; @Value("${firebase.credentials.file:}") private String credentialsFile; @Value("${firebase.credentials.json:}") private String credentialsJson; @PostConstruct public void initialize() { try { if (FirebaseApp.getApps().isEmpty()) { FirebaseOptions options = buildFirebaseOptions(); FirebaseApp.initializeApp(options); log.info("Firebase application initialized successfully"); } else { log.info("Firebase application already initialized"); } } catch (IOException e) { log.error("Failed to initialize Firebase application", e); throw new RuntimeException("Firebase initialization failed", e); } } private FirebaseOptions buildFirebaseOptions() throws IOException { FirebaseOptions.Builder builder = FirebaseOptions.builder(); // Set project ID if (projectId != null && !projectId.trim().isEmpty()) { builder.setProjectId(projectId); } // Set database URL if (databaseUrl != null && !databaseUrl.trim().isEmpty()) { builder.setDatabaseUrl(databaseUrl); } // Set credentials GoogleCredentials credentials = getCredentials(); builder.setCredentials(credentials); return builder.build(); } private GoogleCredentials getCredentials() throws IOException { if (credentialsJson != null && !credentialsJson.trim().isEmpty()) { // Use JSON string from environment variable or configuration try (InputStream serviceAccount = new ByteArrayInputStream(credentialsJson.getBytes())) { return GoogleCredentials.fromStream(serviceAccount); } } else if (credentialsFile != null && !credentialsFile.trim().isEmpty()) { // Use credentials file Resource resource = new ClassPathResource(credentialsFile); try (InputStream serviceAccount = resource.getInputStream()) { return GoogleCredentials.fromStream(serviceAccount); } } else { // Use default credentials (for Google Cloud environments) return GoogleCredentials.getApplicationDefault(); } } @Bean public FirebaseAuth firebaseAuth() { return FirebaseAuth.getInstance(); } }

Application Properties

# application.yml firebase: database: url: ${FIREBASE_DATABASE_URL:https://your-project.firebaseio.com} project: id: ${FIREBASE_PROJECT_ID:your-project-id} credentials: file: ${FIREBASE_CREDENTIALS_FILE:firebase-service-account.json} json: ${FIREBASE_CREDENTIALS_JSON:} auth: token-expiry: 3600 cookie-name: "firebase-token" cookie-secure: true security: jwt: secret: ${JWT_SECRET:your-jwt-secret-key} expiration: 86400000 server: port: 8080 logging: level: com.company.firebase: DEBUG com.google.firebase: INFO

Step 3: Firebase Auth Service

Core Authentication Service

// src/main/java/com/company/firebase/service/FirebaseAuthService.java package com.company.firebase.service; import com.company.firebase.model.FirebaseUser; import com.google.firebase.auth.*; import com.google.firebase.auth.UserRecord; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.util.*; import java.util.concurrent.TimeUnit; @Service @Slf4j public class FirebaseAuthService { private final FirebaseAuth firebaseAuth; public FirebaseAuthService(FirebaseAuth firebaseAuth) { this.firebaseAuth = firebaseAuth; } /** * Verify Firebase ID token and get user information */ public FirebaseUser verifyToken(String idToken) { try { FirebaseToken decodedToken = firebaseAuth.verifyIdToken(idToken); FirebaseUser user = new FirebaseUser(); user.setUid(decodedToken.getUid()); user.setEmail(decodedToken.getEmail()); user.setEmailVerified(decodedToken.isEmailVerified()); user.setDisplayName(decodedToken.getName()); user.setPhotoUrl(decodedToken.getPicture()); user.setIssuer(decodedToken.getIssuer()); user.setSignInProvider(decodedToken.getFirebase().getSignInProvider()); user.setClaims(decodedToken.getClaims()); log.info("Token verified for user: {}", user.getUid()); return user; } catch (FirebaseAuthException e) { log.error("Firebase token verification failed", e); throw new RuntimeException("Token verification failed: " + e.getMessage(), e); } } /** * Verify ID token and check if revoked */ public FirebaseUser verifyTokenAndCheckRevoked(String idToken) { try { FirebaseToken decodedToken = firebaseAuth.verifyIdToken(idToken, true); FirebaseUser user = new FirebaseUser(); user.setUid(decodedToken.getUid()); user.setEmail(decodedToken.getEmail()); user.setEmailVerified(decodedToken.isEmailVerified()); user.setClaims(decodedToken.getClaims()); return user; } catch (FirebaseAuthException e) { log.error("Firebase token verification failed (with revocation check)", e); throw new RuntimeException("Token verification failed: " + e.getMessage(), e); } } /** * Create a new user with email and password */ public FirebaseUser createUser(String email, String password, String displayName) { try { UserRecord.CreateRequest request = new UserRecord.CreateRequest() .setEmail(email) .setPassword(password) .setDisplayName(displayName) .setEmailVerified(false); UserRecord userRecord = firebaseAuth.createUser(request); return convertToFirebaseUser(userRecord); } catch (FirebaseAuthException e) { log.error("Failed to create Firebase user: {}", email, e); throw new RuntimeException("User creation failed: " + e.getMessage(), e); } } /** * Create a new user with additional properties */ public FirebaseUser createUserWithProperties(UserCreationRequest request) { try { UserRecord.CreateRequest createRequest = new UserRecord.CreateRequest() .setEmail(request.getEmail()) .setPassword(request.getPassword()) .setDisplayName(request.getDisplayName()) .setPhotoUrl(request.getPhotoUrl()) .setEmailVerified(request.isEmailVerified()) .setDisabled(false); // Set custom claims if provided if (request.getCustomClaims() != null && !request.getCustomClaims().isEmpty()) { // Custom claims are set after user creation } UserRecord userRecord = firebaseAuth.createUser(createRequest); // Set custom claims if provided if (request.getCustomClaims() != null && !request.getCustomClaims().isEmpty()) { setCustomClaims(userRecord.getUid(), request.getCustomClaims()); } return convertToFirebaseUser(userRecord); } catch (FirebaseAuthException e) { log.error("Failed to create Firebase user with properties: {}", request.getEmail(), e); throw new RuntimeException("User creation failed: " + e.getMessage(), e); } } /** * Get user by UID */ public FirebaseUser getUserById(String uid) { try { UserRecord userRecord = firebaseAuth.getUser(uid); return convertToFirebaseUser(userRecord); } catch (FirebaseAuthException e) { log.error("Failed to get Firebase user: {}", uid, e); throw new RuntimeException("User retrieval failed: " + e.getMessage(), e); } } /** * Get user by email */ public FirebaseUser getUserByEmail(String email) { try { UserRecord userRecord = firebaseAuth.getUserByEmail(email); return convertToFirebaseUser(userRecord); } catch (FirebaseAuthException e) { log.error("Failed to get Firebase user by email: {}", email, e); throw new RuntimeException("User retrieval failed: " + e.getMessage(), e); } } /** * Update user properties */ public FirebaseUser updateUser(String uid, UserUpdateRequest updateRequest) { try { UserRecord.UpdateRequest request = new UserRecord.UpdateRequest(uid); if (updateRequest.getEmail() != null) { request.setEmail(updateRequest.getEmail()); } if (updateRequest.getPassword() != null) { request.setPassword(updateRequest.getPassword()); } if (updateRequest.getDisplayName() != null) { request.setDisplayName(updateRequest.getDisplayName()); } if (updateRequest.getPhotoUrl() != null) { request.setPhotoUrl(updateRequest.getPhotoUrl()); } if (updateRequest.isEmailVerified() != null) { request.setEmailVerified(updateRequest.isEmailVerified()); } if (updateRequest.isDisabled() != null) { request.setDisabled(updateRequest.isDisabled()); } UserRecord userRecord = firebaseAuth.updateUser(request); return convertToFirebaseUser(userRecord); } catch (FirebaseAuthException e) { log.error("Failed to update Firebase user: {}", uid, e); throw new RuntimeException("User update failed: " + e.getMessage(), e); } } /** * Delete user by UID */ public void deleteUser(String uid) { try { firebaseAuth.deleteUser(uid); log.info("Firebase user deleted: {}", uid); } catch (FirebaseAuthException e) { log.error("Failed to delete Firebase user: {}", uid, e); throw new RuntimeException("User deletion failed: " + e.getMessage(), e); } } /** * Set custom claims for a user */ public void setCustomClaims(String uid, Map<String, Object> claims) { try { firebaseAuth.setCustomUserClaims(uid, claims); log.info("Custom claims set for user: {}", uid); } catch (FirebaseAuthException e) { log.error("Failed to set custom claims for user: {}", uid, e); throw new RuntimeException("Setting custom claims failed: " + e.getMessage(), e); } } /** * Get custom claims for a user */ public Map<String, Object> getCustomClaims(String uid) { try { UserRecord userRecord = firebaseAuth.getUser(uid); return userRecord.getCustomClaims(); } catch (FirebaseAuthException e) { log.error("Failed to get custom claims for user: {}", uid, e); throw new RuntimeException("Getting custom claims failed: " + e.getMessage(), e); } } /** * Revoke all refresh tokens for a user */ public void revokeRefreshTokens(String uid) { try { firebaseAuth.revokeRefreshTokens(uid); log.info("Refresh tokens revoked for user: {}", uid); } catch (FirebaseAuthException e) { log.error("Failed to revoke refresh tokens for user: {}", uid, e); throw new RuntimeException("Revoking tokens failed: " + e.getMessage(), e); } } /** * List all users with pagination */ public List<FirebaseUser> listUsers(int maxResults, String pageToken) { try { ListUsersPage page = firebaseAuth.listUsers(null, maxResults); List<FirebaseUser> users = new ArrayList<>(); for (ExportedUserRecord userRecord : page.iterateAll()) { users.add(convertToFirebaseUser(userRecord)); } return users; } catch (FirebaseAuthException e) { log.error("Failed to list Firebase users", e); throw new RuntimeException("Listing users failed: " + e.getMessage(), e); } } /** * Create custom token for server-to-server authentication */ public String createCustomToken(String uid, Map<String, Object> additionalClaims) { try { return firebaseAuth.createCustomToken(uid, additionalClaims); } catch (FirebaseAuthException e) { log.error("Failed to create custom token for user: {}", uid, e); throw new RuntimeException("Custom token creation failed: " + e.getMessage(), e); } } /** * Generate email verification link */ public String generateEmailVerificationLink(String email, String redirectUrl) { try { ActionCodeSettings settings = ActionCodeSettings.builder() .setUrl(redirectUrl) .setHandleCodeInApp(true) .build(); return firebaseAuth.generateEmailVerificationLink(email, settings); } catch (FirebaseAuthException e) { log.error("Failed to generate email verification link for: {}", email, e); throw new RuntimeException("Email verification link generation failed: " + e.getMessage(), e); } } /** * Generate password reset link */ public String generatePasswordResetLink(String email, String redirectUrl) { try { ActionCodeSettings settings = ActionCodeSettings.builder() .setUrl(redirectUrl) .setHandleCodeInApp(true) .build(); return firebaseAuth.generatePasswordResetLink(email, settings); } catch (FirebaseAuthException e) { log.error("Failed to generate password reset link for: {}", email, e); throw new RuntimeException("Password reset link generation failed: " + e.getMessage(), e); } } /** * Generate email sign-in link */ public String generateSignInWithEmailLink(String email, String redirectUrl) { try { ActionCodeSettings settings = ActionCodeSettings.builder() .setUrl(redirectUrl) .setHandleCodeInApp(true) .build(); return firebaseAuth.generateSignInWithEmailLink(email, settings); } catch (FirebaseAuthException e) { log.error("Failed to generate sign-in link for: {}", email, e); throw new RuntimeException("Sign-in link generation failed: " + e.getMessage(), e); } } /** * Import users in batch */ public UserImportResult importUsers(List<UserImportRecord> users) { try { List<UserRecord.ImportUser> importUsers = new ArrayList<>(); for (UserImportRecord user : users) { UserRecord.ImportUser importUser = new UserRecord.ImportUser() .setUid(user.getUid()) .setEmail(user.getEmail()) .setDisplayName(user.getDisplayName()) .setPhotoUrl(user.getPhotoUrl()) .setEmailVerified(user.isEmailVerified()) .setPasswordHash(user.getPasswordHash().getBytes()) .setPasswordSalt(user.getPasswordSalt().getBytes()); importUsers.add(importUser); } UserImportResult result = firebaseAuth.importUsers(importUsers); log.info("Imported {} users successfully", result.getSuccessCount()); return result; } catch (FirebaseAuthException e) { log.error("Failed to import users", e); throw new RuntimeException("User import failed: " + e.getMessage(), e); } } /** * Convert Firebase UserRecord to FirebaseUser */ private FirebaseUser convertToFirebaseUser(UserRecord userRecord) { FirebaseUser user = new FirebaseUser(); user.setUid(userRecord.getUid()); user.setEmail(userRecord.getEmail()); user.setEmailVerified(userRecord.isEmailVerified()); user.setDisplayName(userRecord.getDisplayName()); user.setPhotoUrl(userRecord.getPhotoUrl()); user.setDisabled(userRecord.isDisabled()); user.setCustomClaims(userRecord.getCustomClaims()); user.setProviderData(userRecord.getProviderData()); user.setTokensValidAfterTimestamp(userRecord.getTokensValidAfterTimestamp()); user.setUserMetadata(new FirebaseUser.UserMetadata( userRecord.getUserMetadata().getCreationTimestamp(), userRecord.getUserMetadata().getLastSignInTimestamp() )); return user; } @Data public static class UserCreationRequest { private String email; private String password; private String displayName; private String photoUrl; private boolean emailVerified = false; private Map<String, Object> customClaims; } @Data public static class UserUpdateRequest { private String email; private String password; private String displayName; private String photoUrl; private Boolean emailVerified; private Boolean disabled; private Map<String, Object> customClaims; } @Data public static class UserImportRecord { private String uid; private String email; private String displayName; private String photoUrl; private boolean emailVerified; private String passwordHash; private String passwordSalt; private Map<String, Object> customClaims; } }

Step 4: JWT Token Service

JWT Token Management

// src/main/java/com/company/firebase/service/JwtTokenService.java package com.company.firebase.service; import com.auth0.jwt.JWT; import com.auth0.jwt.JWTVerifier; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.exceptions.JWTVerificationException; import com.auth0.jwt.interfaces.DecodedJWT; import com.company.firebase.model.FirebaseUser; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import java.util.Date; import java.util.HashMap; import java.util.Map; @Service @Slf4j public class JwtTokenService { @Value("${security.jwt.secret}") private String jwtSecret; @Value("${security.jwt.expiration}") private long jwtExpiration; /** * Generate JWT token for authenticated Firebase user */ public String generateToken(FirebaseUser firebaseUser) { try { Algorithm algorithm = Algorithm.HMAC256(jwtSecret); Map<String, Object> claims = new HashMap<>(); claims.put("uid", firebaseUser.getUid()); claims.put("email", firebaseUser.getEmail()); claims.put("emailVerified", firebaseUser.isEmailVerified()); claims.put("provider", firebaseUser.getSignInProvider()); // Add custom claims from Firebase if (firebaseUser.getClaims() != null) { claims.putAll(firebaseUser.getClaims()); } return JWT.create() .withSubject(firebaseUser.getUid()) .withIssuer("firebase-auth-service") .withIssuedAt(new Date()) .withExpiresAt(new Date(System.currentTimeMillis() + jwtExpiration)) .withClaim("user", claims) .sign(algorithm); } catch (Exception e) { log.error("Failed to generate JWT token for user: {}", firebaseUser.getUid(), e); throw new RuntimeException("Token generation failed", e); } } /** * Verify and decode JWT token */ public DecodedJWT verifyToken(String token) { try { Algorithm algorithm = Algorithm.HMAC256(jwtSecret); JWTVerifier verifier = JWT.require(algorithm) .withIssuer("firebase-auth-service") .build(); return verifier.verify(token); } catch (JWTVerificationException e) { log.error("JWT token verification failed", e); throw new RuntimeException("Token verification failed", e); } } /** * Extract user information from JWT token */ public FirebaseUser extractUserFromToken(String token) { try { DecodedJWT decodedJWT = verifyToken(token); FirebaseUser user = new FirebaseUser(); user.setUid(decodedJWT.getSubject()); Map<String, Object> userClaims = decodedJWT.getClaim("user").asMap(); if (userClaims != null) { user.setEmail((String) userClaims.get("email")); user.setEmailVerified(Boolean.TRUE.equals(userClaims.get("emailVerified"))); user.setSignInProvider((String) userClaims.get("provider")); } return user; } catch (Exception e) { log.error("Failed to extract user from JWT token", e); throw new RuntimeException("User extraction from token failed", e); } } /** * Check if token is expired */ public boolean isTokenExpired(String token) { try { DecodedJWT decodedJWT = verifyToken(token); return decodedJWT.getExpiresAt().before(new Date()); } catch (Exception e) { return true; } } /** * Refresh JWT token */ public String refreshToken(String oldToken) { try { FirebaseUser user = extractUserFromToken(oldToken); return generateToken(user); } catch (Exception e) { log.error("Failed to refresh JWT token", e); throw new RuntimeException("Token refresh failed", e); } } /** * Get token expiration date */ public Date getExpirationDate(String token) { try { DecodedJWT decodedJWT = verifyToken(token); return decodedJWT.getExpiresAt(); } catch (Exception e) { log.error("Failed to get token expiration date", e); return new Date(); // Return current date as fallback } } /** * Get token issued date */ public Date getIssuedAtDate(String token) { try { DecodedJWT decodedJWT = verifyToken(token); return decodedJWT.getIssuedAt(); } catch (Exception e) { log.error("Failed to get token issued date", e); return new Date(); // Return current date as fallback } } }

Step 5: Security Configuration

Spring Security Configuration

// src/main/java/com/company/firebase/config/SecurityConfig.java package com.company.firebase.config; import com.company.firebase.security.FirebaseAuthenticationFilter; import com.company.firebase.security.FirebaseAuthenticationProvider; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import java.util.Arrays; @Configuration @EnableWebSecurity @EnableMethodSecurity(prePostEnabled = true) @RequiredArgsConstructor public class SecurityConfig { private final FirebaseAuthenticationProvider firebaseAuthenticationProvider; @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .cors(cors -> cors.configurationSource(corsConfigurationSource())) .csrf(csrf -> csrf.disable()) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(authz -> authz .requestMatchers("/api/auth/public/**", "/api/auth/login", "/api/auth/register").permitAll() .requestMatchers("/api/auth/admin/**").hasRole("ADMIN") .requestMatchers("/api/auth/user/**").hasAnyRole("USER", "ADMIN") .anyRequest().authenticated() ) .addFilterBefore(firebaseAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); return http.build(); } @Bean public FirebaseAuthenticationFilter firebaseAuthenticationFilter() { return new FirebaseAuthenticationFilter(); } @Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); configuration.setAllowedOriginPatterns(Arrays.asList("http://localhost:*", "https://*.yourdomain.com")); configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); configuration.setAllowedHeaders(Arrays.asList("*")); configuration.setAllowCredentials(true); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration); return source; } }

Firebase Authentication Provider

// src/main/java/com/company/firebase/security/FirebaseAuthenticationProvider.java package com.company.firebase.security; import com.company.firebase.model.FirebaseUser; import com.company.firebase.service.FirebaseAuthService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.stereotype.Component; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @Component @RequiredArgsConstructor @Slf4j public class FirebaseAuthenticationProvider implements AuthenticationProvider { private final FirebaseAuthService firebaseAuthService; @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { if (!(authentication instanceof FirebaseAuthenticationToken)) { return null; } FirebaseAuthenticationToken firebaseAuthentication = (FirebaseAuthenticationToken) authentication; String idToken = firebaseAuthentication.getCredentials().toString(); try { // Verify Firebase token FirebaseUser firebaseUser = firebaseAuthService.verifyToken(idToken); // Extract authorities from custom claims List<SimpleGrantedAuthority> authorities = extractAuthorities(firebaseUser); // Create authenticated token FirebaseAuthenticationToken authenticatedToken = new FirebaseAuthenticationToken(firebaseUser, idToken, authorities); authenticatedToken.setAuthenticated(true); log.debug("User authenticated successfully: {}", firebaseUser.getUid()); return authenticatedToken; } catch (Exception e) { log.error("Firebase authentication failed", e); throw new BadCredentialsException("Authentication failed", e); } } @Override public boolean supports(Class<?> authentication) { return FirebaseAuthenticationToken.class.isAssignableFrom(authentication); } private List<SimpleGrantedAuthority> extractAuthorities(FirebaseUser firebaseUser) { Map<String, Object> claims = firebaseUser.getClaims(); if (claims == null || claims.isEmpty()) { return Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER")); } // Extract roles from custom claims Object rolesClaim = claims.get("roles"); if (rolesClaim instanceof List) { @SuppressWarnings("unchecked") List<String> roles = (List<String>) rolesClaim; return roles.stream() .map(role -> new SimpleGrantedAuthority("ROLE_" + role.toUpperCase())) .collect(Collectors.toList()); } // Extract role from single claim Object roleClaim = claims.get("role"); if (roleClaim instanceof String) { return Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + ((String) roleClaim).toUpperCase())); } return Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER")); } }

Firebase Authentication Filter

// src/main/java/com/company/firebase/security/FirebaseAuthenticationFilter.java package com.company.firebase.security; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.filter.OncePerRequestFilter; import java.io.IOException; @Slf4j public class FirebaseAuthenticationFilter extends OncePerRequestFilter { private static final String AUTH_HEADER = "Authorization"; private static final String AUTH_PREFIX = "Bearer "; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String authHeader = request.getHeader(AUTH_HEADER); if (authHeader != null && authHeader.startsWith(AUTH_PREFIX)) { String idToken = authHeader.substring(AUTH_PREFIX.length()); try { FirebaseAuthenticationToken authToken = new FirebaseAuthenticationToken(null, idToken); SecurityContextHolder.getContext().setAuthentication(authToken); } catch (Exception e) { log.warn("Firebase token authentication failed", e); SecurityContextHolder.clearContext(); } } filterChain.doFilter(request, response); } } // Firebase Authentication Token class FirebaseAuthenticationToken extends org.springframework.security.authentication.AbstractAuthenticationToken { private final Object principal; private final String credentials; public FirebaseAuthenticationToken(Object principal, String credentials) { super(null); this.principal = principal; this.credentials = credentials; setAuthenticated(false); } public FirebaseAuthenticationToken(Object principal, String credentials, java.util.Collection<org.springframework.security.core.GrantedAuthority> authorities) { super(authorities); this.principal = principal; this.credentials = credentials; super.setAuthenticated(true); } @Override public Object getCredentials() { return credentials; } @Override public Object getPrincipal() { return principal; } }

Step 6: Data Models

Firebase User Model

// src/main/java/com/company/firebase/model/FirebaseUser.java package com.company.firebase.model; import com.google.firebase.auth.UserInfo; import lombok.Data; import java.util.List; import java.util.Map; @Data public class FirebaseUser { private String uid; private String email; private boolean emailVerified; private String displayName; private String photoUrl; private boolean disabled; private Map<String, Object> customClaims; private List<? extends UserInfo> providerData; private Long tokensValidAfterTimestamp; private UserMetadata userMetadata; private String issuer; private String signInProvider; private Map<String, Object> claims; @Data public static class UserMetadata { private final Long creationTimestamp; private final Long lastSignInTimestamp; public UserMetadata(Long creationTimestamp, Long lastSignInTimestamp) { this.creationTimestamp = creationTimestamp; this.lastSignInTimestamp = lastSignInTimestamp; } } // Helper methods public boolean isActive() { return !disabled && emailVerified; } public boolean hasRole(String role) { if (customClaims == null) return false; Object roles = customClaims.get("roles"); if (roles instanceof List) { return ((List<?>) roles).contains(role); } return role.equals(customClaims.get("role")); } public boolean hasPermission(String permission) { if (customClaims == null) return false; Object permissions = customClaims.get("permissions"); if (permissions instanceof List) { return ((List<?>) permissions).contains(permission); } return false; } }

Step 7: REST API Controllers

Authentication Controller

// src/main/java/com/company/firebase/controller/AuthController.java package com.company.firebase.controller; import com.company.firebase.model.FirebaseUser; import com.company.firebase.security.FirebaseAuthenticationToken; import com.company.firebase.service.FirebaseAuthService; import com.company.firebase.service.JwtTokenService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.*; import java.util.Map; @RestController @RequestMapping("/api/auth") @RequiredArgsConstructor @Slf4j public class AuthController { private final FirebaseAuthService firebaseAuthService; private final JwtTokenService jwtTokenService; @PostMapping("/login") public ResponseEntity<Map<String, Object>> login(@RequestBody LoginRequest request) { try { // Verify Firebase token FirebaseUser firebaseUser = firebaseAuthService.verifyToken(request.getIdToken()); // Generate JWT token String jwtToken = jwtTokenService.generateToken(firebaseUser); return ResponseEntity.ok(Map.of( "success", true, "token", jwtToken, "user", Map.of( "uid", firebaseUser.getUid(), "email", firebaseUser.getEmail(), "displayName", firebaseUser.getDisplayName(), "emailVerified", firebaseUser.isEmailVerified() ) )); } catch (Exception e) { log.error("Login failed", e); return ResponseEntity.badRequest().body(Map.of( "success", false, "error", "Authentication failed" )); } } @PostMapping("/register") public ResponseEntity<Map<String, Object>> register(@RequestBody RegisterRequest request) { try { FirebaseAuthService.UserCreationRequest creationRequest = new FirebaseAuthService.UserCreationRequest(); creationRequest.setEmail(request.getEmail()); creationRequest.setPassword(request.getPassword()); creationRequest.setDisplayName(request.getDisplayName()); FirebaseUser user = firebaseAuthService.createUserWithProperties(creationRequest); return ResponseEntity.ok(Map.of( "success", true, "user", Map.of( "uid", user.getUid(), "email", user.getEmail(), "displayName", user.getDisplayName() ), "message", "User created successfully" )); } catch (Exception e) { log.error("Registration failed", e); return ResponseEntity.badRequest().body(Map.of( "success", false, "error", "Registration failed: " + e.getMessage() )); } } @GetMapping("/profile") public ResponseEntity<Map<String, Object>> getProfile(@AuthenticationPrincipal FirebaseUser user) { if (user == null) { return ResponseEntity.badRequest().body(Map.of( "success", false, "error", "User not authenticated" )); } return ResponseEntity.ok(Map.of( "success", true, "user", Map.of( "uid", user.getUid(), "email", user.getEmail(), "displayName", user.getDisplayName(), "emailVerified", user.isEmailVerified(), "photoUrl", user.getPhotoUrl() ) )); } @PostMapping("/refresh-token") public ResponseEntity<Map<String, Object>> refreshToken(@RequestBody RefreshTokenRequest request) { try { String newToken = jwtTokenService.refreshToken(request.getToken()); return ResponseEntity.ok(Map.of( "success", true, "token", newToken )); } catch (Exception e) { log.error("Token refresh failed", e); return ResponseEntity.badRequest().body(Map.of( "success", false, "error", "Token refresh failed" )); } } @PostMapping("/verify-email") public ResponseEntity<Map<String, Object>> sendVerificationEmail(@RequestBody EmailRequest request) { try { String verificationLink = firebaseAuthService.generateEmailVerificationLink( request.getEmail(), request.getRedirectUrl() ); // In a real application, you would send this link via email log.info("Email verification link: {}", verificationLink); return ResponseEntity.ok(Map.of( "success", true, "message", "Verification email sent" )); } catch (Exception e) { log.error("Failed to send verification email", e); return ResponseEntity.badRequest().body(Map.of( "success", false, "error", "Failed to send verification email" )); } } @PostMapping("/reset-password") public ResponseEntity<Map<String, Object>> sendPasswordResetEmail(@RequestBody EmailRequest request) { try { String resetLink = firebaseAuthService.generatePasswordResetLink( request.getEmail(), request.getRedirectUrl() ); // In a real application, you would send this link via email log.info("Password reset link: {}", resetLink); return ResponseEntity.ok(Map.of( "success", true, "message", "Password reset email sent" )); } catch (Exception e) { log.error("Failed to send password reset email", e); return ResponseEntity.badRequest().body(Map.of( "success", false, "error", "Failed to send password reset email" )); } } @GetMapping("/public/health") public ResponseEntity<Map<String, String>> healthCheck() { return ResponseEntity.ok(Map.of("status", "OK", "service", "firebase-auth")); } // Request DTOs @Data public static class LoginRequest { private String idToken; } @Data public static class RegisterRequest { private String email; private String password; private String displayName; } @Data public static class RefreshTokenRequest { private String token; } @Data public static class EmailRequest { private String email; private String redirectUrl; } }

User Management Controller

// src/main/java/com/company/firebase/controller/UserController.java package com.company.firebase.controller; import com.company.firebase.model.FirebaseUser; import com.company.firebase.service.FirebaseAuthService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.*; import java.util.List; import java.util.Map; @RestController @RequestMapping("/api/users") @RequiredArgsConstructor @Slf4j public class UserController { private final FirebaseAuthService firebaseAuthService; @GetMapping("/me") public ResponseEntity<Map<String, Object>> getCurrentUser(@AuthenticationPrincipal FirebaseUser user) { try { FirebaseUser currentUser = firebaseAuthService.getUserById(user.getUid()); return ResponseEntity.ok(Map.of( "success", true, "user", Map.of( "uid", currentUser.getUid(), "email", currentUser.getEmail(), "displayName", currentUser.getDisplayName(), "emailVerified", currentUser.isEmailVerified(), "photoUrl", currentUser.getPhotoUrl(), "disabled", currentUser.isDisabled(), "metadata", Map.of( "createdAt", currentUser.getUserMetadata().getCreationTimestamp(), "lastLogin", currentUser.getUserMetadata().getLastSignInTimestamp() ) ) )); } catch (Exception e) { log.error("Failed to get current user", e); return ResponseEntity.badRequest().body(Map.of( "success", false, "error", "Failed to get user information" )); } } @PutMapping("/me") public ResponseEntity<Map<String, Object>> updateCurrentUser( @AuthenticationPrincipal FirebaseUser currentUser, @RequestBody UpdateUserRequest request) { try { FirebaseAuthService.UserUpdateRequest updateRequest = new FirebaseAuthService.UserUpdateRequest(); updateRequest.setDisplayName(request.getDisplayName()); updateRequest.setPhotoUrl(request.getPhotoUrl()); FirebaseUser updatedUser = firebaseAuthService.updateUser(currentUser.getUid(), updateRequest); return ResponseEntity.ok(Map.of( "success", true, "user", Map.of( "uid", updatedUser.getUid(), "displayName", updatedUser.getDisplayName(), "photoUrl", updatedUser.getPhotoUrl() ), "message", "User updated successfully" )); } catch (Exception e) { log.error("Failed to update user", e); return ResponseEntity.badRequest().body(Map.of( "success", false, "error", "Failed to update user" )); } } @PreAuthorize("hasRole('ADMIN')") @GetMapping("/{uid}") public ResponseEntity<Map<String, Object>> getUserById(@PathVariable String uid) { try { FirebaseUser user = firebaseAuthService.getUserById(uid); return ResponseEntity.ok(Map.of( "success", true, "user", createUserResponse(user) )); } catch (Exception e) { log.error("Failed to get user: {}", uid, e); return ResponseEntity.badRequest().body(Map.of( "success", false, "error", "User not found" )); } } @PreAuthorize("hasRole('ADMIN')") @GetMapping public ResponseEntity<Map<String, Object>> listUsers( @RequestParam(defaultValue = "100") int limit, @RequestParam(required = false) String pageToken) { try { List<FirebaseUser> users = firebaseAuthService.listUsers(limit, pageToken); List<Map<String, Object>> userResponses = users.stream() .map(this::createUserResponse) .toList(); return ResponseEntity.ok(Map.of( "success", true, "users", userResponses, "count", userResponses.size() )); } catch (Exception e) { log.error("Failed to list users", e); return ResponseEntity.badRequest().body(Map.of( "success", false, "error", "Failed to list users" )); } } @PreAuthorize("hasRole('ADMIN')") @PostMapping("/{uid}/claims") public ResponseEntity<Map<String, Object>> setCustomClaims( @PathVariable String uid, @RequestBody Map<String, Object> claims) { try { firebaseAuthService.setCustomClaims(uid, claims); return ResponseEntity.ok(Map.of( "success", true, "message", "Custom claims set successfully" )); } catch (Exception e) { log.error("Failed to set custom claims for user: {}", uid, e); return ResponseEntity.badRequest().body(Map.of( "success", false, "error", "Failed to set custom claims" )); } } @PreAuthorize("hasRole('ADMIN')") @PostMapping("/{uid}/disable") public ResponseEntity<Map<String, Object>> disableUser(@PathVariable String uid) { try { FirebaseAuthService.UserUpdateRequest updateRequest = new FirebaseAuthService.UserUpdateRequest(); updateRequest.setDisabled(true); firebaseAuthService.updateUser(uid, updateRequest); return ResponseEntity.ok(Map.of( "success", true, "message", "User disabled successfully" )); } catch (Exception e) { log.error("Failed to disable user: {}", uid, e); return ResponseEntity.badRequest().body(Map.of( "success", false, "error", "Failed to disable user" )); } } @PreAuthorize("hasRole('ADMIN')") @PostMapping("/{uid}/enable") public ResponseEntity<Map<String, Object>> enableUser(@PathVariable String uid) { try { FirebaseAuthService.UserUpdateRequest updateRequest = new FirebaseAuthService.UserUpdateRequest(); updateRequest.setDisabled(false); firebaseAuthService.updateUser(uid, updateRequest); return ResponseEntity.ok(Map.of( "success", true, "message", "User enabled successfully" )); } catch (Exception e) { log.error("Failed to enable user: {}", uid, e); return ResponseEntity.badRequest().body(Map.of( "success", false, "error", "Failed to enable user" )); } } @PreAuthorize("hasRole('ADMIN')") @DeleteMapping("/{uid}") public ResponseEntity<Map<String, Object>> deleteUser(@PathVariable String uid) { try { firebaseAuthService.deleteUser(uid); return ResponseEntity.ok(Map.of( "success", true, "message", "User deleted successfully" )); } catch (Exception e) { log.error("Failed to delete user: {}", uid, e); return ResponseEntity.badRequest().body(Map.of( "success", false, "error", "Failed to delete user" )); } } private Map<String, Object> createUserResponse(FirebaseUser user) { return Map.of( "uid", user.getUid(), "email", user.getEmail(), "displayName", user.getDisplayName(), "emailVerified", user.isEmailVerified(), "photoUrl", user.getPhotoUrl(), "disabled", user.isDisabled(), "customClaims", user.getCustomClaims(), "metadata", Map.of( "createdAt", user.getUserMetadata().getCreationTimestamp(), "lastLogin", user.getUserMetadata().getLastSignInTimestamp() ) ); } @Data public static class UpdateUserRequest { private String displayName; private String photoUrl; } }

Step 8: Application Configuration

Main Application Class

// src/main/java/com/company/firebase/FirebaseAuthApplication.java package com.company.firebase; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCaching; @SpringBootApplication @EnableCaching public class FirebaseAuthApplication { public static void main(String[] args) { SpringApplication.run(FirebaseAuthApplication.class, args); } }

Best Practices

  1. Security
  • Store Firebase service account credentials securely
  • Use environment variables for sensitive configuration
  • Implement proper CORS configuration
  • Use HTTPS in production
  1. Performance
  • Cache Firebase user information when appropriate
  • Use connection pooling for Firebase Admin SDK
  • Implement rate limiting for authentication endpoints
  1. Error Handling
  • Implement comprehensive error handling
  • Provide meaningful error messages to clients
  • Log authentication events for security monitoring
  1. Scalability
  • Use stateless authentication with JWT tokens
  • Implement token refresh mechanisms
  • Support multiple authentication providers

Conclusion

This Firebase Authentication implementation provides:

  • Complete Authentication: Email/password, social providers, and custom tokens
  • User Management: Create, read, update, and delete user accounts
  • Security Integration: Spring Security integration with Firebase tokens
  • Custom Claims: Role-based access control with custom claims
  • Token Management: JWT token generation and verification

Key features:

  1. Multiple Authentication Methods: Support for various Firebase auth providers
  2. Role-Based Access Control: Custom claims for fine-grained permissions
  3. Comprehensive User Management: Full CRUD operations for user accounts
  4. Security Integration: Seamless integration with Spring Security
  5. Scalable Architecture: Stateless JWT tokens for horizontal scaling

This solution provides a robust foundation for implementing Firebase Authentication in Java applications, suitable for both small projects and enterprise applications.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper