With the recent release of FirebaseInstanceId and FirebaseCloudMessaging (21.0.0) Firebase has deprecated iid package and both getToken() and getId() methods are now deprecated.
According to the Firebase release note the method getToken() is moved to FirebaseMessaging
Before:
FirebaseInstanceId.getInstance().getToken()
After:
FirebaseMessaging.getInstance().getToken()
Which gives use the fcmToken, but to retrieve instance id, there's no method available in FirebaseMessaging nor FirebaseInstanceId.
So, Is instance_id considered a useless id and should no longer be used? or is there a replacement for this?
FirebaseInstanceId class is deprecated, to get token use FirebaseMessagingClass. The token can be generated using the following code:
FirebaseMessaging.getInstance().getToken()
.addOnCompleteListener(new OnCompleteListener<String>() {
#Override
public void onComplete(#NonNull Task<String> task) {
if (!task.isSuccessful()) {
Log.w(TAG, "Fetching FCM registration token failed", task.getException());
return;
}
// Get new FCM registration token
String token = task.getResult();
// Log and toast
String msg = getString(R.string.msg_token_fmt, token);
Log.d(TAG, msg);
Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show();
}
});
Regarding the Firebase InstanceId, this is what the official document says:
public Task getInstanceId () ->
This method is deprecated.
For an instance identifier, use FirebaseInstallations.getId() instead. For an FCM registration token, use FirebaseMessaging.getToken() instead.
Fcm Token
Before deprecation
val fcmToken = FirebaseInstanceId.getInstance().getToken()
Replacement
val fcmToken = FirebaseMessaging.getInstance().getToken()
FirebaseInstanceId#getId
Before deprecation
val istanceId = FirebaseInstanceId.getInstance().getId()
Replacement
Checking out the code of FirebaseInstanceId#getId() I saw the suggestion that you should use FirebaseInstallations#getId instead.
This method is deprecated
Use FirebaseInstallations.getId() instead.
val instanceId = FirebaseInstallation.getInstance().getId()
FCM Token:
Use firebase_messaging package
String? token = await FirebaseMessaging.instance.getToken();
Installation ID:
Use flutterfire_installations package
String id = await FirebaseInstallations.instance.getId();
Installation auth token:
String token = await FirebaseInstallations.instance.getToken();
try this one
public String getToken() {
String token;
FirebaseMessaging.getInstance().getToken()
.addOnCompleteListener(new OnCompleteListener<String>() {
#Override
public void onComplete(#NonNull Task<String> task) {
if (task.isSuccessful()) {
token = task.getResult();
}
}
});
return token;
}
Related
I am trying to authenticate an user with a custom token using cloud functions. The code for the token generation is:
export const test = functions.https.onCall(() => {
const uid = 'test_uid'
admin.auth().createCustomToken(uid)
.then((customtoken) => {
console.log(customtoken)
return customtoken
}).catch((error) => {
console.log(error)
})
})
The code on the client side is:
private void getmessage() {
FirebaseFunctions.getInstance()
.getHttpsCallable("test")
.call()
.addOnCompleteListener(this, new OnCompleteListener<HttpsCallableResult>() {
#Override
public void onComplete(#NonNull Task<HttpsCallableResult> task) {
if(task.isSuccessful()){
Toast.makeText(getApplicationContext(), task.getResult().getData().toString(), Toast.LENGTH_LONG).show();
}else{
Toast.makeText(getApplicationContext(), "Task is NOT Successful", Toast.LENGTH_LONG).show();
}
}
});
}
The token is successfully logged in the console, but returns null value on the client side. Is there something which I am doing wrong?
A callable function needs to return a promise that resolves when the async work is complete. That promise should resolve with the data to send to the client. Right now, your function is returning nothing.
Try this instead:
return admin.auth()
.createCustomToken(...)
.then(...)
.catch(...)
implementation 'com.google.firebase:firebase-config:11.8.0'
FirebaseRemoteConfig mFirebaseRemoteConfig = FirebaseRemoteConfig.getInstance();
FirebaseRemoteConfigSettings configSettings = new FirebaseRemoteConfigSettings.Builder()
.setDeveloperModeEnabled(BuildConfig.DEBUG)
.build();
mFirebaseRemoteConfig.setConfigSettings(configSettings);
mFirebaseRemoteConfig.fetch(0).addOnCompleteListener(this, new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
mFirebaseRemoteConfig.activateFetched();
String appDefaultColor = mFirebaseRemoteConfig.getString(FIREBASE_REMOTE_CONFIG_DEFAULT_COLOR);
if (appDefaultColor != null && appDefaultColor.length() > 0) {
System.out.println("==== appDefaultColor : " + appDefaultColor);
}
}
}
});
public static String FIREBASE_REMOTE_CONFIG_DEFAULT_COLOR = "project_default_theme_color";
here is my implementation of Firebase remote config.
As above my code explanation, project4_default_theme_color, i get the value from firebase, But the situation is that i change that value from Firebase remote config , but i did't get.
My firebase remote config Key project_default_theme_color and value is #f04030 and Publish Changes.is any wrong in this?
Follow below instruction to resolve this issue
Update your firebase library version
implementation 'com.google.firebase:firebase-config:19.1.0'
implementation 'com.google.firebase:firebase-core:17.2.1'
Initialize FirebaseRemoteConfig
FirebaseRemoteConfig firebaseRemoteConfig = FirebaseRemoteConfig.getInstance();
Set firebaseRemoteConfig parameter default value
firebaseRemoteConfig.setDefaultsAsync(R.xml.remote_config_defaults);
Add below code in remote_config_defaults.xml
<?xml version="1.0" encoding="utf-8"?>
<defaultsMap>
<entry>
<key>your_key</key>
<value>defaultValue</value>
</entry>
Add this code in your java file
firebaseRemoteConfig.fetch(cacheTimeDuration)
.addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
String errorString = "";
if (task.isSuccessful()) {
firebaseRemoteConfig.activate();
errorString = " task is successful ";
} else {
errorString = "task is canceled";
}
Log.i(TAG, "onComplete: error " + errorString);
Log.i(TAG, " Get firebase remote config value " + firebaseRemoteConfig.getString("your_key"));
}
});
Note :
If your Apk is debug then use this method .fetch(cacheTimeDuration)
If your Apk is Release then use this method .fetch()
I made a function in my Cloud Functions to verify a purchase signature. It must return a boolean and a string value. I read on Firebase Cloud Functions documentation that I should return a JSON structure that contains all the values. I made it like this.
EDIT
This is my entire Cloud Functions:
const BASE_64_ENCODED_PUBLIC_KEY = "MY_PUBLIC_KEY_HERE"
const KEY_FACTORY_ALGORITHM = "RSA-SHA1";
// The Cloud Functions for Firebase SDK to create Cloud Functions and setup triggers.
const functions = require('firebase-functions');
const crypto = require('crypto');
exports.verifySignature = functions.https.onCall((data, context) => {
const purchaseJSON = data.signedData;
const signature = data.signature;
console.log("start verification");
if (purchaseJSON === null || signature === null) {
console.log("Purchase verification failed: missing data.");
return {
message: "missing data",
verified: false
}
}
const verifier = crypto.createVerify(KEY_FACTORY_ALGORITHM);
verifier.update(purchaseJSON);
if (verifier.verify(publicKey, signature, "base64")){
console.log("signature verification success!");
return {
message: "verification success",
verified: true
}
} else {
console.log("signature verification failed!");
return {
message: "verification failed",
verified: false
};
}
});
And this is my code on the client:
private Task<String> verifyValidSignature(String signedData, String signature) {
// Create the arguments to the callable function.
Map<String, Object> data = new HashMap<>();
data.put("signedData", signedData);
data.put("signature", signature);
return mFunctions.getHttpsCallable("verifySignature")
.call(data)
.continueWith(new Continuation<HttpsCallableResult, String>() {
#Override
public String then(#NonNull Task<HttpsCallableResult> task) throws Exception {
HttpsCallableResult result = task.getResult();
if (result != null) {
return result.getData().toString();
}
return null;
}
});
}
How can I get the message value and convert it to a string and the verified value and convert it to a boolean in Android/Java?
result.getData() is returning a Map type object, because you returned an object from the function. JavaScript objects become Java Maps. You just use it like you would any other Map.
Map<String, Object> data = (Map<String, Object>) result.getData();
String message = (String) data.get("message");
boolean verified = (Boolean) data.get("verified");
What you are doing is ok, the only thing missing is to promosify it, this would do the trick
return Promise.resolve({ message: “verification success”, verified: true })
Similar for the error case but instead of using Promise.resolve you will use Promise.reject
I have followed the link and have done the configuration on the server as mentioned.
"/users":
post:
description: "<Description>"
operationId: "<OperationID>"
produces:
- "application/json"
responses:
200:
description: "user List"
schema:
$ref: "#/definitions/echoMessage"
parameters:
- description: "Search Criteria"
in: body
name: message
required: true
schema:
$ref: "#/definitions/echoMessage"
security:
- firebase: []
and
firebase:
authorizationUrl: ""
flow: "implicit"
type: "oauth2"
x-google-issuer: "https://securetoken.google.com/<Project-ID>"
x-google-jwks_uri: "https://www.googleapis.com/service_accounts/v1/metadata/x509/securetoken#system.gserviceaccount.com"
And after going through JWT standards I came to know that while calling calling the service we have to add Authorization header with Bearer so I have added the header as follows,
Authorization: Bearer
I initially tried with
String token = FirebaseInstanceId.getInstance().getToken();
But it gave error so I tried with,
FirebaseUser firebaseUser = FirebaseAuth.getInstance().getCurrentUser();
if (firebaseUser != null) {
firebaseUser.getIdToken(true)
.addOnSuccessListener(new OnSuccessListener<GetTokenResult>() {
#Override
public void onSuccess(GetTokenResult getTokenResult) {
String token = getTokenResult.getToken();
SharedPreferences.Editor editor = mSharedPreferences.edit();
editor.putString(Constants.PREFS_FCM_TOKEN, token);
editor.apply();
}
});
}
But even with both codes I am getting error as 401 and invalid_token
After so many days of struggle I am able to solve the issue.
I solved the issue by following this,
"/users":
post:
description: "<Description>"
operationId: "<OperationID>"
produces:
- "application/json"
responses:
200:
description: "user List"
schema:
$ref: "#/definitions/echoMessage"
parameters:
- description: "Search Criteria"
in: body
name: message
required: true
schema:
$ref: "#/definitions/echoMessage"
security:
- firebase: []
and
firebase:
authorizationUrl: ""
flow: "implicit"
type: "oauth2"
x-google-issuer: "https://securetoken.google.com/<Project-ID>"
x-google-jwks_uri: "https://www.googleapis.com/service_accounts/v1/metadata/x509/securetoken#system.gserviceaccount.com"
x-google-audiences: "<Project-ID>" //I have added this, this was the main culprit.
as mentioned in the comment, I was missing
x-google-audiences: ""
in the server configuration.
And for another clarification for which token to use: We have to use the second approach that I have mentioned in the question, i.e, as below,
FirebaseUser firebaseUser = FirebaseAuth.getInstance().getCurrentUser();
if (firebaseUser != null) {
firebaseUser.getIdToken(true)
.addOnSuccessListener(new OnSuccessListener<GetTokenResult>() {
#Override
public void onSuccess(GetTokenResult getTokenResult) {
String token = getTokenResult.getToken();
SharedPreferences.Editor editor = mSharedPreferences.edit();
editor.putString(Constants.PREFS_FCM_TOKEN, token);
editor.apply();
}
});
}
Is there any way to unsubscribe from all topics at once?
I'm using Firebase Messaging to receive push notification from some topics subscribed, and somehow I need to unsubscribe from all topics without unsubscribing one by one. Is that possible?
You can use Instance API to query all the available topics subscribed to given token and than call multiple request to unsubscribe from all the topics.
However, if you want to stop receiving from all the topics and then the token is not useful at all, you can call FirebaseInstanceId.getInstance().deleteInstanceId() (reference: deleteInstanceId() and surround with a try/catch for a potential IOException) that will reset the instance id and than again you can subscribe to new topics from the new instance id and token.
Hope this helps someone.
If you want to avoid deleting InstanceId and moreover, to avoid missing some of the subscribed topics when saved in a local database or a remote database due to highly probable buggy implementation.
First get all subscribed topics:
var options = BaseOptions(headers: {'Authorization':'key = YOUR_KEY'});
var dio = Dio(options);
var response = await dio.get('https://iid.googleapis.com/iid/info/' + token,
queryParameters: {'details': true});
Map<String, dynamic> subscribedTopics = response.data['rel']['topics'];
Get your key here:
Firebase console -> your project -> project settings -> cloud messaging -> server key
Get your token as:
var firebaseMessaging = FirebaseMessaging();
String token;
firebaseMessaging.getToken().then((value) {
token = value;
});
Now unsubscribe from all topics:
Future<void> unsubscribeFromAllTopics() async {
for (var entry in subscribedTopics.entries) {
await Future.delayed(Duration(milliseconds: 100)); // throttle due to 3000 QPS limit
unawaited(firebaseMessaging.unsubscribeFromTopic(entry.key)); // import pedantic for unawaited
debugPrint('Unsubscribed from: ' + entry.key);
}
}
All code is in Dart.
For more information about instance id:
https://developers.google.com/instance-id/reference/server
For Java users:
If you want to do it topic wise, refer others answers and If you want to stop recieving FCM push notification, do below:
new Thread(new Runnable() {
#Override
public void run() {
try {
FirebaseInstanceId.getInstance().deleteInstanceId();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
I have placed deleteInstanceId() in a separate thread to stop java.io.IOException: MAIN_THREAD W/System.err and wrapped with try / catch to handle IOException.
I know this is not the best way but it works!
You can store list of all topics in Database and then unsubscribe from all topics when user sign-outs
final FirebaseMessaging messaging= FirebaseMessaging.getInstance();
FirebaseDatabase.getInstance().getReference().child("topics").addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
String topic = dataSnapshot.getValue(String.class);
messaging.unsubscribeFromTopic(topic);
}...//rest code
Keep a private list of subscribed topics in Preferences.
It's not that hard. Here's what I do:
public class PushMessagingSubscription {
private static SharedPreferences topics;
public static void init(ApplicationSingleton applicationSingleton) {
topics = applicationSingleton.getSharedPreferences("pushMessagingSubscription", 0);
}
public static void subscribeTopic(String topic) {
if (topics.contains(topic)) return; // Don't re-subscribe
topics.edit().putBoolean(topic, true).apply();
// Go on and subscribe ...
}
public static void unsubscribeAllTopics() {
for (String topic : topics.getAll().keySet()) {
FirebaseMessaging.getInstance().unsubscribeFromTopic(topic);
}
topics.edit().clear().apply();
// FirebaseInstanceId.getInstance().deleteInstanceId();
}
}
We can use the unsubcribeAllTopics in server side as below.
Example,
interface GetTopics {
rel: {topics: {[key: string]: any}}
}
/**
* Unsubcribe all topics except one topic
*
* Example response of `https://iid.googleapis.com/iid/info/${fcmToken}?details=true`
* {
"applicationVersion": "...",
"application": "...",
"scope": "*",
"authorizedEntity": "...",
"rel": {
"topics": {
"TOPIC_KEY_STRING": { // topic key
"addDate": "2020-12-23"
}
}
},
"appSigner": "...",
"platform": "ANDROID"
}
*/
export const unsubcribeAllTopics = async (
fcmTokens: string | string[],
exceptTopic?: string,
) => {
const headers = {
'Content-Type': 'application/json',
Authorization: `key=${process.env.FCM_SERVER_KEY}`,
}
const url = `https://iid.googleapis.com/iid/info/${fcmTokens}?details=true`
try {
const response = await fetch(url, {method: 'GET', headers: headers})
const result: GetTopics = await response.json()
const keys = Object.keys(result.rel.topics)
keys.forEach(key => {
key !== exceptTopic &&
messaging()
.unsubscribeFromTopic(fcmTokens, key)
.catch(error => {
console.error('error', {data: error})
})
})
} catch (error) {
console.error('error', {data: error})
}
}
https://gist.github.com/JeffGuKang/62c280356b5632ccbb6cf146e2bc4b9d
if you are using flutter if you want to unsubscribe from all topics use-
final FirebaseMessaging _fcm = FirebaseMessaging();
_fcm.deleteInstanceID().then((value){
print('deleted all'+value.toString());
});
Firebase.messaging.deleteToken()
Is actual answer for 2021.
try {
FirebaseInstallations.getInstance().delete()
} catch (e: IOException) {
}