FCM in Quarkus

I'm trying to integrate a push notification module in Keycloak and I'm having problems using the FCM service in quarkus. I want to use the FCM service for sending messages to mobile devices. I have the following code:

 

```

package org.keycloak.pushNotification.interfaces;

import com.google.auth.oauth2.GoogleCredentials;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.ApplicationScoped;;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;

@ApplicationScoped
public class FirebaseInitializer {
private String accessToken;

@PostConstruct
public void initialize() {
try {
InputStream serviceAccount = Thread.currentThread().getContextClassLoader().getResourceAsStream("/fcm.json");
if (serviceAccount == null) {
throw new FileNotFoundException("Could not find 'fcm.json' in the classpath");
}
GoogleCredentials googleCredentials = GoogleCredentials.fromStream(serviceAccount)
.createScoped(List.of("https://www.googleapis.com/auth/firebase.messaging"));
googleCredentials.refreshIfExpired();
accessToken = googleCredentials.getAccessToken().getTokenValue();
;
FirebaseOptions options = FirebaseOptions.builder()
.setCredentials(googleCredentials)
.build();

if (FirebaseApp.getApps().isEmpty()) {
System.out.println("S-a realizat initializarea");
FirebaseApp.initializeApp(options);
}
} catch (IOException e) {
throw new RuntimeException("Failed to initialize Firebase", e);
}
}

public String getAccessToken() {
return accessToken;
}
}

```

And here I have a specific Keycloak interface where I want to send messages to users:

 

```

package org.keycloak.pushNotification.interfaces;

import jakarta.json.Json;
import jakarta.json.JsonObject;
import jakarta.ws.rs.core.Response;
import org.keycloak.authentication.*;
import org.keycloak.credential.CredentialProvider;
import org.keycloak.models.UserModel;
import org.keycloak.pushNotification.model.PNCredentialModel;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.List;
import java.util.Random;

public class PNRequiredAction implements RequiredActionProvider, CredentialRegistrator {

FirebaseInitializer firebaseInitializer = new FirebaseInitializer();
public static final String PROVIDER_ID = "push_notification_config";

@Override
public void evaluateTriggers(RequiredActionContext context) {

}

@Override
public void requiredActionChallenge(RequiredActionContext context) {
UserModel user = context.getUser();
List<String> tokensFCM = user.getAttributes().get("tokenFCM");
String tokenFCM = tokensFCM.isEmpty() ? null : tokensFCM.get(0);
String code = generateCode();

if (tokensFCM != null) {
try {
firebaseInitializer.initialize();
String accessToken = firebaseInitializer.getAccessToken();
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI("https://fcm.googleapis.com/fcm/send"))
.header("Content-Type", "application/json")
.header("Authorization", "Bearer " + accessToken)
.POST(HttpRequest.BodyPublishers.ofString(buildMessage(tokenFCM, "Title", "Code: " + code)))
.build();

HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println("Access token: " + accessToken);
System.out.println("Response status: " + response.statusCode());
System.out.println("Response body: " + response.body());
} catch (Exception e) {
e.printStackTrace();
}
}

// if (tokenFCM != null) {
// Message message = Message.builder()
// .setToken(tokenFCM)
// .setNotification(Notification.builder()
// .setTitle("Autentificare noua")
// .setBody("Aici este codul tau: " + code)
// .build())
// .build();
//
// // LOGGER HERE
// try {
// String response = FirebaseMessaging.getInstance().send(message);
// System.out.println("Successfully sent message: " + response);
// } catch (Exception e) {
// System.err.println("Failed to send message: " + e.getMessage());
// return;
// }
// }

Response challenge = context.form()
.createForm("push-notification-form.ftl");
context.challenge(challenge);
}

private String buildMessage(String token, String title, String body) {
JsonObject message = Json.createObjectBuilder()
.add("to", token)
.add("notification", Json.createObjectBuilder()
.add("title", title)
.add("body", body))
.build();
return message.toString();
}

@Override
public void processAction(RequiredActionContext context) {
String answer = (context.getHttpRequest().getDecodedFormParameters().getFirst("code_access"));
PNCredentialProvider pn = (PNCredentialProvider) context.getSession().getProvider(CredentialProvider.class, "push-notification");
pn.createCredential(context.getRealm(), context.getUser(), PNCredentialModel.createPN());
context.success();
}

@Override
public void close() {

}

private String generateCode() {
Random random = new Random();
int code = 100000 + random.nextInt(900000);
return String.valueOf(code);
}
}

```

pom.xml:

```

<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>org.keycloak.pushNotification</groupId>
<artifactId>Push-Notification</artifactId>
<version>1.0-SNAPSHOT</version>
<name>Push Notification</name>
<properties>
<firebase-admin-sdk.version>9.2.0</firebase-admin-sdk.version>
<java.version>17</java.version>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<microprofile-jwt-auth-api.version>2.1</microprofile-jwt-auth-api.version>
<quarkus.version>3.8.2</quarkus.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-bom</artifactId>
<version>${quarkus.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>io.quarkus.platform</groupId>
<artifactId>quarkus-google-cloud-services-bom</artifactId>
<version>3.8.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-universe-bom</artifactId>
<version>${quarkus.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>24.0.1</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-server-spi-private</artifactId>
<version>24.0.1</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-server-spi</artifactId>
<version>24.0.1</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-services</artifactId>
<version>24.0.1</version>
</dependency>
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-core</artifactId>
<version>15.0.0.Final</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-infinispan</artifactId>
<version>24.0.1</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId>
</dependency>
<dependency>
<groupId>io.quarkiverse.googlecloudservices</groupId>
<artifactId>quarkus-google-cloud-firebase-admin</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>com.google.firebase</groupId>
<artifactId>firebase-admin</artifactId>
<version>${firebase-admin-sdk.version}</version>
</dependency>
<dependency>
<groupId>com.google.auth</groupId>
<artifactId>google-auth-library-credentials</artifactId>
<version>1.23.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-maven-plugin</artifactId>
<version>${quarkus.version}</version>
<executions>
<execution>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

```

And it keeps giving me this error at runtime:

2024-03-24 09:54:49,039 ERROR [org.keycloak.services.error.KeycloakErrorHandler] (executor-thread-1) Uncaught server error: java.lang.NoClassDefFoundError: com/google/auth/oauth2/GoogleCredentials
at org.keycloak.pushNotification.interfaces.FirebaseInitializer.initialize(FirebaseInitializer.java:25)
at org.keycloak.pushNotification.interfaces.PNRequiredAction.requiredActionChallenge(PNRequiredAction.java:37)
at org.keycloak.services.managers.AuthenticationManager.executeAction(AuthenticationManager.java:1275)
at org.keycloak.services.managers.AuthenticationManager.lambda$executionActions$15(AuthenticationManager.java:1222)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
at java.base/java.util.stream.SortedOps$RefSortingSink.end(SortedOps.java:400)
at java.base/java.util.stream.Sink$ChainedReference.end(Sink.java:258)
at java.base/java.util.stream.Sink$ChainedReference.end(Sink.java:258)
at java.base/java.util.stream.Sink$ChainedReference.end(Sink.java:258)
at java.base/java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:528)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:513)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
at java.base/java.util.stream.FindOps$FindOp.evaluateSequential(FindOps.java:150)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.findFirst(ReferencePipeline.java:647)
at org.keycloak.services.managers.AuthenticationManager.executionActions(AuthenticationManager.java:1223)
at org.keycloak.services.managers.AuthenticationManager.actionRequired(AuthenticationManager.java:1111)
at org.keycloak.services.managers.AuthenticationManager.nextActionAfterAuthentication(AuthenticationManager.java:958)
at org.keycloak.services.resources.LoginActionsService.processRequireAction(LoginActionsService.java:1079)
at org.keycloak.services.resources.LoginActionsService.requiredActionGET(LoginActionsService.java:1061)
at org.keycloak.services.resources.LoginActionsService$quarkusrestinvoker$requiredActionGET_900f1400af417d7ade6b5fdd106784903c8de34e.invoke(Unknown Source)
at org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:29)
at io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:141)
at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:147)
at io.quarkus.vertx.core.runtime.VertxCoreRecorder$14.runWith(VertxCoreRecorder.java:582)
at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2513)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1538)
at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:840)
Caused by: java.lang.ClassNotFoundException: com.google.auth.oauth2.GoogleCredentials
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525)
at io.quarkus.bootstrap.runner.RunnerClassLoader.loadClass(RunnerClassLoader.java:115)
at io.quarkus.bootstrap

If someone can help me, please explain what I'm doing wrong that I can't solve it.

 

 

1 1 221
1 REPLY 1

Hi @Bianca12334,

Welcome to Google Cloud Community!

Based on this GitHub issue, Firebase Cloud Messaging is not yet fully supported. You may either file a feature request or through the GitHub link I provided above.

You may also check these other documentations that you may find helpful and may act as an alternative for your current project:

Hope this helps.