Reputation: 1619
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
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