I am registering user using Email/Password Sign-In-Method. So after successfully registration and loge in, I can't read and write my data in Firebase Real Time.
I'm new to Firebase so I can't really figure out what the main problem is or which steps I have missed.
This rule is obviously working fine:
{
"rules": {
".read": "auth == null",
".write": "auth == null"
}
}
However, this rule is not working, although my user is already registered and has got a UID.
{
"rules": {
".read": "auth != null",
".write": "auth != null"
}
}
It always shows the following error:
Exception occured while processing the request.
Url: https://App-Name-6ssd2.firebaseio.com/Appointments/-MUAG-CHWBFSv-Vrtsoc/.json?print=silent
Response: {
"error" : "Permission denied"
}
What I currently want is to allow ANY registered user to read/write everybody's information, but it always denied the permission.
Screenshots:
My implementation for getting Firebase Data.
The error is also pointing to the this method.
public async Task<List<Appointment>> GetUserAppointment(string userId)
{
var appointments = (await Firebase.Child("Appointments")
.OnceAsync<Appointment>()).Where(a => a.Object.UID == userId).Select(item =>
new Appointment
{
UID = item.Object.UID,
AppointmentID = item.Object.AppointmentID,
Title = item.Object.Title,
Date = item.Object.Date,
Time = item.Object.Time,
Status = item.Object.Status,
ReasonText = item.Object.ReasonText
}).ToList();
return appointments;
My implementation when a user SinUp for the first time.
public async Task<string> SignUpWithEmailAndPassword(string email, string password)
{
try
{
var signUpTask = await auth.CreateUserWithEmailAndPasswordAsync(email, password);
var user = signUpTask.User;
var token = await auth.CurrentUser.GetIdToken(false).AsAsync<GetTokenResult>();
await SendEmailVerification();
return token.Token;
}
catch (FirebaseAuthInvalidUserException e)
{
e.PrintStackTrace();
return string.Empty;
}
catch (FirebaseAuthInvalidCredentialsException e)
{
e.PrintStackTrace();
return string.Empty;
}
catch (FirebaseAuthUserCollisionException existEmail)
{
existEmail.PrintStackTrace();
return string.Empty;
}
}
In order to check whether a user is authenticated/verified or not, i did the debugging and as a result... I can see the same UID in my console as it is in Firebase -> Authentication -> Users.
Here is the screenshot.
Debugging GetUserAppointment(string userID) to check whether the current user is set.
It's because you are not authorized to the Database, check the Rules Tab in the Realtime database.
Open firebase, select database on the left hand side.
Now on the right hand side, select Realtime database from the dropdown and change the rules to allow anyone to write the Database
{
"rules": {
".read": true,
".write": true
}
}
Your rule is:
{
"rules": {
".read": "auth != null",
".write":"auth != null"
}
}
This means only authorized user's can write and read the Data.
Related
I have two different rules for my Firebase Realtime database.
One enables only authenticated users to read and write, while the other one is more complex. It only enables authenticated users to read and write data from the "rooms" node, and it enables users to work only with their own data in the "users" node.
However, when I use the second rule, I get the following error for some reasons I don't know.
System.NullReferenceException: Object reference not set to an instance
of an object
My two rules are:
{
"rules": {
".read": "auth != null",
".write": "auth != null"
}
}
{
"rules": {
"users": {
"$uid": {
".write": "$uid === auth.uid",
".read": "$uid === auth.uid"
}
},
"rooms": {
".read": "auth != null",
".write": "auth != null"
}
}
}
My code which contains the error-causing line (3rd line):
void HandleValueChanged(object sender, ValueChangedEventArgs args) {
if(auth.CurrentUser != null) {
currentRoom = args.Snapshot.Child(auth.CurrentUser.UserId).Child("currentRoom").Value.ToString(); //this line causes the error
FirebaseDatabase.DefaultInstance.GetReference("rooms").Child(currentRoom).Child("members").Child(auth.CurrentUser.UserId).Child("type").GetValueAsync().ContinueWith(task => {
if (task.IsFaulted) {
// Handle the error...
} else if (task.IsCompleted) {
DataSnapshot snapshot = task.Result;
charType = snapshot.Value.ToString();
}
});
}
}
I tried to do some things in the rules playground in Firebase, but I didn't manage to get a solution there.
The HandleValueChanged method is working with the users node:
FirebaseDatabase.DefaultInstance.GetReference("users").ValueChanged += HandleValueChanged;
Your code tries to read the entire users node:
FirebaseDatabase.DefaultInstance.GetReference("users").ValueChange
But your security rules only allow a user read access to their own node:
{
"rules": {
"users": {
"$uid": {
".write": "$uid === auth.uid",
".read": "$uid === auth.uid"
}
},
Since the user doesn't have access to all of /users, reading from that node is rejected.
The problem is that rules are not filters, so they don't filter data on their own. Instead they merely ensure that the operations that the user performs don't conflict with the rules you've set for them. And since you didn't grant anyone read access to (all of) /users, the read is rejected.
In your case I'd highly recommend simply changing the code to only access the note that the user does have access to:
String uid = Firebase.Auth.FirebaseAuth.DefaultInstance.CurrentUser.UserId;
FirebaseDatabase.DefaultInstance.GetReference("users").Child(uid).ValueChanged = ...
In some other cases you might be able to combine a query and security rules to accomplish your needs. The query only requests the data that the user has access to, and the security rules ensure that the query matches those rules. For more on this, see the documentation on query based rules.
I am using Firebase to allow the user to login to a Unity game via Facebook. It is working fine, but I cannot get access to the user's email by using this parameter auth.CurrentUser.Email. Also, the email is not stored in Firebase Authentication Console. The email can be stored/accessed succussfully when I use other sign-in methods, such as email and google.
Here is my code:
public void SignInFacebook()
{
var perms = new List<string>() { "public_profile", "email", "user_friends" };
FB.LogInWithReadPermissions(perms, AuthCallback);
}
private void AuthCallback(ILoginResult result)
{
if (FB.IsLoggedIn)
{
// AccessToken class will have session details
var aToken = Facebook.Unity.AccessToken.CurrentAccessToken;
// Print current access token's User ID
Debug.Log(aToken.UserId);
// Print current access token's granted permissions
foreach (string perm in aToken.Permissions)
{
Debug.Log(perm);
}
Credential credential = FacebookAuthProvider.GetCredential(aToken.TokenString);
auth.SignInWithCredentialAsync(credential).ContinueWithOnMainThread(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} - {2} - ({1})",
newUser.DisplayName, newUser.UserId, newUser.Email);
});
}
else
{
Debug.Log("User cancelled login");
}
}
And this is what it looks like in the console ("-" is where the email is supposed to be stored. If I use another sign-in method, such as email or google, the email is stored without any issues)
Similar questions were asked about this issue and it was suggested that I change the Account email address setting in Firebase to Prevent creation of multiple accounts with the same email address, but it did not solve the issue.
Thanks!
If your Facebook app is in test mode you must login from your Facebook ID. Go to the settings, scroll down, select Apps and Websites, and click on your app. From there, make sure email address require is enabled.
you can try this
private void FacebookAuthCallback(ILoginResult result)
{
if (FB.IsLoggedIn)
{
FB.API("/me?fields=id,name,email", HttpMethod.GET, FacebookGetInfo);
}
else
{
Debug.Log("User cancelled login");
}
}
private void FacebookGetInfo(IResult result)
{
if (result.Error == null)
{
if (result.ResultDictionary.ContainsKey("email"))
{
string aEmail = result.ResultDictionary["email"].ToString();
return;
}
}
else
{
Debug.Log(result.Error);
}
}
Here is my login code, the problem happens to users redirected to "Profile" after logging in.
protected void UserLogin_Click(object sender, EventArgs e)
{
if (IsValid)
{
var manager = new IdentityModels.UserManager();
IdentityModels.User user = manager.Find(Username.Text, Password.Text);
if (user != null)
{
if (isUserActive(user.PatientId)=="isNotActive")
{
lblError.Text =
"you are no longer active. Please contact your local clinic to find out why.";
return;
}
if (isUserActive(user.PatientId) == "clinicNotActive")
{
lblError.Text =
"Your clinic is no longer active. Please contact your local clinic to find out why.";
return;
}
IdentityModels.IdentityHelper.SignIn(manager, user, RememberMe.Checked);
if (manager.IsInRole(user.Id,"Administrator") || manager.IsInRole(user.Id,"Staff") || manager.IsInRole(user.Id,"Physician"))
{
Response.Redirect("Dashboard");
}
if (Request.QueryString["Profile"] != null)
{
IdentityModels.IdentityHelper.RedirectToReturnUrl(Request.QueryString["Profile"], Response);
}
else
{
Response.Redirect("Profile");
}
}
else
{
ModelState.AddModelError("", "Invalid username or password");
lblError.Text = "Invalid username or password";
}
}
}
here is my page load code on the Profile page:
var manager = new IdentityModels.UserManager();
IdentityModels.User user = manager.FindById(HttpContext.Current.User.Identity.GetUserId());
if (user == null)
{
var ex = new Exception("patient was null, BUT TRIED SIGNING IN NOW" + UserAccess.GetUserId().ToString());
Elmah.ErrorSignal.FromCurrentContext().Raise(ex);
Response.Redirect("Login");
}
Elmah logs show the exception "patient was null, BUT TRIED SIGNING IN NOW 0".
So if my users are signing in successfully, which they must be because they are hitting the profile page, then why do some of them hit this error. Why is the user null?
I just can't figure it out, why it effects some but not all. When I republish the website all users can then login for a few minutes, sometimes a few hours, then it starts again.
Try using User.Identity rather than HttpContext.Current.User.Identity. I've seen some cases where the context (which is based on ASP.NET's session) gets out of sync with Identity's tokens.
OK guys, here is the answer.
Change the session state from InProc, in my case to SQLServer, it's been 22 hours since a login redirect, which hasn't happened before, so I think it's safe to say the problem is solved and that was the answer.
I'm using the following sample code from the DotnetOpenAuth Samples (OpenId Controller in OpenIdProviderMvc)
public ActionResult ProcessAuthRequest() {
if (ProviderEndpoint.PendingRequest == null) {
return this.RedirectToAction("Index", "Home");
}
// Try responding immediately if possible.
ActionResult response;
if (this.AutoRespondIfPossible(out response)) {
return response;
}
// We can't respond immediately with a positive result. But if we still have to respond immediately...
if (ProviderEndpoint.PendingRequest.Immediate) {
// We can't stop to prompt the user -- we must just return a negative response.
return this.SendAssertion();
}
return this.RedirectToAction("AskUser");
}
private bool AutoRespondIfPossible(out ActionResult response)
{
if (ProviderEndpoint.PendingRequest.IsReturnUrlDiscoverable(OpenIdProvider.Channel.WebRequestHandler) == RelyingPartyDiscoveryResult.Success
&& User.Identity.IsAuthenticated) {
if (ProviderEndpoint.PendingAuthenticationRequest != null) {
if (ProviderEndpoint.PendingAuthenticationRequest.IsDirectedIdentity
|| this.UserControlsIdentifier(ProviderEndpoint.PendingAuthenticationRequest)) {
ProviderEndpoint.PendingAuthenticationRequest.IsAuthenticated = true;
response = this.SendAssertion();
return true;
}
}
if (ProviderEndpoint.PendingAnonymousRequest != null) {
ProviderEndpoint.PendingAnonymousRequest.IsApproved = true;
response = this.SendAssertion();
return true;
}
}
response = null;
return false;
}
However, I don't want to ask the user anything. I'm trying to set up a web application portal that should automatically respond positively to the RP if the user is logged in (which he is). Yet AutoRespondIfPossible returns false, because ProviderEndpoint.PendingRequest.IsReturnUrlDiscoverable returns false and I'm not sure why. What action should I be taking here?
Logs:
RP: http://pastebin.com/0EX2ZE1C
EP: http://pastebin.com/q5CPrWp6
Previous related questions:
SSO - No OpenID endpoint found
OpenIdProvider.GetRequest() returns null
Does an OpenID realm have to be the base URL of the web site?
IsReturnUrlDiscoverable performs what OpenID calls "RP Discovery". And it's important anyway, but particularly if you will be auto-logging users in, it's critical for security. The fact that it's returning false tells you the RP needs some work to do this correctly.
This blog post explains what the RP must do to pass "RP Discovery" tests.
I am using Azure Mobile Services (following the standard Azure TodoItems tutorial), and the most basic GET method that they provide is:
public IQueryable<MyModel> GetAllMyInfo()
{
return Query();
}
This works, but I am trying to extend it so that the method will only return MyModel data for an authenticated user (identified by the X-ZUMO-AUTH authentication header standard for Mobile Service API calls). So I modified the code for:
public IQueryable<MyModel> GetAllMyInfo()
{
// Get the current user
var currentUser = User as ServiceUser;
var ownerId = currentUser.Id;
return Query().Where(s => s.OwnerId == ownerId);
}
This also works when a valid auth token is passed. However, if an invalid auth header is passed, then the currentUser is null, and the query fails (obviously). So I am trying to check for null and return a BadRequest or a 403 HTTP code. Yet a simple `return BadRequest("Invalid authentication") gives a compilation error:
public IQueryable<MyModel> GetAllMyInfo()
{
// Get the current user
var currentUser = User as ServiceUser;
if(currentUser == null) {
return BadRequest("Database has already been created."); // This line gives a compilation error saying I need a cast.
}
var ownerId = currentUser.Id;
return Query().Where(s => s.OwnerId == ownerId);
}
Does anyone know how to check for a valid authentication token and return a 403 on this method (which wants an IQueryable return type?
You can use the [AuthorizeLevel] attribute on this method to indicate that a valid token must be present in order for the method to be invoked. It will return a 401 if not.
So your full method would be:
[AuthorizeLevel(AuthorizationLevel.User)]
public IQueryable<MyModel> GetAllMyInfo()
{
// Get the current user
var currentUser = User as ServiceUser;
var ownerId = currentUser.Id;
return Query().Where(s => s.OwnerId == ownerId);
}
Please note that for the Azure Mobile Apps SDK (not Mobile Services), the above attribute is simply replaced with [Authorize].
I know this is a bit late, but will document here for you and others that may come looking for a similar problem.
(While agreeing with Matt that a 403 could/should be achieved with a [Authorize] attribute, the question is regarding returning a different HttpStatusCode OR IQueryable)
I had a similar scenario where I needed to validate some query parameters and either return my results or a HttpError (in my case I wanted a 404 with content).
I found 2 ways, either keeping the return as IQueryable<T> and throwing a HttpResponseException or changing the return to IHttpActionResult and returning normal with HttpStatusCode or Ok(Data).
I found to prefer the later as throwing an Exception would be breaking the execution while in debug and not a very pleasant development experience.
Option 1 (Preferred)
//Adding Return annotation for API Documentation generation
[ResponseType(typeof(IQueryable<MyModel>))]
public IHttpActionResult GetAllMyInfo()
{
// Get the current user
var currentUser = User as ServiceUser;
if(currentUser == null) {
return BadRequest("Database has already been created.");
}
var ownerId = currentUser.Id;
return Ok(Query().Where(s => s.OwnerId == ownerId));
}
Option 2 (Throwing Exception)
public IQueryable<MyModel> GetAllMyInfo()
{
// Get the current user
var currentUser = User as ServiceUser;
if(currentUser == null) {
throw new HttpResponseException(System.Net.HttpStatusCode.BadRequest)
// Or to add a content message:
throw new HttpResponseException(new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.BadRequest) {
Content = new System.Net.Http.StringContent("Database has already been created.")
});
}
var ownerId = currentUser.Id;
return Query().Where(s => s.OwnerId == ownerId);
}