Last active
June 23, 2021 20:09
-
-
Save jhannes/831cf19282576ff93ba25240b1f57c19 to your computer and use it in GitHub Desktop.
Firebase Cloud Messaging sender client in java
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
import org.jsonbuddy.JsonNode; | |
import org.jsonbuddy.JsonObject; | |
import java.io.FileInputStream; | |
import java.io.IOException; | |
import java.io.OutputStreamWriter; | |
import java.net.HttpURLConnection; | |
import java.net.URL; | |
import java.nio.charset.StandardCharsets; | |
import java.security.GeneralSecurityException; | |
import java.security.KeyFactory; | |
import java.security.NoSuchAlgorithmException; | |
import java.security.PrivateKey; | |
import java.security.Signature; | |
import java.security.spec.InvalidKeySpecException; | |
import java.security.spec.PKCS8EncodedKeySpec; | |
import java.util.Base64; | |
public class FcmSender { | |
private static final String MESSAGING_SCOPE = "https://www.googleapis.com/auth/firebase.messaging"; | |
private static final String[] SCOPES = {MESSAGING_SCOPE}; | |
private String projectId; | |
public String keyId; | |
public static Base64.Encoder base64Encoder = Base64.getUrlEncoder(); | |
private String clientEmail; | |
private PrivateKey privateKey; | |
public static void main(String[] args) throws IOException, GeneralSecurityException { | |
JsonObject serviceAccount = JsonObject.read(new FileInputStream("service-account.json")); | |
FcmSender fcmSender = new FcmSender(); | |
fcmSender.setClientEmail(serviceAccount.requiredString("client_email")); | |
fcmSender.setProjectId(serviceAccount.requiredString("project_id")); | |
fcmSender.setKeyId(serviceAccount.requiredString("private_key_id")); | |
fcmSender.setPrivateKey(serviceAccount.requiredString("private_key")); | |
String clientToken = "...."; | |
fcmSender.send(clientToken, "Hello", "Message"); | |
} | |
private void send(String clientToken, String title, String message) throws IOException, GeneralSecurityException { | |
HttpURLConnection connection = (HttpURLConnection) new URL("https://fcm.googleapis.com/v1/projects/" + projectId + "/messages:send").openConnection(); | |
connection.setRequestProperty("Authorization", "Bearer " + getAccessToken()); | |
connection.setRequestProperty("Content-Type", "application/json; UTF-8"); | |
connection.setDoOutput(true); | |
try (OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream(), StandardCharsets.UTF_8)) { | |
writer.write(createMessage(clientToken, title, message).toString()); | |
} | |
JsonObject response = JsonObject.read(connection); | |
System.out.println(response); | |
} | |
private JsonObject createMessage(String clientToken, String title, String body) { | |
return new JsonObject().put("message", new JsonObject() | |
.put("notification", new JsonObject() | |
.put("title", title) | |
.put("body", body)) | |
.put("token", clientToken)); | |
} | |
private String getAccessToken() throws IOException, GeneralSecurityException { | |
HttpURLConnection connection = (HttpURLConnection) new URL("https://oauth2.googleapis.com/token").openConnection(); | |
connection.setRequestMethod("POST"); | |
connection.setDoOutput(true); | |
connection.getOutputStream().write( | |
("grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer" + | |
"&assertion=" + getJwtAssertion()).getBytes(StandardCharsets.UTF_8)); | |
JsonObject tokenResponse = JsonObject.read(connection); | |
return tokenResponse.requiredString("access_token"); | |
} | |
private String getJwtAssertion() throws GeneralSecurityException { | |
JsonObject jwtHeader = new JsonObject() | |
.put("alg", "RS256") | |
.put("typ", "JWT") | |
.put("kid", keyId); | |
JsonObject jwtPayload = new JsonObject() | |
.put("aud", "https://oauth2.googleapis.com/token") | |
.put("iss", clientEmail) | |
.put("scope", String.join(" ", SCOPES)) | |
.put("iat", System.currentTimeMillis() / 1000) | |
.put("exp", System.currentTimeMillis() / 1000 + 3600); | |
return createSignedJwt(jwtHeader, jwtPayload, privateKey); | |
} | |
private static String createSignedJwt(JsonObject jwtHeader, JsonObject jwtPayload, PrivateKey privateKey) throws GeneralSecurityException { | |
String jwtContent = base64Encode(jwtHeader) + "." + base64Encode(jwtPayload); | |
Signature signature = Signature.getInstance("SHA256withRSA"); | |
signature.initSign(privateKey); | |
signature.update(jwtContent.getBytes()); | |
return jwtContent + "." + base64Encode(signature.sign()); | |
} | |
public void setProjectId(String projectId) { | |
this.projectId = projectId; | |
} | |
public void setClientEmail(String clientEmail) { | |
this.clientEmail = clientEmail; | |
} | |
public void setKeyId(String keyId) { | |
this.keyId = keyId; | |
} | |
private void setPrivateKey(String privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException { | |
privateKey = privateKey | |
.replace("-----BEGIN PRIVATE KEY-----", "") | |
.replace("-----END PRIVATE KEY-----", "") | |
.replace("\n", ""); | |
setPrivateKey(Base64.getDecoder().decode(privateKey)); | |
} | |
private void setPrivateKey(byte[] bytes) throws NoSuchAlgorithmException, InvalidKeySpecException { | |
setPrivateKey(KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(bytes))); | |
} | |
private void setPrivateKey(PrivateKey privateKey) { | |
this.privateKey = privateKey; | |
} | |
private static String base64Encode(JsonNode json) { | |
return base64Encode(json.toString().getBytes(StandardCharsets.UTF_8)); | |
} | |
private static String base64Encode(byte[] bytes) { | |
return base64Encoder.encodeToString(bytes); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment