finisinfinitatis
finisinfinitatis

Reputation: 1619

Authenticate users with Firebase Id Token in Spring Boot

I am currently trying to authenticate users using their Firebase Id token which I send via the Authorization header. However I always get a HTTP 403 Error for each request with any method. The token I use is valid since the method verifyIdToken(idToken) throws no exception when debugging this, so I guess this issue is more related to Spring Security than to Firebase.

What could be the reason for this?

My code for the Firebase filter I built:

@Component
class FirebaseFilter : OncePerRequestFilter() {
    override fun doFilterInternal(request: HttpServletRequest,
                                  response: HttpServletResponse,
                                  filterChain: FilterChain) {
        val authToken = request.getHeader(AUTH_HEADER).substring(7)
        if (authToken!="") {
            SecurityContextHolder.getContext().authentication = getAuthentication(authToken)
            logger.debug("Successfully Authenticated")
        }
        filterChain.doFilter(request, response)
    }

    private fun verifyIdToken(idToken: String): FirebaseToken {
        require(idToken!="") { "idToken is blank" }
        return FirebaseAuth.getInstance().verifyIdToken(idToken)
    }

    private fun getAuthentication(idToken: String): UsernamePasswordAuthenticationToken {

        val token = verifyIdToken(idToken)
        return UsernamePasswordAuthenticationToken(token.uid,token)
    }

    companion object {
        private const val AUTH_HEADER = "Authorization"
    }
}

And the code for the WebSecurityConfigurerAdapter:

@EnableWebSecurity
@Configuration
class HttpConfig : WebSecurityConfigurerAdapter() {


    @Autowired
    lateinit var firebaseFilter: FirebaseFilter

    override fun configure(http: HttpSecurity) {
        http.cors().and().csrf().disable()
                .authorizeRequests()
                .anyRequest().authenticated().and()
                .addFilterBefore(firebaseFilter, UsernamePasswordAuthenticationFilter::class.java)
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
    }
}

Upvotes: 2

Views: 1735

Answers (1)

finisinfinitatis
finisinfinitatis

Reputation: 1619

Ok I found a solution and got an idea what I did wrong. As authentication I returned a UsernamePasswordAuthenticationToken which was not correct in my case. Instead I subclassed AbstractAuthenticationToken like this and used it to return it in the filter:

class FireBaseAuthenticationToken(val idToken: String): AbstractAuthenticationToken(null) {

    override fun getCredentials(): Any {
        TODO("Not yet implemented")
    }

    override fun getPrincipal(): Any {
        TODO("Not yet implemented")
    }
}

Also adding an AuthenticationToken alone is not sufficient since Spring needs a way to authenticate this token. Usually Spring iterates through the list of available AuthenticationProviders for this case. But since no suitable provider is available by default I had to implement my own as it follows:

@Component
class FireBaseTokenAuthenticationProvider : AuthenticationProvider {
    override fun authenticate(authentication: Authentication?): Authentication {
        val fireBaseAuthToken = authentication as FireBaseAuthenticationToken
        val fireBaseToken = FirebaseAuth.getInstance().verifyIdToken(fireBaseAuthToken.idToken)
        val userRecord = FirebaseAuth.getInstance().getUser(fireBaseToken.uid)
        val user = FirebaseUser(userRecord)
        println("user authenticated")
        return user
    }

    override fun supports(authentication: Class<*>?): Boolean {
        return authentication!!.isAssignableFrom(FireBaseAuthenticationToken::class.java)
    }
}

Now everything works fine! :)

Upvotes: 1

Related Questions