I have a Springboot REST application that I'm trying to secure with firebase, with a user authentication flow that goes like:
- Users authenticate on a separate Vue frontend using firebase auth, gets a Firebase JWT id token.
- Frontend sends this token in the auth header to my backend.
- My backend should then get the token, and authenticate by sending the token to the Firebase server.
- if the token is valid then return whatever resource the user was requesting, it not return a 401.
I have already configured the front end code, and am now sending the JWT token with every request. I'm trying to setup the backend but am running into some problems.
I have added the firebase SDK admin to my app, initialized the Firebaseapp, and implemented a HTTP filter to filter all requests to check and authenticate the token. I have been following this tutorial from firebase on authenticating tokens. For some reason all my requests come back with a 403? I'm not really sure whats going wrong with my implementation, as there are no errors to fix.
My pom xml snippet:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>com.google.firebase</groupId>
<artifactId>firebase-admin</artifactId>
<version>7.3.0</version>
</dependency>
<dependency>
<groupId>com.google.http-client</groupId>
<artifactId>google-http-client</artifactId>
<version>1.39.2</version>
</dependency>
My firebase class to initialize firebase SDK:
@Service
public class FirebaseInitialization {
@PostConstruct
public void initialization() {
try{
FileInputStream inputStream = new FileInputStream("src/main/resources/serviceAccountKey.json");
FirebaseOptions options = new FirebaseOptions.Builder()
.setCredentials(GoogleCredentials.fromStream(inputStream))
.build();
FirebaseApp.initializeApp(options);
}
catch (Exception error) {
error.printStackTrace();
}
}
}
My security config class:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ApplicationSecurityConfig extends WebSecurityConfigurerAdapter {
private static final String[] AUTH_WHITELIST = {"/publicEndpoint"};
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors().and()
.csrf().disable()
.addFilterAfter(new FireBaseTokenFilter(), FireBaseTokenFilter.class)
.sessionManagement().sessionCreationPolicy(STATELESS).and()
.authorizeRequests()
.antMatchers(AUTH_WHITELIST).permitAll()
.anyRequest().authenticated();
http.headers().frameOptions().disable();
}
}
My HTTP filter to check firebase token:
public class FireBaseTokenFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
String authenticationHeader = request.getHeader("Authorization");
//checks if token is there
if (authenticationHeader == null || !authenticationHeader.startsWith("Bearer "))
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED,"Missing token!");
FirebaseToken decodedToken = null;
try {
//Extracts token from header
String token = authenticationHeader.substring(7, authenticationHeader.length());
//verifies token to firebase server
decodedToken = FirebaseAuth.getInstance().verifyIdToken(token);
}
catch (FirebaseAuthException e) {
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED,"Error! "+e.toString());
}
//if token is invalid
if (decodedToken==null){
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED,"Invalid token!");
}
chain.doFilter(request,response);
}
}