/firebase-integration-spring-boot

Spring-boot application integrating with Firestore and Firebase Authentication to build a CRUD application.

Primary LanguageJavaThe UnlicenseUnlicense

Firebase Authentication and Firestore Integration in Spring Boot

A reference proof-of-concept that leverages Firestore Database to perform CRUD operations and Firebase Authentication with Spring-Security to authenticate users.
🛠 upgraded to Spring Boot 3 and Spring Security 6 🛠

Application Flow and Security Configuration

The project simulates a rudimentary Task Management Application.

API endpoints dealing with user account creation and login credentials validation are made public, by annotating their corresponding controller methods with custom annotation @PublicEndpoint. Requests to the configured API paths will not be evaluated by the Security filter with the logic being governed by ApiEndpointSecurityInspector.

Below is a sample controller method declared as public which will be exempted from authentication checks:

@PublicEndpoint
@PostMapping(value = "/login")
public ResponseEntity<TokenSuccessResponse> login(@RequestBody UserLoginRequest userLoginRequest) {
  final var response = userService.login(userLoginRequest);
  return ResponseEntity.ok(response);
}

API requests to private endpoints, handling CRUD operations on the Task entity are intercepted by the JwtAuthenticationFilter, which is added to the security filter chain and configured in the SecurityConfiguration. The custom filter holds the responsibility for verifying the authenticity of the incoming access token by communicating with the Firebase Authentication service and populating the security context.

Post successful authentication, AuthenticatedUserIdProvider is responsible for retrieving the authenticated user-id from the security context. This helps the Service layer maintain relationship between the current authenticated user and their corresponding Tasks. If an authenticated user attempts to perform any action on Tasks not owned by them, then the below API response is sent back to the client.

{
  "Status": "403 FORBIDDEN",
  "Description": "Access Denied: Insufficient privileges to perform this action."
}

In the event of authentication failure, when the access token received in the HTTP Request Headers is not valid, the below API response is sent back to the client.

{
  "Status": "401 UNAUTHORIZED",
  "Description": "Authentication failure: Token missing, invalid or expired"
}

The above JSON response is dispatched to the client as a result of CustomAuthenticationEntryPoint configured within the SecurityConfiguration which assumes any exception thrown by the Security filter is due to token verification failure. Hence, the implementation instantiates TokenVerificationException and delegates the responsibility of exception handling to ExceptionResponseHandler.


Local Setup

To run the application locally, ensure you have the following prerequisites:

  • A private key associated with the service account to establish a connection with Firebase.
  • The Web API key of the Firebase project you've created to invoke the Firebase Authentication REST API.
  • The created Firebase Authentication service has the Email/Password native sign-in provider enabled.

Create a file named private-key.json in the base directory and paste the contents of the service account's private key into this file.

Execute the following commands in the project's base directory to build the application image and start the backend application container:

FIREBASE_PRIVATE_KEY=$(cat private-key.json)
FIREBASE_WEB_API_KEY=your-web-api-key-here
sudo docker-compose build
sudo FIREBASE_PRIVATE_KEY="$FIREBASE_PRIVATE_KEY" FIREBASE_WEB_API_KEY="$FIREBASE_WEB_API_KEY" docker-compose up -d

To remove the environment variables from memory after the application has started, the below commands can be executed

unset FIREBASE_PRIVATE_KEY
unset FIREBASE_WEB_API_KEY

Visual Walkthrough

firebase-auth-and-firestore-integration-spring-boot.mov