Last active
April 15, 2024 09:45
-
-
Save proton5000/672b5e9e81193ff38509384f2e4f7fa2 to your computer and use it in GitHub Desktop.
firebase app check filter (spring boot 3)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package ua.varus.scan.and.go.config.security; | |
import com.nimbusds.jose.JOSEException; | |
import com.nimbusds.jose.jwk.JWK; | |
import com.nimbusds.jose.jwk.JWKSet; | |
import io.jsonwebtoken.Claims; | |
import io.jsonwebtoken.JwsHeader; | |
import io.jsonwebtoken.Jwts; | |
import jakarta.servlet.FilterChain; | |
import jakarta.servlet.ServletException; | |
import jakarta.servlet.http.HttpServletRequest; | |
import jakarta.servlet.http.HttpServletResponse; | |
import lombok.RequiredArgsConstructor; | |
import org.apache.commons.io.IOUtils; | |
import org.springframework.stereotype.Component; | |
import org.springframework.web.filter.OncePerRequestFilter; | |
import java.io.IOException; | |
import java.net.URL; | |
import java.nio.charset.Charset; | |
import java.text.ParseException; | |
import java.util.Date; | |
@Component | |
@RequiredArgsConstructor | |
public class AppCheckFilter extends OncePerRequestFilter { | |
private static final String PROJECT_NUMBER = "1050204332425"; | |
private static final String FIREBASE_APP_CHECK_URL = "https://firebaseappcheck.googleapis.com/v1/jwks"; | |
@Override | |
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) | |
throws ServletException, IOException { | |
String token = request.getHeader("HTTP_X_FIREBASE_APPCHECK"); | |
if (token == null) { | |
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthenticated"); | |
return; | |
} | |
String appId = checkToken(token); | |
if (appId == null) { | |
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthenticated"); | |
return; | |
} | |
request.setAttribute("firebase.app", appId); | |
filterChain.doFilter(request, response); | |
} | |
private String checkToken(String token) { | |
// Load JWKS keys from a file or URL | |
try { | |
// Not necessary to get the public keys each time, it can be cached for 6 hours, | |
// info from docs https://firebase.google.com/docs/app-check/custom-resource-backend#other | |
String jwksString = IOUtils.toString(new URL(FIREBASE_APP_CHECK_URL), Charset.defaultCharset()); | |
JWKSet jwks = JWKSet.parse(jwksString); | |
// Extract JWK with given kid | |
for (JWK jwk : jwks.toPublicJWKSet().getKeys()) {// Use JWK to verify JWT signature | |
try { | |
// If a getting claims not throw the exception it's like You can already trust your mobile application, | |
// it has a signature, but to clarify that it is your application, you can check the following statements | |
Claims claims = Jwts.parser() | |
.verifyWith(jwk.toRSAKey().toRSAPublicKey()) | |
.build() | |
.parseSignedClaims(token) | |
.getPayload(); | |
JwsHeader header = Jwts.parser() | |
.verifyWith(jwk.toRSAKey().toRSAPublicKey()) | |
.build() | |
.parseSignedClaims(token) | |
.getHeader(); | |
for (String claimName : claims.keySet()) { | |
System.out.println(claimName + ": " + claims.get(claimName)); | |
} | |
// Validate claims, you can choose what is better to validate for your app | |
if (!header.getAlgorithm().equals("RS256")) { | |
return null; | |
} | |
if (!header.getType().equals("JWT")) { | |
return null; | |
} | |
if (!claims.getIssuer().equals("https://firebaseappcheck.googleapis.com/#" + PROJECT_NUMBER)) { | |
return null; | |
} | |
if (claims.getExpiration().before(new Date())) { | |
return null; | |
} | |
if (!claims.getAudience().contains("projects/" + PROJECT_NUMBER)) { | |
return null; | |
} | |
System.out.println("header -> " + header.getAlgorithm()); | |
System.out.println("type -> " + header.getType()); | |
System.out.println("issuer -> " + claims.getIssuer()); | |
System.out.println("audience -> " + claims.getAudience().contains("projects/" + PROJECT_NUMBER)); | |
System.out.println("expiration -> " + claims.getExpiration().before(new Date())); | |
System.out.println("subject -> " + claims.getSubject()); | |
System.out.println("Signature is valid"); | |
return claims.getSubject(); | |
} catch (JOSEException e) { | |
System.out.println("mess_1 -> " + e.getMessage()); | |
System.out.println("Failed to verify signature: " + e.getMessage()); | |
} | |
} | |
} catch (IOException | ParseException e) { | |
System.out.println("mess_2 -> " + e.getMessage()); | |
} | |
return null; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<dependency> | |
<groupId>io.jsonwebtoken</groupId> | |
<artifactId>jjwt-api</artifactId> | |
<version>0.12.5</version> | |
</dependency> | |
<dependency> | |
<groupId>io.jsonwebtoken</groupId> | |
<artifactId>jjwt-impl</artifactId> | |
<version>0.12.5</version> | |
<scope>runtime</scope> | |
</dependency> | |
<dependency> | |
<groupId>io.jsonwebtoken</groupId> | |
<artifactId>jjwt-gson</artifactId> | |
<version>0.12.5</version> | |
</dependency> | |
<dependency> | |
<groupId>io.jsonwebtoken</groupId> | |
<artifactId>jjwt-jackson</artifactId> | |
<version>0.12.5</version> | |
<scope>runtime</scope> | |
</dependency> | |
<dependency> | |
<groupId>io.jsonwebtoken</groupId> | |
<artifactId>jjwt-orgjson</artifactId> | |
<version>0.12.5</version> | |
<scope>runtime</scope> | |
</dependency> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is how to Verify Firebase App Check tokens from a Java (spring boot 3) back end
https://firebase.google.com/docs/app-check/custom-resource-backend#other