FirebaseMessaging generates same token - android

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

Related

Android dynamic link doesn't survive fresh app installation

I'm trying to implement referral in my app with Firebase Dynamic link.
The happy path would be:
Link is shared with a JWT token as parameter
Another user clicks on the link, doesn't have the app so it goes to the Playstore
After app was installed, at app startup, the JWT token is retrieved trough parameters and stored locally
New signup is complete, the token is sent to the backend to activate referrer's reward
Problem is that in this scenario, step 3 doesn't find any data at first app startup, whereas it is found when I click on the link anew after the app has been installed.
This is the way the link is generated for the referrer:
val parameters = new DynamicLink.SocialMetaTagParameters.Builder()
.setImageUrl(...)
.setTitle(...)
.build();
val link = Uri.parse("https://my.domain.com/path/?token=${jwtToken}");
val dynamicLink = FirebaseDynamicLinks.getInstance().createDynamicLink()
.setLink(link)
.setSocialMetaTagParameters(parameters)
.setDomainUriPrefix("https://my.domain.com")
.setAndroidParameters(DynamicLink.AndroidParameters.Builder().build()))
.setIosParameters(...)
.setNavigationInfoParameters(
DynamicLink.NavigationInfoParameters.Builder()
.setForcedRedirectEnabled(true)
.build
).buildDynamicLink()
The link is shorten by:
FirebaseDynamicLinks.getInstance().createDynamicLink()
.setDomainUriPrefix("https://my.domain.com/")
.setLongLink(dynamicLink.getUri())
.buildShortDynamicLink()
.addOnCompleteListener(activity, task -> {
if (task.isSuccessful() && task.getResult() != null) {
linkCallback.success(task.getResult().getShortLink().toString());
} else {
linkCallback.failure(task.getException());
}
})
At app's opening, link is read in the onResume() method of the starting activity :
override fun onResume() {
super.onResume()
FirebaseDynamicLinks.getInstance()
.getDynamicLink(getIntent()) // getIntent() refers to the Activity's method
.addOnSuccessListener(activity) { data -> // Get deep link from result (may be null if no link is found)
val isReferralLink = data?.link?.toString()?.startsWith("https://my.domain.com/path") == true
val jwt = data?.link?.getQueryParameter("token")
// Here, data is null. <-------
// Other attempt :
data?.let { aiData ->
FirebaseAppInvite.getInvitation(aiData)?.let { result ->
// Here result is still null
}
}
}
}
I've seen on a stack overflow thread that it doesn't survive to beta track install, so I tried to leave the beta and use remote config to hide it in production track, but I have not been able see any difference.
And the version I'm using is :
implementation 'com.google.firebase:firebase-dynamic-links:21.0.0'
implementation 'com.google.firebase:firebase-analytics:20.0.0'
implementation 'com.google.firebase:firebase-invites:17.0.0'
Question:
Is there something I'm missing here ?
Thank you in advance for your help !
EDIT:
In this scenario the link and token are correctly found.
Link is shared with a JWT token as parameter
Another user clicks on the link, doesn't have the app so it goes to the Playstore
After app was installed, the second user clicks again on the link and opens the app for the first time
Ok so after a few days of testing and researches, I found out why it was not working.
FirebaseDynamicLinks.getInstance()
.getDynamicLink(getIntent())
.addOnSuccessListener(activity) { data ->
// Some data reading
}
In this case I was using the addOnSuccessListener() with an activity as parameter, which makes it lifecycle aware. In my app, a new user (fresh install) will be redirected to an onboarding activity, so the listener's activity is paused, and the callback is never fired.
=> TLDR: Removing this activity parameter solved my problem.

Android Firebase Cloud Messaging Token (FCM) token is too short/incomplete

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;
})

Deleting a user from Azure Active Directory B2C Android/Java

I have an Android application in which I'm using Azure AD B2C to authenticate users. Users login and logout of the application as needed. I would like to give the user the option to delete their own account.
I understand that I need to use the Azure AD Graph API to delete the user. This is what I have so far:
According to this link, it looks like deleting a user from a personal account (which is what the B2C users are using) is not possible. Is that correct?
Here's my code snippet for the Graph API call. Feel free to ignore it if I'm off track and there is a better way to solve this.
I believe I need a separate access token than what my app currently has (as the graph API requires other API consent). So, I'm getting the access token as follows:
AcquireTokenParameters parameters = new AcquireTokenParameters.Builder()
.startAuthorizationFromActivity(getActivity())
.fromAuthority(B2CConfiguration.getAuthorityFromPolicyName(B2CConfiguration.Policies.get("SignUpSignIn")))
.withScopes(B2CConfiguration.getGraphAPIScopes())
.withPrompt(Prompt.CONSENT)
.withCallback(getGraphAPIAuthCallback())
.build();
taxApp.acquireToken(parameters);
In the getGraphAPIAuthCallback() method, I'm calling the Graph API using a separate thread (in the background):
boolean resp = new DeleteUser().execute(authenticationResult.getAccessToken()).get();
Finally, in my DeleterUser() AsyncTask, I'm doing the following:
#Override
protected Boolean doInBackground(String... aToken) {
final String asToken = aToken[0];
//this method will be running on background thread so don't update UI from here
//do your long running http tasks here,you dont want to pass argument and u can access the parent class' variable url over here
IAuthenticationProvider mAuthenticationProvider = new IAuthenticationProvider() {
#Override
public void authenticateRequest(final IHttpRequest request) {
request.addHeader("Authorization",
"Bearer " + asToken);
}
};
final IClientConfig mClientConfig = DefaultClientConfig
.createWithAuthenticationProvider(mAuthenticationProvider);
final IGraphServiceClient graphClient = new GraphServiceClient.Builder()
.fromConfig(mClientConfig)
.buildClient();
try {
graphClient.getMe().buildRequest().delete();
} catch (Exception e) {
Log.d(AccountSettingFragment.class.toString(), "Error deleting user. Error Details: " + e.getStackTrace());
}
return true;
}
Currently, my app fails when trying to get an access token with a null pointer exception:
com.microsoft.identity.client.exception.MsalClientException: Attempt to invoke virtual method 'long java.lang.Long.longValue()' on a null object reference
Any idea what I need to do to provide the user the option to users to delete their own account? Thank you!
Thanks for the help, #allen-wu. Due to his help, this azure feedback request and this azure doc, I was able to figure out how to get and delete users silently (without needing intervention).
As #allen-wu stated, you cannot have a user delete itself. So, I decided to have the mobile app call my server-side NodeJS API when the user clicks the 'Delete Account' button (as I do not want to store the client secret in the android app) and have the NodeJS API call the Azure AD endpoint to delete the user silently. The one caveat is that admin consent is needed the first time you try to auth. Also, I have only tested this for Graph API. I'm not a 100% sure if it works for other APIs as well.
Here are the steps:
Create your application in your AAD B2C tenant. Create a client secret and give it the following API permissions: Directory.ReadWrite.All ;
AuditLog.Read.All (I'm not a 100% sure if we need the AuditLog permission. I haven't tested without it yet).
In a browser, paste the following link:
GET https://login.microsoftonline.com/{tenant}/adminconsent?
client_id=6731de76-14a6-49ae-97bc-6eba6914391e
&state=12345
&redirect_uri=http://localhost/myapp/permissions
Login using an existing admin account and provide the consent to the app.
Once you've given admin consent, you do not have to repeat steps 1-3 again. Next, make the following call to get an access token:
POST https://login.microsoftonline.com/{B2c_tenant_name}.onmicrosoft.com/oauth2/v2.0/token
In the body, include your client_id, client_secret, grant_type (the value for which should be client_credentials) and scope (value should be 'https://graph.microsoft.com/.default')
Finally, you can call the Graph API to manage your users, including deleting them:
DELETE https://graph.microsoft.com/v1.0/users/{upn}
Don't forget to include the access token in the header. I noticed that in Postman, the graph api had a bug and returned an error if I include the word 'Bearer' at the start of the Authorization header. Try without it and it works. I haven't tried it in my NodeJS API yet, so, can't comment on it so far.
#allen-wu also suggested using the ROPC flow, which I have not tried yet, so, cannot compare the two approaches.
I hope this helps!
There is a line of code: graphClient.getUsers("").buildRequest().delete();
It seems that you didn't put the user object id in it.
However, we can ignore this problem because Microsoft Graph doesn't allow a user to delete itself.
Here is the error when I try to do it.
{
"error": {
"code": "Request_BadRequest",
"message": "The principal performing this request cannot delete itself.",
"innerError": {
"request-id": "8f44118f-0e49-431f-a0a0-80bdd954a7f0",
"date": "2020-06-04T06:41:14"
}
}
}

Why Firebase is unable to get PlayGames AuthCode and pass it into its Credential?

To begin with, I'm working on a Unity Game where I'm authenticating user when the game starts. My build environment is android. I'm using Firebase authentication for Google Play Games Services to authenticate user.
When the game starts in my android device or emulator, it is able to authenticate Play Games Services as well as able to connect with Firebase (I'm getting analytics data). However, when I pass the PlayGames AuthCode into Firebase.Auth Credentials, it stops executing the code (I've debug log for it). It does not throw any error in LogCat except
Firebase | server_auth_code
I tried searching web for different issues, but nothing. I checked my keys in player setting, firebase settings, OAuth 2.0 credentials on my Google API console and even check keys from my Google Play Console (which I'm not using at this stage). I have even checked my test users email addresses in Game Services and tried multiple google play games account. But issue still persist.
I'm using similar script in my other unity project where authentication works like a charm. I tried to use same script here and ended up with this issue: here. However, I solved it by removing all the packages and re-importing them into unity and changed my call functions in the script. Now, I'm stuck at this issue.
Here is cs file:
using GooglePlayGames;
using GooglePlayGames.BasicApi;
using UnityEngine.SocialPlatforms;
using System.Threading.Tasks;
public class SetFirebase : MonoBehaviour
{
string authCode;
void Start()
{
PlayGamesClientConfiguration config = new PlayGamesClientConfiguration.Builder().
RequestServerAuthCode(false /* Don't force refresh */).Build();
PlayGamesPlatform.InitializeInstance(config);
PlayGamesPlatform.Activate();
Social.localUser.Authenticate((bool success) =>
{
if (success)
{
authCode = PlayGamesPlatform.Instance.GetServerAuthCode();
Debug.Log("PlayGames successfully authenticated!");
Debug.Log("AuthCode: " + authCode);
}
else
{
Debug.Log("PlayGames SignIn Failed");
}
});
Firebase.FirebaseApp.CheckAndFixDependenciesAsync().ContinueWith(task =>
{
var dependencyStatus = task.Result;
if (dependencyStatus == Firebase.DependencyStatus.Available)
{
Debug.Log("Firebase Ready!!!");
RunFirebase();
}
else
{
Debug.LogError(System.String.Format("Could not resolve all Firebase dependencies: {0}", dependencyStatus));
}
});
}
private void RunFirebase(){
Firebase.Auth.FirebaseAuth auth = Firebase.Auth.FirebaseAuth.DefaultInstance;
Debug.Log("init firebase auth ");
Firebase.Auth.Credential credential = Firebase.Auth.PlayGamesAuthProvider.GetCredential(authCode);
Debug.Log(" passed auth code ");
auth.SignInWithCredentialAsync(credential).ContinueWith(task =>
{
if (task.IsCanceled)
{
Debug.LogError("SignInOnClick was canceled.");
return;
}
if (task.IsFaulted)
{
Debug.LogError("SignInOnClick encountered an error: " + task.Exception);
return;
}
Firebase.Auth.FirebaseUser newUser = task.Result;
Debug.LogFormat("SignInOnClick: User signed in successfully: {0} ({1})", newUser.DisplayName, newUser.UserId);
});
}
}
My LogCat executes everything till "init firebase auth" but does not execute "passed auth code" so I know there is some issue in passing the credentials. It also does not run anything inside auth.SignInWithCredentialAsync(credential).
Any help or suggestion would be highly appreciated. Thank you.
There are two things I may suggest:
1) Replace ContinueWith with ContinueWithOnMainThread. This is a Firebase Extension that will guarantee that your logic runs on the main Unity thread (which tends to resolve many Unity specific issues). I go into more detail about that here.
2) Your logic may have a race condition between the Authenticate callback and the CheckAndFixDependenciesAsync continuation. These will not necessarily run in the order that you see them in your logic.
If I were building this system, I might prefer using Coroutines and a custom yield instruction:
class Authenticate : CustomYieldInstruction
{
private bool _keepWaiting = true;
public override bool keepWaiting => _keepWaiting;
public Authenticate(Social.ILocalUser user) {
user.Authenticate((bool success)=>{
/* old authentication code here */
_keepWaiting = false;
});
}
}
Then in a coroutine have something like:
private IEnumerator InitializeCoroutine() {
/* old authentication code */
// I'm ignoring error checking for now, but it shouldn't be hard to figure in.
// I'm mostly going from memory now anyway
// start both authentication processes in parallel
var authenticate = new Authenticate(Social.localUser);
var firebaseDependenciesTask = FirebaseApp.CheckAndFixDependenciesAsync();
// wait on social
yield return authenticate;
// wait on Firebase. If it finished in the meantime this should just fall through
yield return new WaitUntil(()=>firebaseDependenciesTask.IsComplete);
RunFirebase();
}
This way my logic looks roughly synchronous whilst still maintaining the asynchronosity (spell check claims that I made up that word) of the systems you're depending on and you avoid threading related issues that arise when using ContinueWith.
Let me know if that helps!
--Patrick

Firebase facebook popup returns null onAuth

My firebase facebook popup login on ionic was working correctly but suddenly it stop working (well, i was modifiying the app but i didnt touch the service). The process still works on browser (when i use ionic serve) but not running on ionic app.
var auth = $fAuth(ref);
// login with Facebook
auth.$onAuth(function(authData){ // authData -> null
console.log("Auth..", authData); // Auth.. null
/* refresh user data on firebase and registering push */
});
auth.$authWithOAuthPopup("facebook").catch(function(error) {
console.log("Authentication failed:", error);
});
When it was working, $onAuth cb function was recieving an object in authData variable with properties like uid and facebook.
when i run:
ref.authWithOAuthPopup("facebook",function(){console.log(arguments)})
or:
ref.authWithOAuthPopup("twitter",function(){console.log(arguments)})
the callback function never fires.
I run the auth process on $ionicPlatform.ready event.
I already uninstalled and reinstalled the app, and cleaned app data.
thanks!
Are you sure it doesn't fire?
A callback function that will be called when authentication has completed. On failure, the first argument will be an Error object indicating the failure, with a machine-readable code attribute. On success, the first argument will be null and the second will be an object containing the fields uid (the unique user id), provider (string identifying the provider), auth (the auth. token payload), and expires (expiration time in seconds since the Unix epoch) - and more, depending upon the provider used to authenticate.
You didn't put the arguments there.
I think it should work if you try it again.
Example code from firebase:
ref.authWithOAuthPopup("facebook", function(error, authData) {
// ^^^^^ ^^^^^^^^
if (error) {
console.log("Login Failed!", error);
} else {
console.log("Authenticated successfully with payload:", authData);
}
});

Categories

Resources