Admin SDK token failure when subscribe to topic

985 views
Skip to first unread message

saitz.peter

unread,
Aug 10, 2018, 9:23:57 AM8/10/18
to Firebase Google Group
When we subscribe a device for a topic - called by a service in JBoss application server AS7.1 - the Admin SDK fails with "Required parameter is missing: response_type". This is the stack trace:

java.util.concurrent.ExecutionException: com.google.firebase.messaging.FirebaseMessagingException: Error while calling IID backend service
 at com
.google.firebase.tasks.Tasks.getResultOrThrowExecutionException(Tasks.java:179) [firebase-admin-5.11.0.jar:]
 at com
.google.firebase.tasks.Tasks.await(Tasks.java:109) [firebase-admin-5.11.0.jar:]
 at com
.google.firebase.internal.TaskToApiFuture.get(TaskToApiFuture.java:74) [firebase-admin-5.11.0.jar:]
 
.....
Caused by: com.google.firebase.messaging.FirebaseMessagingException: Error while calling IID backend service
 at com
.google.firebase.messaging.FirebaseMessaging.makeTopicManagementRequest(FirebaseMessaging.java:304) [firebase-admin-5.11.0.jar:]
 at com
.google.firebase.messaging.FirebaseMessaging.access$100(FirebaseMessaging.java:57) [firebase-admin-5.11.0.jar:]
 at com
.google.firebase.messaging.FirebaseMessaging$2.call(FirebaseMessaging.java:195) [firebase-admin-5.11.0.jar:]
 at com
.google.firebase.messaging.FirebaseMessaging$2.call(FirebaseMessaging.java:192) [firebase-admin-5.11.0.jar:]
 at com
.google.firebase.tasks.Tasks$1.run(Tasks.java:82) [firebase-admin-5.11.0.jar:]
 
.....
Caused by: java.io.IOException: Error getting access token for service account:
 at com
.google.auth.oauth2.ServiceAccountCredentials.refreshAccessToken(ServiceAccountCredentials.java:385) [google-auth-library-oauth2-http-0.9.0.jar:]
 at com
.google.auth.oauth2.OAuth2Credentials.refresh(OAuth2Credentials.java:175) [google-auth-library-oauth2-http-0.9.0.jar:]
 at com
.google.auth.oauth2.OAuth2Credentials.getRequestMetadata(OAuth2Credentials.java:161) [google-auth-library-oauth2-http-0.9.0.jar:]
 at com
.google.auth.http.HttpCredentialsAdapter.initialize(HttpCredentialsAdapter.java:96) [google-auth-library-oauth2-http-0.9.0.jar:]
 at com
.google.firebase.internal.FirebaseRequestInitializer.initialize(FirebaseRequestInitializer.java:46) [firebase-admin-5.11.0.jar:]
 at com
.google.api.client.http.HttpRequestFactory.buildRequest(HttpRequestFactory.java:93) [google-http-client-1.23.0.jar:1.23.0]
 at com
.google.api.client.http.HttpRequestFactory.buildPostRequest(HttpRequestFactory.java:133) [google-http-client-1.23.0.jar:1.23.0]
 at com
.google.firebase.messaging.FirebaseMessaging.makeTopicManagementRequest(FirebaseMessaging.java:289) [firebase-admin-5.11.0.jar:]
 
.....
Caused by: com.google.api.client.http.HttpResponseException: 400 Bad Request
<!DOCTYPE html><html .....
<p><b>Error: invalid_request</b></p>
<p id="errorDescription">Required parameter is missing: response_type</p>
....
<div id="request_info_header">Request Details
<ul id="request_info_items">
<li class="param_entry" id="param_entry_0">grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer</
li>
<li class="param_entry" id="param_entry_1">assertion=.....</li>
</ul>

....
 at com
.google.api.client.http.HttpRequest.execute(HttpRequest.java:1070) [google-http-client-1.23.0.jar:1.23.0]
 at com
.google.auth.oauth2.ServiceAccountCredentials.refreshAccessToken(ServiceAccountCredentials.java:383) [google-auth-library-oauth2-http-0.9.0.jar:]


When we do the same in a simple Java class, called from the main() method, it works fine.

Here the code (in principle)

  public static final void main(final String[] args) {
   
try {
     
final FirebaseApp fbApp = getFirebaseApp();
     
final int numRegs = createRegistration(fbApp);
     
if (numRegs > 0) {
       
final String id = sendNotification(fbApp, false);
       
System.out.println("Done! ID = " + id);
     
}
   
} catch (final Exception e) {
     
System.err.println("Failed! " + e.getMessage());
     
System.exit(-1);
   
}
 
}


 
private static int createRegistration(final FirebaseApp fbApp) throws InvalidAppConfigException, PushBrokerException {
   
try {
     
final StringBuilder errors = new StringBuilder();
     
final String regToken = TOKEN;
     
final ApiFuture<TopicManagementResponse> registerListener = FirebaseMessaging
       
.getInstance(fbApp)
       
.subscribeToTopicAsync(Collections.singletonList(regToken), TOPIC);
     
System.out.println("Sent registration for registration token " + regToken + " for topic " + TOPIC);
     
TopicManagementResponse response = registerListener.get(); // <-- here we crash in JBoss AS7.1
     
if (response != null) {
       
System.out.println("Received response for new registration : " + response.toString());
       
if (response.getErrors() != null && !response.getErrors().isEmpty()) {
         
for (final TopicManagementResponse.Error error : response.getErrors()) {
            errors
.append(errors.length() > 0 ? "\n" : "");
            errors
.append(error.getReason());
         
}
         
System.err.println(errors.toString());
       
}
     
} else {
       
System.err.println("Didn't receive a response for registration of registration token " + regToken
         
+ " for topic " + TOPIC);          
     
}
     
return response != null ? response.getSuccessCount() : 0;

   
} catch (final InterruptedException ie) {
     
System.err.println("Registration interrupted : " +  ie.getMessage());
     
return -1;
   
} catch (final ExecutionException ee) {
     
System.err.println("Failed to register : " +  ee.getMessage());
     
return -1;
   
}
 
}


 
private static FirebaseApp getFirebaseApp() {
   
final String pgwAppId = "try-out-app";

   
// get Firebase application
   
FirebaseApp fbApp = null;
   
try {
      fbApp
= FirebaseApp.getInstance(pgwAppId);
     
if (fbApp != null) { // already initialized?
       
return fbApp; // no need to initialize again
     
}
   
} catch (final IllegalStateException ise) {
   
}

   
// build Firebase credentials
   
ServiceAccountCredentials fbServiceCredentials = null;
   
try {
      fbServiceCredentials
= ServiceAccountCredentials.fromPkcs8(
       
"1166_and_so_on", // client ID
       
"firebase-ad...@my-project-name.iam.gserviceaccount.com", // client mail
       
"-----BEGIN PRIVATE KEY-----\n_and_so_on", // private key
       
"f239_and_so_on", // private key id
       
null, // scopes
       
null, // transport factory
       
new URI("https://accounts.google.com/o/oauth2/token"), // token url
       
null); // service acount user
   
} catch (final IOException ioe) {
     
System.err.println("Invalid private key");
     
return null;
   
} catch (final URISyntaxException use) {
     
System.err.println("Invalid token URI");
     
return null;
   
}

   
// build Firebase options
   
final FirebaseOptions fbOptions = new FirebaseOptions
     
.Builder()
     
.setConnectTimeout(10000)
     
.setReadTimeout(60000)
     
.setProjectId("my-project-name") // project id
     
.setCredentials(fbServiceCredentials)
     
.setDatabaseUrl("https://my-project-name.firebaseio.com") // database url
     
.build();

   
// initialize Firebase application
   
try {
      fbApp
= FirebaseApp.initializeApp(fbOptions, pgwAppId);
     
System.out.println("Firebase app initialized : " + fbApp);
   
} catch (final IllegalStateException ise1) {
     
System.err.println("Firebase app already initialized");
     
try {
       
return FirebaseApp.getInstance(pgwAppId);
     
} catch (final IllegalStateException ise2) {
       
System.err.println("Firebase app not initialized");
     
}
     
return null;
   
}
   
return fbApp;
 
}


 
private static String sendNotification(final FirebaseApp fbApp, final boolean dryRun) {
   
Message fbMessage = createMessage();
   
String response = null;
   
String id = null;
   
try {
     
final ApiFuture<String> sendListener = FirebaseMessaging.getInstance(fbApp).sendAsync(fbMessage, dryRun);
      response
= sendListener.get();
     
System.out.println("Sent push-notification : \n\t\tpayload = " + fbMessage.toString());

     
if (response != null && !response.isEmpty()) {
       
System.out.println("Received response for push-notification : " + response);
        id
= response.substring(response.lastIndexOf('/') + 1);
     
} else {
       
System.err.println("Didn't receive a response for push-notification");
     
}
   
} catch (final InterruptedException ie) {
     
System.err.println("Sending push-notification interrupted : " +  ie.getMessage());
     
return null;
   
} catch (final ExecutionException ee) {
     
System.err.println("Failed to send push-notification : " +  ee.getMessage());
     
return null;
   
}
   
return id;
 
}




Hiranya Jayathilaka

unread,
Aug 10, 2018, 1:13:54 PM8/10/18
to fireba...@googlegroups.com
The problem is with your credentials. You should be able to reproduce this by just running fbServiceCredentials.refreshAccessToken().

Please double check the arguments passed into fromPkcs8(), and if everything looks good file a bug at https://github.com/google/google-auth-library-java. You can also try passing a service account json stream to one of the fromStream() factory methods and see if that works.
--

Hiranya Jayathilaka | Software Engineer | h...@google.com | 650-203-0128

Hiranya Jayathilaka

unread,
Aug 10, 2018, 1:30:40 PM8/10/18
to fireba...@googlegroups.com
Oh I just noticed the bit about it working fine when testing outside JBoss. I wonder if this has something to do with the default transport picked by the Google Auth client. I also found this similar post on SO, where the root cause was an incorrect token server URI. You can try passing null for the URI and let the Auth client pick the right one.

saitz.peter

unread,
Aug 13, 2018, 12:57:37 PM8/13/18
to Firebase Google Group
Please double check the arguments passed into fromPkcs8()....

My fault. I passed the authentication URI "https://accounts.google.com/o/oauth2/auth" instead of the token URI "https://accounts.google.com/o/oauth2/token" as 7th parameter to ServiceAccountCredentials.fromPkcs8().
Reply all
Reply to author
Forward
0 new messages