Accessing data once Realm Legacy to Mongodb migration is done - realm

We have migrated our old data into Atlas db.Android Code refactoring for app is done as well as per the Realm Mongodb Migration guide.
Issue arises when legacy user tries to login first time.
Sequence of steps: Register the legacy user in Auth, login (works till here).
public void getUserById(String uid, CompletionUser completionUser) {
if(realm == null)
return;
User userModel = realm.where(User.class).equalTo("_id", uid).findFirst();
if(userModel != null && userModel.isValid()) {
completionUser.onSuccess(userModel);
}
else {
completionUser.onFail("User does not found");
}
}
Always returns user not found, even though the data is present in legacy.

Related

How can I authenticate with Google Play Services when using Realtime Database's API?

I'm working on a mobile game in Unity and using a Firebase Realtime Database for online leaderboards.
A pre-requisite for playing my game is the user logging into Google Play Services (Integrated into Unity using the official SDK), so I want my code to use the user's Google Play Services account to authenticate with my DB.
I know that authentication via Google Play Services is possible, but my questions are:
Can one authenticate using Google Play Services when using the REST API? If so, what is the structure of such a query?
What rules/configuration do I need to set up on the Firebase Realtime Database side to allow all read/writes from authenticated users and deny all anonymous operations?
Thanks in advance!
First I'll apologize by not answering the question directly but by trying to answer the spirit of the question. Since you are using Realtime Database and Firebase Authentication, it's worth pointing out that as long as you're building a mobile game (iOS/Android) you're best off using the official Firebase SDK. Since desktop support is beta, other platforms aren't there (consoles, web, &c), and some devs prefer not to use native plugins feel free to vote the answer down due to this initial caveat.
With that out of the way, first I'll point out that there is an example online leaderboard you can pull directly into your project that uses the Realtime Database Unity SDK.
I'd recommend following this video to get started, and this link for how to auth using Google Sign-in. But you'll generally do something like:
// make sure you've finished CheckAndFixDependenciesAsync before this
var auth = FirebaseAuth.DefaultInstance;
// sign in credentials persist between runs, make sure you're not already signed in
if (auth.CurrentUser == null) {
Firebase.Auth.Credential credential =
Firebase.Auth.GoogleAuthProvider.GetCredential(googleIdToken, googleAccessToken);
auth.SignInWithCredentialAsync(credential).ContinueWith(task => {
if (task.IsCanceled) {
Debug.LogError("SignInWithCredentialAsync was canceled.");
return;
}
if (task.IsFaulted) {
Debug.LogError("SignInWithCredentialAsync encountered an error: " + task.Exception);
return;
}
Firebase.Auth.FirebaseUser newUser = task.Result;
Debug.LogFormat("User signed in successfully: {0} ({1})",
newUser.DisplayName, newUser.UserId);
});
}
I know that the docs are a little sparse here, but googleIdToken should come from the Google Sign In plugin and the access token looks like it can be null. To pull one more piece of google documentation, this is a sample for how to use google sign in. So:
GoogleSignIn.Configuration = new GoogleSignInConfiguration {
RequestIdToken = true,
// Copy this value from the google-service.json file.
// oauth_client with type == 3
WebClientId = "1072123000000-iacvb7489h55760s3o2nf1xxxxxxxx.apps.googleusercontent.com"
};
GoogleSignIn.DefaultInstance.SignIn().ContinueWithOnMainThread(task => {
var idToken = ((Task<GoogleSignInUser>)task).Result.IdToken;
// create the Firebase credential now.
});
Now for rules, I'm going to assume that you're using the Realtime Database Unity SDK. If you use this SDK and the Firebase Auth one, your user credentials will automatically get propagated to Realtime Database for you. In that case, what you want to do is a pretty common use case.
So if you just want to require authentication to read and write to a part of the database:
{
"rules": {
"node_to_protect": {
".read": "auth != null",
".write": "auth != null",
}
}
}
Note that Realtime Database's security validator will not accept just auth != null as security, but that will get you started.
I finally found the answer, thanks in part to Patrick Matrin's response.
Can one authenticate using Google Play Services when using the REST API? If so, what is the structure of such a query?
The answer is yes, it is possible, but it's not straightforward.
In order to add authentication to requests, an access token is required.
The syntax change is quite simple, so let's say the URL you were originally posting to was:
https://<DATABASE_NAME>.firebaseio.com/<SUB_FOLDER>/<SUB_FOLDER2>.json
You will want to change it to:
https://<DATABASE_NAME>.firebaseio.com/<SUB_FOLDER>/<SUB_FOLDER2>.json?access_token=<ACCESS_TOKEN>
You can read more about it here.
The main issue is getting that Access Token. To grossly over-simplify, what you want to do is the following:
Start Google Play Services in your game
Authenticate user with Google Play Services (have them 'log in')
Swap out the authentication code we got for an access token
Steps 1 and 2 should be fairly straightforward, and you probably already did them if you're using Google Play Services. In any case, this is how the class looks in my project:
public class GPGSAuthentication : MonoBehaviour
{
public static PlayGamesPlatform platform;
void Start()
{
if (platform == null)
{
PlayGamesClientConfiguration config = new PlayGamesClientConfiguration.Builder().RequestServerAuthCode(false).Build();
PlayGamesPlatform.InitializeInstance(config);
PlayGamesPlatform.DebugLogEnabled = true;
platform = PlayGamesPlatform.Activate();
}
Social.Active.localUser.Authenticate(success =>
{
if (success)
{
Debug.Log("GSPS - Logged in successfully");
}
else
{
Debug.Log("GSPS - Falied to login");
}
});
}
}
Notice the use of RequestServerAuthCode(false) in the config build. You'll need this if you want to get the Authentication Code discussed earlier.
After that, you'll need to use REST API in order to exchange the auth code for an access token. You'll need to query a GoogleAPI URL and supply some parameters:
/// <summary>
/// Trades the auth code for a Google Access Token.
/// </summary>
/// <param name="clientID">The ID of the client we're trying to reach</param>
/// <param name="clientSecret">The secret of the client we're trying to reach</param>
/// <param name="grantType">The grant_type value</param>
/// <param name="authCode">The auth code we want to trade in for the Google Access Token</param>
/// <returns></returns>
IEnumerator GetAccessToken(string clientID, string clientSecret, string grantType, string authCode)
{
if (googleAccessToken != null && googleAccessToken != "")
{
Debug.Log("Not requesting googleAccessToken again since we already have it");
yield break;
}
//googleAccessToken = "";
WWWForm form = new WWWForm();
form.AddField("client_id", clientID);
form.AddField("client_secret", clientSecret);
form.AddField("grant_type", grantType);
form.AddField("code", authCode);
UnityWebRequest www = UnityWebRequest.Post("https://www.googleapis.com/oauth2/v4/token", form);
yield return www.SendWebRequest();
if (www.isNetworkError || www.isHttpError)
{
Debug.Log("Ran into error while trying to get access token\n Error: " + www.error + "\nURL: " + www.url.ToString() + "\nCode: " + authCode);
}
else
{
Debug.Log("Got Access Token in return, full string is: \n" + www.downloadHandler.text);
GoogleData googleData = JsonUtility.FromJson<GoogleData>(www.downloadHandler.text);
googleAccessToken = googleData.access_token;
}
}
/// <summary>
/// Class for handling data received from Google after giving Auth Code
/// </summary>
[System.Serializable]
public class GoogleData
{
public string access_token;
public string token_type;
public int expires_in;
public string refresh_token;
}
You'll need to get the values for client_id and client_secret from your OAuth2 web client that you created. You should be able to read them in the Google API credentials page.
As for grant_type, always supply the value "authorization_code", and in order to get the value of your Authentication Code, use PlayGamesPlatform.Instance.GetServerAuthCode(), though obviously only after successfully logging in the user in Google Play Services.
Now you should have everything needed to authenticate your user with Firebase using Google Play Services. No further SDK or package/plugin needed.
What rules/configuration do I need to set up on the Firebase Realtime Database side to allow all read/writes from authenticated users and deny all anonymous operations?
Patrick's answer here was correct. This is a simple rule with the following syntax:
{
"rules": {
"node_to_protect": {
".read": "auth != null",
".write": "auth != null",
}
}
}

Firebase gets logged out after long time in Flutter Web

I'm developing a web app and I use Firebase Authentication for the authentication service.
The project seems to store the authentication, since if I refresh the page, or close the browser, the user is still logged in.
However I noticed that if I don't access the app for a long time (more than 1 hour, after the night for example), the authentication gets lost.
I don't know how to debug this and how to solve this.
Following some snippets of code to better understand my implementation:
This is the function I have in my startup view to redirect the user to the right page based on auth status.
bool isUserLoggedIn() {
var user = _firebaseAuth.currentUser;
return user != null;
}
void handleStartupBasedOnAuthStatus() {
Future.delayed(const Duration(milliseconds: 1000), () async {
bool loggedInShared =
await sharedPreferences.getBoolSharedPreferences("loggedIn");
if (isUserLoggedIn() || loggedInShared) {
String ruoloValue =
await sharedPreferences.getSharedPreferences('ruolo');
(ruoloValue == Ruolo.ADMIN)
? navigationService.replaceWith(Routes.admin)
: navigationService.replaceWith(Routes.messages);
} else {
navigationService.replaceWith(Routes.login);
}
});
}
In the following function I call the onAuthStateChange to set sharedpreferences accordingly. I have the check on the timestamp because I noticed that it is triggered more time once the page is refreshed.
void listenToAuthChangesSharedPref() {
FirebaseAuth.instance.authStateChanges().listen((firebaseUser) async {
var datetimeNow = (DateTime.now().millisecondsSinceEpoch);
String oldDatetimeString =
await sharedPreferences.getSharedPreferences('previous_timestamp');
if (oldDatetimeString != null) {
var oldDatetime = (new DateTime.fromMillisecondsSinceEpoch(
int.parse(oldDatetimeString)))
.millisecondsSinceEpoch;
if (datetimeNow - oldDatetime > 1000) {
if (firebaseUser == null) {
await sharedPreferences.setBoolSharedPreferences('loggedIn', false);
} else {
await sharedPreferences.setBoolSharedPreferences('loggedIn', true);
}
await sharedPreferences.setSharedPreferences(
'previous_timestamp', datetimeNow.toString());
}
} else {
if (firebaseUser == null) {
await sharedPreferences.setBoolSharedPreferences('loggedIn', false);
} else {
await sharedPreferences.setBoolSharedPreferences('loggedIn', true);
}
await sharedPreferences.setSharedPreferences(
'previous_timestamp', datetimeNow.toString());
}
});
}
My question is: is possible that after long time currentUser and also the onAuthStateChanges gets called and the user is not logged in?
Persisting authentication state#
The Firebase SDKs for all platforms provide out of the box support for ensuring that your user's authentication state is persisted across app restarts or page reloads.
On native platforms such as Android & iOS, this behaviour is not configurable and the user's authentication state will be persisted on-device between app restarts. The user can clear the apps cached data via the device settings which will wipe any existing state being stored.
On web platforms, the user's authentication state is stored in local storage. If required, you can change this default behaviour to only persist authentication state for the current session, or not at all. To configure these settings, call the setPersistence() method (note; on native platforms an UnimplementedError will be thrown):
// Disable persistence on web platforms
await FirebaseAuth.instance.setPersistence(Persistence.NONE);
for more info:
for more info:

Xamarin Forms iOS - Saving a user tag in Azure Notification Hubs works in AppDelegate but not in a service

I'm currently trying to get push notifications working for my mobile app using Azure Notification Hubs. Android is working fine and the initial iOS set up in AppDelegate works ok with a sample tag.
public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
if (deviceToken == null)
{
return;
}
SBNotificationHub hub = new SBNotificationHub(CommonConstants.LISTEN_CONNECTION_STRING, CommonConstants.NOTIFICATION_HUB_NAME);
// update registration with Azure Notification Hub
hub.UnregisterAll(deviceToken, async (error) =>
{
if (error != null)
{
System.Diagnostics.Debug.WriteLine($"Unable to call unregister {error}");
return;
}
string[] tags = new[] { "iostestpush" };
NSSet userTags = new NSSet(tags);
hub.RegisterNative(deviceToken, userTags, (error) =>
{
if (error != null)
{
System.Diagnostics.Debug.WriteLine($"Unable to call register {error}");
return;
}
});
var templateExpiration = DateTime.Now.AddDays(120).ToString(System.Globalization.CultureInfo.CreateSpecificCulture("en-US"));
hub.RegisterTemplate(deviceToken, "defaultTemplate", CommonConstants.APN_TEMPLATE_BODY, templateExpiration, userTags, (errorCallback) =>
{
if (errorCallback != null)
{
System.Diagnostics.Debug.WriteLine($"RegisterTemplateAsync error: {errorCallback}");
}
});
});
}
The issue I'm having is I need to register the UserId after a successful login. So I set up a service with the above code, saved the token to the device as string so it can be retrieved in the service and turned back into an NSData token
NSData deviceToken = new NSData(token, NSDataBase64DecodingOptions.None);
After a successful login I send the token string and the tag array to my service.
string[] userTag = new[] { loginResponse.UserId.ToString() };
await this._azureReg.SendRegistrationToServer(deviceToken, userTag);
Which, other than turning the token back into NSData and the user tag into an NSSet, is the same as above other than the name change. But Azure is claiming there is no registration even though my output shows
Registered for push notifications with token: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
I thought it was the string conversion back and forth, so tested that in the AppDelegate and it worked fine.
So, I'm at a loss at how to register the UserId after a successful login and why it works in one place but not the other.
I hope that's clear and thanks for any advice in advance.
You probably ran into the same bug as me and several others.
Basically SBNotificationHub method overloads like UnregisterAll and RegisterTemplate with the callback signature do not work when you use them off the main thread, using the libraries to date. I was also using a Service for the same purpose (to handle push across platforms with different tags, especially for user id) but my implementation involved switching off the main thread for this.
The bug we logged and is now being addressed is here: https://github.com/Azure/azure-notificationhubs-ios/issues/95
The solution, for now, is to ditch SBNotificationHub completely. The Xamarin / Azure documentation is out of date, and SBNOtificationHub is legacy code. The recommended library is MSNotificationHub. https://github.com/azure/azure-notificationhubs-xamarin
As workarounds you can use the SBNotificationHub method overloads that do not involve callbacks (they return an error message instead) or the workaround in the 95 issue above.

where to store user credentials in front end

in ASP.NET web API in the log in algorithm i have a action filter that generates a token for each user and the front end sends that token back to authenticate the user by using that token in web server i can get current user information till now every thing is working fine however i have new requirements that every user has relation many to many with account which means the same user can exists in more than one account with different roles for example in account one he is an admin in account two he is normal user so i have to regenerate the token which requires the user to re log in again i do not want him to be redirected to the log in page again. what i think of is to store user name and password in html 5 local storage but i think that is a bad practices any ideas.
Her is how i generate token.
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (!actionContext.Request.Headers
.Any(header => header.Key == "AuthorizationHeader"))
{
if (this.IsAnonymousAllowed(actionContext) == false)
{
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized, "Un Autherized");
}
}
else
{
string token = actionContext.Request.Headers
.Where(header => header.Key == "AuthorizationHeader")
.First().Value.First();
if (this.IsAnonymousAllowed(actionContext) == true)
{
return;
}
string passPhrase = System.Configuration.ConfigurationSettings.AppSettings["PassPhrase"];
string ticket_string = Crypto.Decrypt(token, passPhrase);
TicketData ticket = JsonConvert.DeserializeObject<TicketData>(ticket_string);
if (ticket == null || ticket.Expiration < DateTime.Now)
{
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized, "UnAuthorized");
}
else
{
OurIdentity identity = (OurIdentity)ticket.TokenData.OurIdentity;
System.Threading.Thread.CurrentPrincipal = new OurPrincipal
{
OurIdentity = identity,
};
}
}
}
You are right saving username and password in the local storage is bad. It is bad to save it anywhere on the client.
Usually a token is generated and put in a cookie. That token corresponds with a record on the server, in either a session log or a database.
I strongly suggest to use existing methods for this, like OAUTH Bearer tokens in this tutorial.
As far as I understand, if you are storing a hash (perhaps with a salt for extra protection) it is not nessecescarily bad to store the credentials. These would have to be stored somewhere at the end of the day anyway.

ExtendedMembershipProvider.GetUserIDfromOauth returns what user ID?

I'm trying to customize my own implementation of ExtendedMembershipProvider. I have no idea what the GetUserIDFromOauth method is supposed to do? I see it is throwing an exception by default, and that it is supposed to return the user ID from the open auth provider.
I fail to see how this is supposed to be done, unless this means find if that user exists in the system? Is that it's purpose? I find the lack of documentation confusing...
Thanks.
GetUserIdFromOAuth is a method used by ExtendedMembershipProvider class to find User.Id in your table of users in your web application database based on Provider and ProviderUserId that you get from OAuth or OpenId Provider. After getting Provider and ProviderUserId data for a specified user, you need to save it in your database.
It returns throw new NotImplementedException(); by default. You need to implement this method to return an integer of your User.Id from your application database.
This is a sample implementation:
public override int GetUserIdFromOAuth(string provider, string providerUserId)
{
using (var context = new YourApplicationEntities())
{
// Try to find user with certain Provider and ProviderUserId
var user = context.Users.SingleOrDefault(
q => q.Provider == provider &&
q.ProviderUserId == providerUserId
);
if (user != null)
{
return user.Id;
}
}
return -1;
}
This implementation assumed that you have Provider and ProviderUserId field in your User table. If this information saved in a different table, you just need to modify the LINQ to return the desired result.

Resources