I've been working with Azure Notification Hub the past few days, trying to set up push notifications in ASP NET and Dart/Kotlin. I've been struggling with FCM/PNS tokens.
When I register my app, I get this token: ddGYUP9OSdi2YR9Y****** using * just in case.
At one point in development, I found I had a registration associated with Hubs with the token: ddGYUP9OSdi2YR9Y******:APA91bMANCn_SZQV8bUJCWOiyPzdXaBPrqLmqIk8ELj6RfCx5TKNR2hLmiNMfuyK7LdY70-BtMxxyRbituhPH2t5v9p0A-8qkCleEgOWi4cXcvKpxedW2QmqEmym-hk8oZOXdx-*****
It's the same token, but with something added after the semi colon. What is this, and where does it come from?
I get the first token from FirebaseInstallations.getInstance().id, and with every device I register with the tokens are a similar length. However in my ASP NET project, sending a notification to a device only works with the longer token. When I test a notification using the Firebase Console: Firebase - Engage - Cloud Messaging - Compose Notification, only the long one works. Which leads me to believe there's a problem in my registration code.
So what is that extra stuff after the colon on the short token?
My code for getting the FCM token for those interested.
private fun getDeviceToken() : String {
if(!playServicesAvailable) {
throw Exception(getPlayServicesError())
}
val token = PushNotificationsFirebaseMessagingService.token
if (token.isNullOrBlank()) {
throw Exception("Unable to resolve token for FCM.")
}
return token
}
The assumption, that the token string would end at the colon, is just wrong ...
That PushNotificationsFirebaseMessagingService simply returns an invalid token
and the question doesn't have any PushNotificationsFirebaseMessagingService.
if (token.isNullOrBlank() || !token.contains(":")) {
throw IllegalArgumentException("bad or no token given")
}
Found the problem. A microsoft doc had
FirebaseInstallations
.getInstance()
.id
.addOnCompleteListener(OnCompleteListener { task ->
if (!task.isSuccessful) {
return#OnCompleteListener
}
PushNotificationsFirebaseMessagingService.token = task.result
PushNotificationsFirebaseMessagingService.notificationRegistrationService?.refreshRegistration()
})
In on create. So that 'token' was just the installation Id.
The correct code for the token, as shown in Firebase docs is
FirebaseMessaging.getInstance().token.addOnCompleteListener(OnCompleteListener { task ->
if(!task.isSuccessful) {
print("Fetching FCM registration token failed")
return#OnCompleteListener
}
val token = task.result
PushNotificationsFirebaseMessagingService.token = token;
})
Related
I'm trying to generate a new messaging token but it seems to generate the same one, over and over again.
I tried both
FirebaseMessaging.getInstance().deleteToken().addOnCompleteListener {
Log.d("Notifications", "Token Deleted!!!")
}
and
FirebaseInstallations.getInstance().delete().addOnCompleteListener {
Log.d("Notifications", "Token Deleted!!!")
}
Neither seem to trigger the
override fun onNewToken(refreshedToken: String) {}
of the service. And each time i query for the current token i get the same one.
What am i doing wrong?
I am not exactly sure if you can delete the generated token to get a new one. However, as per documentation a token is generated once on app's initial startup.
If you want a new token - simply uninstall the app and install it once again. Then Firebase will generate a new token.
I was having the same issue when trying to log out users.
I would recommend using the depreciated method below:
Thread(Runnable {
Thread.sleep(1000)
FirebaseInstanceId.getInstance().deleteInstanceId()
}).start()
This will not call onNewToken, however, if you now call..
FirebaseMessaging.getInstance().token.addOnCompleteListener { -> task
// get the result here
}
Result should be a new token
I want to verify the reCAPTCHA of my Android user. So I'm reading this documentation: https://developers.google.com/recaptcha/docs/verify:
For Android library users, you can call the SafetyNetApi.RecaptchaTokenResult.getTokenResult() method to get response token if the status returns successful.
In the manual of this function, the following description is written about getTokenResult (https://developers.google.com/android/reference/com/google/android/gms/safetynet/SafetyNetApi.RecaptchaTokenResult.html#getTokenResult()):
Gets the reCAPTCHA user response token, which must be validated by calling the siteverify method described in Verify the user's response.
The manual of the siteverify function describes the following (https://developers.google.com/android/reference/com/google/android/gms/safetynet/SafetyNetClient.html#verifyWithRecaptcha(java.lang.String)):
Provides user attestation with reCAPTCHA.
If reCAPTCHA is confident that this is a real user on a real device it will return a token with no challenge. Otherwise it will provide a visual/audio challenge to attest the humanness of the user before returning a token.
My question
I want to use my backend server (Cloud Functions) to verify the reCAPTCHA. However, according to the Android documentation, all the above functions seem to be put client-side. Indeed, siteverify should be called with the token got with getTokenResult, and both seem to be part of the Android SecureNET ReCAPTCHA Android API...
However, I think that using Cloud Functions would be more secure! Can I use my backend however?
Edit: back-end call to siteverify in Cloud Functions
exports.verifyRecaptcha = functions.https.onRequest((request, response) => {
const user_response_token = request.query.user_response_token;
if(user_response_token == '') {
throw new functions.https.HttpsError('invalid-argument', 'The function must be called with an adequat user response token.');
}
const remote_url = 'https://www.google.com/recaptcha/api/siteverify';
const secret = null;
request.post({url: remote_url, form:{secret: secret, response: user_response_token}}, function(error, response, body) {
if(error) {
throw new functions.https.HttpsError('unknown', error);
}
if(!response.statusCode != 200) {
throw new functions.https.HttpsError('unknown', 'Something went wrong. Status code: ' + response.statusCode + '.');
}
if(!body.success) {
throw new functions.https.HttpsError('unknown', 'Unable to verify this captcha.');
}
return response;
});
});
You can take the token returned from getTokenResult(), send it to your backend, and have your backend call the web API version of siteverify:
https://www.google.com/recaptcha/api/siteverify
I found this solution and I think it is not the ID which is required for notification kindly tell me by some samples:
import android.provider.Settings.Secure;
private String androidIdd = Secure.getString(getContext().getContentResolver(), Secure.ANDROID_ID);
I guess I don't need an android id. I just want to get the device unique ID for Firebase Notification or GCM.
Instead of using some third party tutorials I would prefer to take a look into the official Firebase Cloud Messaging documentation.
If you have set up everything correctly you can just scroll to the topic
Retrieve the current registration token
When you need to retrieve the current token, call FirebaseInstanceId.getInstance().getToken(). This method returns null if the token has not yet been generated.
In a nutshell: Call FirebaseInstanceId.getInstance().getToken() to reveive the current device token.
EDIT:
The command FirebaseInstanceId.getInstance().getToken() is deprecated. The same documentation mentions a new approach as below:
FirebaseInstanceId.getInstance().getInstanceId()
.addOnCompleteListener(new OnCompleteListener<InstanceIdResult>() {
#Override
public void onComplete(#NonNull Task<InstanceIdResult> task) {
if (!task.isSuccessful()) {
Log.w(TAG, "getInstanceId failed", task.getException());
return;
}
// Get new Instance ID token
String token = task.getResult().getToken();
}
});
I got unregistered registration token even I am sure my token is correct and I check it in my log I am using master token FirebaseInstanceId.Instance.Token.
Here is my method:
private void ConfigureFireBase()
{
Task.Run(() => {
var instanceId = FirebaseInstanceId.Instance;
Android.Util.Log.Debug("TAG", "{0} {1}", instanceId?.Token?.ToString(), instanceId.GetToken(GetString(Resource.String.gcm_defaultSenderId), Firebase.Messaging.FirebaseMessaging.InstanceIdScope));
});
}
I check as well OnTokenRefresh method the same token
public override void OnTokenRefresh()
{
var refreshedToken = FirebaseInstanceId.Instance.Token;
Log.Debug(TAG, "Refreshed token: " + refreshedToken);
SendRegistrationToServer(refreshedToken);
}
but when I tried in Firebase console it gives me this error message, when I tried in http://pushtry.com/ with the same token I got not NotRegistered message
Note when I uninstall the app and install again the token working, but after while I got this error message.
The reason why this issue fired cause that token is unregistered
The registration token may change when:
The app deletes Instance ID
The app is restored on a new device
The user uninstalls/reinstall the app
The user clears app data.
Reference
and this happen in debug mode only so dont worry in release mode every thing will be fine.
How you can fix the issue ?
its easy just force to refresh token call this method in your landing activity (MainActivity , Login ) , this method force firebase to call OnTokenRefresh()
private void ConfigureFireBase()
{
#if DEBUG
Task.Run(() =>
{
var instanceId = FirebaseInstanceId.Instance;
instanceId.DeleteInstanceId();
Android.Util.Log.Debug("TAG", "{0} {1}", instanceId?.Token?.ToString(), instanceId.GetToken(GetString(Resource.String.gcm_defaultSenderId), Firebase.Messaging.FirebaseMessaging.InstanceIdScope));
});
// For debug mode only - will accept the HTTPS certificate of Test/Dev server, as the HTTPS certificate is invalid /not trusted
ServicePointManager.ServerCertificateValidationCallback += (o, certificate, chain, errors) => true;
#endif
}
Hope this help any one face same issue
In my case i had used Emulator and the Firewall was blocking it. I used my mobile, enabled the developer options and it got worked.
Hope this helps.
I have fully integrated Firebase Auth in my Android App, now I want the client to interact with my backend (rails) using a unique Token. my question is this how it's done :
User is logged-in, using for example Facebook Auth,
Send the Firebase Token to backend and verify it
Respond to the client with a unique Token for the upcoming Api requests (get users data, save user data ...)
Thank you
I have developed the firebase_id_token gem.
It deals with all the certificates/signature business.
It can be used simply as:
FirebaseIdToken::Signature.verify(token)
Taking JinLiu's answer forward , once you get the Token in your android code , send it to your backend say : https://www.yourbackend.com?authenticate?token=asdad7687h...
Note that the token generated by Firebase is a JWT token , we need to first verify its authenticity and decode it in the backend. For this , you need to add these two gems to your gemfile
gem 'jwt' , '1.5.6'
gem 'rest-client' , '2.0.1'
With that done , you need to add these two (private) function in your controller:
def get_set_user
begin
token = params[:token]
if token.nil?
#user = nil
return
end
firebase_id = verify_user(token)[0]["user_id"]
if User.where(:firebase_id => firebase_id).exists?
#user = User.where(:firebase_id => firebase_id).first
else
#user = User.new(:firebase_id => firebase_id)
#user.save
#user
end
rescue
#user = nil
end
end
def verify_user(token)
certificate_url = "https://www.googleapis.com/robot/v1/metadata/x509/securetoken#system.gserviceaccount.com"
myresponse = RestClient.get(certificate_url).body
certificates = JSON.parse myresponse.gsub('=>', ':')
myjson =""
certificates.each do|key , value|
begin
x509 = OpenSSL::X509::Certificate.new(value)
iss = 'https://securetoken.google.com/<yourdomain>'
aud = 'yourdomain' # change this
myjson = JWT.decode(token, x509.public_key, true,
{ algorithm: "RS256", verify_iat: true ,
iss: iss , verify_iss: true ,
aud: aud , verify_aud: true
})
return myjson
rescue
end
end
return nil
end
Now you can call get_set_user as before action to see , if the user has valid token .
The idea is simple. Check if the token is signed by one of the keys mentioned at https://www.googleapis.com/robot/v1/metadata/x509/securetoken#system.gserviceaccount.com . If yes , decode the token and get the firebase id .
You do not need step #3. Firebase Auth Android SDK getToken() API returns a short-lived (one hour) Firebase Auth ID Token for the login user, and automatically returns a new token if the previous one expires.
The Firebase token verification doc describes how to get a Firebase token on client app and verify the token on server side.