You can get an ID token (JWT) when you log in using Firebase Authentication. For example, when performing login authentication with SPA, you may want to log in using Firebase with a client (JavaScript), send the acquired token to the server, and link the uid in JWT with the user managed by the application. I think there is.
This time, I will mainly write the verification method of JWT on the server side (java + SpringBoot).
This time I will use Nuxt. How to get and send tokens.
Authentication process (excerpt)
async signin() {
try {
//Authenticate using email address and password
await firebase.auth().signInWithEmailAndPassword(this.email, this.password)
//Get ID token (JWT)
const token = await firebase.auth().currentUser.getIdToken(true)
//Save to local storage
localStorage.setItem('token', token)
//Transition to the page after authentication
this.$router.push('/')
} catch (e) {
//At the time of authentication error, token acquisition error
console.log(e)
}
}
~/plugins/axios.js
export default function ({ $axios }) {
$axios.onRequest(config => {
const token = localStorage.getItem('token');
if (token) {
//If there is a token in the local storage, give it to the request header (Authorization)
config.headers.common['Authorization'] = "Bearer " + token;
}
})
}
Use Spring Security's OnecePerRequestFilter.
In addition, the following libraries are used.
Library name | Use |
---|---|
OkHttp | HTTP client |
jjwt | JWT verification |
LoginFilter.java
@Component
public class LoginFilter extends OncePerRequestFilter {
//Register ObjectMapper as a bean in advance.
@Autowired
ObjectMapper objectMapper;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
//Set login user information in context
SecurityContextHolder.getContext().setAuthentication(new PreAuthenticatedAuthenticationToken(
auth(request), null));
filterChain.doFilter(request, response);
}
//Get login user information
private LoginUser auth(HttpServletRequest request) {
//Extract JWT from request header
String token = getToken(request);
try {
//Verify JWT, get complaints
//If the validation fails, an exception is thrown.
Jws<Claims> claim = Jwts.parser()
.setSigningKeyResolver(new GoogleSigningKeyResolver()) //Need your own resolver
.parseClaimsJws(token);
//Get the uid from the body part of the claim
String uid = (String)claim.getBody().get("user_id");
//Get uid and search user information
User user = userService.findByUid(uid);
if(user == null){
throw new BadCredentialsException("User does not exist");
}
return new LoginUser(user);
} catch (Exception e) {
throw new BadCredentialsException("The token is invalid");
}
}
//Get the token from the request header.
private String getToken(HttpServletRequest request) {
String token = request.getHeader("Authorization");
if (token == null || !token.startsWith("Bearer ")) {
return null;
}
return token.substring("Bearer ".length());
}
/**
*We will return the public key used for signing.
*
* @author h.ono
*
*/
public class GoogleSigningKeyResolver extends SigningKeyResolverAdapter {
@Override
public Key resolveSigningKey(JwsHeader jwsHeader, Claims claims) {
try {
Map<String, Object> map = getJwks();
if (map.isEmpty()) {
return null;
}
String keyValue = (String) map.get(jwsHeader.getKeyId());
if (keyValue == null) {
return null;
}
//Important point
//Remove the start (BEGIN) and end (END) labels.
keyValue = keyValue
.replaceAll("-----BEGIN CERTIFICATE-----\n", "")
.replaceAll("-----END CERTIFICATE-----\n", "");
InputStream in = new ByteArrayInputStream(Base64.decodeBase64(keyValue.getBytes("UTF-8")));
X509Certificate certificate = (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate(in);
return certificate.getPublicKey();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
*Get JWKS from Google.
*
* @return JWKS
*/
private Map<String, Object> getJwks() {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://www.googleapis.com/robot/v1/metadata/x509/[email protected]")
.build();
try (Response response = client.newCall(request).execute()) {
return objectMapper.readValue(response.body().string(), new TypeReference<Map<String, Object>>() {
});
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
I was able to associate the UID obtained from Firebase with the user who has the app. I did a lot of research, but most of the authentication was completed on the client side, so I had a hard time arriving at a method like this one. .. ..
When verifying JWT, I also tried java-jwt provided by auth0, but when getting the public key, I couldn't get it because it doesn't support X.509 format.
Recommended Posts