PFInstallation currentInstallation clearCache - ios

Are we able to clear PFInstallation data forcefully?
Needed this to recreate another PFInstallation when I want to force logout a user.
Current Problem:
New Account is using PFInstallation of old account and New Account can't update the PFInstallation (clearing it when logging out).
Other Possible Solution:
Update PFInstallation's ACL through cloud code with the new account's data. Is this possible?

In the beforeSave trigger for the installation, check to see if the user is getting set. If so, turn off public read/write access and give read/write access to that user. When logging out, the user should be removed, and you can return public read/write access.
Parse.Cloud.beforeSave(Parse.Installation, function(request, response) {
var installation = request.object;
if( installation.dirty("user") ) {
var acl = new Parse.ACL();
var user = installation.get("user");
if( user ) {
acl.setPublicReadAccess(false);
acl.setPublicWriteAccess(false);
acl.setReadAccess(user.id, true);
acl.setWriteAccess(user.id, true);
}
else {
acl.setPublicReadAccess(true);
acl.setPublicWriteAccess(true);
}
installation.setACL(acl);
}
response.success();
});

Related

How to know user is login by facebook or phone number in firebase ios (swift)? [duplicate]

I am using firebase from google and I have some trouble with user authentication. After logging with facebook I obtain FirebaseUser in AuthStateListener, but how can I detect if this user is logged via facebook or differently?
UPDATE
As #Frank van Puffelen said FirebaseAuth.getInstance().getCurrentUser().getProviderId()
should return "facebook", but in my case it returns "firebase". Now I cannot figure out what's the reason of this behavior. When I got FacebookToken I do something like this:
AuthCredential credential = FacebookAuthProvider.getCredential(facebookToken.getToken());
mAuth.signInWithCredential(credential)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
// If sign in fails, display a message to the user. If sign in succeeds
// the auth state listener will be notified and logic to handle the
// signed in user can be handled in the listener.
if (!task.isSuccessful()) {
}
}
});
And afterthat before onComplete() method is called, my AuthStateListener gets user which provider id is not "facebook" as it should be. Am I doing something wrong? I followed official google documentation
In version 3.x and later a single user can be signed in with multiple providers. So there is no longer the concept of a single provider ID. In fact when you call:
FirebaseAuth.getInstance().getCurrentUser().getProviderId()
It will always return firebase.
To detect if the user was signed in with Facebook, you will have to inspect the provider data:
for (UserInfo user: FirebaseAuth.getInstance().getCurrentUser().getProviderData()) {
if (user.getProviderId().equals("facebook.com")) {
System.out.println("User is signed in with Facebook");
}
}
In my app, I use Anonymous Firebase accounts. When I connect Firebase auth with a Facebook account or Google Account I am checking like the following:
for (UserInfo user: FirebaseAuth.getInstance().getCurrentUser().getProviderData()) {
if (user.getProviderId().equals("facebook.com")) {
//For linked facebook account
Log.d("xx_xx_provider_info", "User is signed in with Facebook");
} else if (user.getProviderId().equals("google.com")) {
//For linked Google account
Log.d("xx_xx_provider_info", "User is signed in with Google");
}
}
For me, the following solution is working.
First, get the firebase user object if you have'nt already:
FirebaseAuth mAuth = FirebaseAuth.getInstance();
FirebaseUser firebaseUser = mAuth.getCurrentUser();
Now use the following on the FirebaseUser object to get the sign in provider:
firebaseUser.getIdToken(false).getResult().getSignInProvider()
Sources:
https://firebase.google.com/docs/reference/android/com/google/firebase/auth/FirebaseUser
https://firebase.google.com/docs/reference/android/com/google/firebase/auth/GetTokenResult.html
It will return password, google.com, facebook.com and twitter.com for email, google, facebook and twitter respectively.
Sharing for FirebaseAuth targeting version 6.x.x (Swift 5.0), year 2020:
I use Auth.auth().currentUser?.providerData.first?.providerID.
This will return password if logged in via email. And facebook.com if via Facebook.
There exist information in the responding Intent.
Refer to following snippet:
The responseCode is either "phone", "google.com", "facebook.com", or "twitter.com".
`import com.firebase.ui.auth.AuthUI;
import com.firebase.ui.auth.IdpResponse;
.....
#Override
protected void onActivityResult(final int requestCode, int resultCode, Intent
data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RC_SIGN_IN) {
progressBar.setVisibility(View.VISIBLE);
IdpResponse response = IdpResponse.fromResultIntent(data);
if (resultCode == RESULT_OK) {
String providerCode = response.getProviderType();
...
}
}
Most recent solution is:
As noted here
var uiConfig = {
callbacks: {
signInSuccessWithAuthResult: function(authResult, redirectUrl) {
var providerId = authResult.additionalUserInfo.providerId;
//...
},
//..
}
and for display in page
firebase.auth().onAuthStateChanged(function (user) {
if (user) {
user.getIdToken().then(function (idToken) {
$('#user').text(welcomeName + "(" + localStorage.getItem("firebaseProviderId")+ ")");
$('#logged-in').show();
}
}
});

Implicit grant SPA with identity server4 concurrent login

how to restrict x amount of login on each client app in specific the SPA client with grant type - implicit
This is out of scope within Identity server
Solutions tried -
Access tokens persisted to DB, however this approach the client kept updating the access token without coming to code because the client browser request is coming with a valid token though its expired the silent authentication is renewing the token by issues a new reference token ( that can be seen in the table persistGrants token_type 'reference_token')
Cookie event - on validateAsync - not much luck though this only works for the server web, we can't put this logic on the oidc library on the client side for SPA's.
Custom signInManager by overriding SignInAsync - but the the executing is not reaching to this point in debug mode because the IDM kept recognising the user has a valid toke ( though expired) kept re issueing the token ( please note there is no refresh token here to manage it by storing and modifying!!!)
Any clues how the IDM re issue the token without taking user to login screen, even though the access token is expired??(Silent authentication. ??
implement profile service overrride activeasync
public override async Task IsActiveAsync(IsActiveContext context)
{
var sub = context.Subject.GetSubjectId();
var user = await userManager.FindByIdAsync(sub);
//Check existing sessions
if (context.Caller.Equals("AccessTokenValidation", StringComparison.OrdinalIgnoreCase))
{
if (user != null)
context.IsActive = !appuser.VerifyRenewToken(sub, context.Client.ClientId);
else
context.IsActive = false;
}
else
context.IsActive = user != null;
}
startup
services.AddTransient<IProfileService, ProfileService>();
while adding the identity server service to collection under configure services
.AddProfileService<ProfileService>();
Update
Session.Abandon(); //is only in aspnet prior versions not in core
Session.Clear();//clears the session doesn't mean that session expired this should be controlled by addSession life time when including service.
I have happened to found a better way i.e. using aspnetuser securitystamp, every time user log-in update the security stamp so that any prior active session/cookies will get invalidated.
_userManager.UpdateSecurityStampAsync(_userManager.FindByEmailAsync(model.Email).Result).Result
Update (final):
On sign-in:-
var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberLogin, false);
if (result.Succeeded)
{
//Update security stamp to invalidate existing sessions
var user = _userManager.FindByEmailAsync(model.Email).Result;
var test= _userManager.UpdateSecurityStampAsync(user).Result;
//Refresh the cookie to update securitystamp on authenticationmanager responsegrant to the current request
await _signInManager.RefreshSignInAsync(user);
}
Profile service implementation :-
public class ProfileService : ProfileService<ApplicationUser>
{
public override async Task IsActiveAsync(IsActiveContext context)
{
if (context == null) throw new ArgumentNullException(nameof(context));
if (context.Subject == null) throw new ArgumentNullException(nameof(context.Subject));
context.IsActive = false;
var subject = context.Subject;
var user = await userManager.FindByIdAsync(context.Subject.GetSubjectId());
if (user != null)
{
var security_stamp_changed = false;
if (userManager.SupportsUserSecurityStamp)
{
var security_stamp = (
from claim in subject.Claims
where claim.Type =="AspNet.Identity.SecurityStamp"
select claim.Value
).SingleOrDefault();
if (security_stamp != null)
{
var latest_security_stamp = await userManager.GetSecurityStampAsync(user);
security_stamp_changed = security_stamp != latest_security_stamp;
}
}
context.IsActive =
!security_stamp_changed &&
!await userManager.IsLockedOutAsync(user);
}
}
}
*
Hook in the service collection:-
*
services.AddIdentityServer()
.AddAspNetIdentity<ApplicationUser>()
.AddProfileService<ProfileService>();
i.e. on every login, the security stamp of the user gets updated and pushed to the cookie, when the token expires, the authorize end point will verify on the security change, If there is any then redirects the user to login. This way we are ensuring there will only be one active session

Returning multiple Parse classes in one PFQuery?

I currently have my parse classes set up as follows
User - objectid, usename, password, location
Profile - birthday, weight, height, et....
Settings - user app preferences such as "Show my location"
Both profile and settings have a pointer to the user objectid called "user"
Is there anyway I can call a query knowing the Users.objectid that returns both Profile and Settings?
I have played around with includes key and matches query but only get back empty results.
If it isn't possible is there a way to execute a function once both queries have completed? (using getFirstObjectInBackgroundWithBlock)
Any help would greatly be appreciated.
No, but you could hide the combination in a function....
function profileAndSettingsForUser(user) {
var profiles;
var profileQuery = new Parse.Query("Profile");
profileQuery.equalTo("user", user);
return profileQuery.find().then(function(result) {
profiles = result;
settingsQuery = new Parse.Query("Settings");
settingsQuery.equalTo("user", user);
return settingsQuery.find();
}).then(function(settings) {
return profiles.concat(settings);
});
};
You could even locate that function in the cloud, so to hide the combination from the client.
Parse.Cloud.define("profileAndSettingsForUser", function(request, response) {
// we could pass a userId in params, then start by querying for that user
// or, if we know its always the current user who's calling for his own profile and settings...
var user = request.user;
profileAndSettingsForUser(user).then(function(profileAndSettings) {
response.success(profileAndSettings);
}, function(error) {
response.error(error);
});
});

Create new PFUser using code without logging in as this new PFUser

I am creating a new PFUser to be a "sub user" of the currently logged in user.
For example Master is logged in and decides to create a Junior User.
The Junior user will have a different PFRole to the Master.
I can create a new User via
var newUser = PFUser()
newUser.email = "someemail#gmail.com"
newUser.username = "Demo Subuser"
newUser.password = "12341234"
newUser.signUpInBackgroundWithBlock { (newUser, error) -> Void in
println(error)
}
The user is created, but the problem is I am logged out of the master account and logged in as the Junior user.
How can I create a new PFUser without it logging into that account.
Ideally, I want to send an email to the new subuser stating, welcome here is your username and details.
Maybe I should be using CloudCode ?
Thanks
Create a Cloudcode function and upload it to Parse
Parse.Cloud.define("createNewUser", function(request, response) {
// extract passed in details
var username = request.params.username
var pw = request.params.password
var email = request.params.email
// cloud local calls
var user = new Parse.User();
user.set("username", username);
user.set("password", pw);
user.set("email", email);
user.signUp(null, {
success: function(user) {
//response.error("working");
// do other stuff here
// like set ACL
// create relationships
// and then save again!! using user.save
// you will need to use Parse.Cloud.useMasterKey();
},
error: function(user, error) {
//response.error("Sorry! " + error.message);
} });
});
Call the cloud code function from within your app via. Pass in details to the clod function using a Dictionary, ["username":"", "password":""] etc
PFCloud.callFunctionInBackground

Parse iOS SDK: How to set user "online" status?

Scenario = I have an app that allows users to log in and view other users who are also "online". In order to set each user's online status I believe that I should set the code...
[PFUser currentUser] setObject:[NSNumber numberWithBool:YES] forKey:#"isOnline"];
[[PFUser currentUser] saveInBackground];
at certain times in the application when the user uses the app. Probably the appDelegate but I'm not sure.
(if this is not correct then please correct me)
Question = Where should this code be set inside the application so that it always keeps track of when a user is "online" and when they are "offline"?
(please include method names)
The most reliable way to handle this is to create a column named lastActive of type Date, then create a Cloud Code function to update this with the server time (so clock differences isn't an issue).
Then to get "online" users just have another Cloud Function that does a query where lastActive is greater than now - some time window like 2 minutes.
var moment = require("moment");
Parse.Cloud.define("registerActivity", function(request, response) {
var user = request.user;
user.set("lastActive", new Date());
user.save().then(function (user) {
response.success();
}, function (error) {
console.log(error);
response.error(error);
});
});
Parse.Cloud.define("getOnlineUsers", function(request, response) {
var userQuery = new Parse.Query(Parse.User);
var activeSince = moment().subtract("minutes", 2).toDate();
userQuery.greaterThan("lastActive", activeSince);
userQuery.find().then(function (users) {
response.success(users);
}, function (error) {
response.error(error);
});
});
Your client will want to call the registerActivity Cloud Function every 1.5 minutes to allow some overlap so users don't appear to go offline if their internet is a bit slow.
Of course you can adjust the time windows to suit your needs. You could also add the ability to filter which users are returned (e.g. online friends only).

Resources