Ensuring unique username on creating a new user in Firebase (Swift) [duplicate] - ios

This question already has answers here:
How do you prevent duplicate user properties in Firebase?
(2 answers)
Closed 6 years ago.
I am a newbie to Firebase so any hint will be appreciated. Right now I am using Firebase Login & Auth to handle all the authentication for me. But I want to store some user data, like username DOB, created_at... I have created a users node, and I am appending children nodes to users using the unique id I get on sign up or login. However I want to ensure that there are no duplicate usernames in the system. How can I check if a username already exists before writing to the server?

I'm currently investigating the same thing. My issue is that I also want persistence enabled, so I need to be aware of what can be accessed offline. If you're using persistence, I would also recommend disallowing particular operations such as checking username existence if your client is offline, which you can do by listening to ".info/connected" as further detailed here:
https://firebase.google.com/docs/database/ios/offline-capabilities#section-connection-state
My personal workflow for this is as follows:
Login to user's account
Check if the user already has a username
Check the firebase database to see if their user details includes a username:
DB/users/*userUID*/username != nil
If they don't have a username, then prompt them to set a username.
When they set their username, check if the username exists in:
DB/usernames/*username*/ != nil
If it doesn't exist, then write the username and userId in the two database locations checked above.
eg.
user.uid = wewe32323
username = scuba_steve
DB/usernames/scuba_steve = wewe32323
DB/users/wewe32323/username = scuba_steve
So now you have the DB/usernames reference that you can check quickly to see if anyone has a username already, and you also have DB/users/ where you can quickly find a username for a given user.
I won't say this is fool-proof, as I still a few concerns around concurrent requests. My current issue I'm investigating is that lets say you delete the username association to a particular user, the user can depend on their local copy of the database to incorrectly assert that they are still assigned to that username.
You could look into the database write rules to disallow anyone to modify existing data (enforcing that you can only write to the DB/usernames directory if there is no existing data. This would prevent overriding of whoever sets the username first, which I think is an important step.
It may also be worth investigating Transactions:
https://firebase.google.com/docs/database/ios/save-data#save_data_as_transactions
But I believe correct write rules as mentioned in the paragraph above should allow dependable writing.

You can check if the username is stored like this:
let username = "fred"
self.ref.child("users/\(username)").observeSingleEventOfType(.Value, withBlock: { snapshot in
if snapshot.exists() {
If the snapshot returned in the closure exists, then the username "fred" exists.

Related

UserManager.AddPassword doesn't work as expected

I am using ASP.NET Identity for membership in MVC5 and have a page for the admin user to manage users and roles. I am trying to allow the admin to change the password for any other user if he needs to do so. I am using this code to perform the task:
userManager.RemovePassword(userId);
userManager.AddPassword(userId, newPassword);
Seems simple enough, but when I try to login with the new password I just created, I can't; I get the invalid password message.
I tried to investigate this issue further by trying to find out how it hashes passwords and discovered this:
string hashed = userManager.PasswordHasher.HashPassword("some password");
Firstly, I am assuming this password hasher is what AddPassword() is using internally.
Secondly, I noticed that each time I run HashPassword('some password'), I get a completely different hash returned to me.
I don't get it...
Alright, I found the problem, it's because of the following line of code in the AccountController:
var user = await UserManager.FindAsync(model.Email, model.Password);
What's happening here is that FindAsync requires a username, not an email and with the user I was testing, the username and email were different.
I wondered if maybe I changed the code in the Login action to pass in the email address and didn't remember... so I created a new MVC5 project and used Nuget to updated to the latest version... well, it looks like that's a bug! Yes, the default implementation actually passes in the email address to FindAsync. Obviously this is because when you register a new user, it sets up the username to be the email address (so they are the same and it doesn't matter). However, they didn't take into account the fact that someone may want to change their username!
Solved!

DataSnap and Database Connection / Login

I am trying to work out the "right" way to establish a connection to my database from the server of my DataSnap application.
Each (most) of my tables in the database have fields (whose values are set via a trigger on insert and update) called Updated and Created (this is the current time stamp when the record is written) and updated_by and created_by which (should) contain the currently logged in user.
I want the user to 'login' from the client side such that these fields reflect the user who has logged in (and by extension, I'll get user authentication from the database, not just from the server). I can handle the authentication to the server itself from the client ok handling the OnUserAuthenticate and OnUserAuthorize events on the server. I am trying to then pass the credentials to my database so that the triggers can set the fields stated above correctly.
So what is the way to approach this scenario? I am wonder if the DSAuthProxyUser and DSAuthProxyPassword from the client can be used but I can't find much (any) documentation on how I would use that. Do I establish a new connection for every user who connects? This seems most logical to me. I'm not going to have a lot of concurrent users. Tops 30. Most likely 5-10. But what is the "normal" way this is done? I don't want to (hope I don't have to) pass in the username to each of my insert/updates to set the values in the tables.
I hope I have explained my situation clearly.
Thanks
I haven't used it yet, but it looks to me that the RDB$SET_CONTEXT() and RDB$GET_CONTEXT() introduced in Firebird 2 are what you need. Using these functions, you can set (and get) additional information specific to the user session (namespace USER_SESSION) or the current transaction (namespace USER_TRANSACTION). You can also retrieve additional system information for the current session (namespace SYSTEM), but that is probably not relevant for your case.
What you would need to do is call the RDB$SET_CONTEXT() method in that OnUserAuthorize event, eg using (as a query):
SELECT RDB$SET_CONTEXT('USER_SESSION', 'actualuser', '<name of user')
FROM RDB$DATABASE
Here 'actualuser' is the context variable we use. In your triggers you can then retrieve the name (assuming PSQL, with a declared variable actualuser)
actualuser = RDB$GET_CONTEXT('USER_SESSION', 'actualuser');
You can then use actualuser in the rest of your trigger. Just make sure you also account for the case where the context variable is not set (eg an administrator making direct changes to the database or something like that).
Firebird has the CURRENT_USER keyword which can be used in SQL.
The example below is based on http://www.firebirdsql.org/refdocs/langrefupd15-current_user.html
create trigger bi_customers for customers before insert as
begin
New.created_by = CURRENT_USER;
end
To have the user name for updates, simply declare a before update trigger like
create trigger bi_customers for customers before update as
begin
New.updated_by = CURRENT_USER;
end
This solution requires a 1:1 mapping of database users to external users. In the most simple implementation, it means that the DataSnap session authenticates the user credentials against the database.
In your description however, it seems to be a two step authentification (first against the DataSnap layer, then against the database). I am not sure how this can be done regarding safe password handling, and if you plan to have a separate user/password table only for the first authentication stage (DataSnap) and a user login mapping from DataSnap to database as some kind of 'decoupling'.

How to create a "backdoor" for administrator, to be able to log in as anohter user and see information?

I am creating an online survey tool.
As an administrator, i would like to see what the users have answered and also be able to answer on their behalf. The system get's a users answers and other information based on his/her username, when they are logged in, using the built in membership provider.
There are currently three roles: Administrator, Moderator and Respondent
If i would like to show my administrator a list of users,
how would it be possible to create a "backdoor" for the administrator, so that he can "log" in as the user, see the users answers etc ? (Just like the user would be able to if he was logged in to his own account).
When answering and retrieving quyestions, the system is bound to `User.Identity.Name
My suggestion on how to solve this:
Currently, when i want to retrive a users answers i use the following code:
Firma_ID = db.Firma.Single(x => x.CVR_nummer == User.Identity.Name).firma_id;
var answers = db.Tabelform_Answers.Where(x => x.question_id == model.Question_ID && x.respondent == Firma_ID);
This is because i have a table named Firma, that has a column referencing to a users Name, called CVR_Nummer. I then retrieve all the records in the Tabelform_Answers table, that match question_id and Firma_ID (A users answers for a specific question).
Instead of using `Firma_ID = db.Firma.Single(x => x.CVR_nummer == User.Identity.Name).firma_id;
to retrive the Firma_ID of a given user, i could store it in the Session upon Login. When i want to view a specific users Answers as Administrator, i would then just change Firma_ID in the Session. Changing Firma_ID in the Session would only be allowed through a controller which has the following code:
[Authorize(Roles = "Administrator")]
Also, i would set the Session timeout to be the same as the Authentication timeout.
Can somebody tell me which pros and cons of this solution? Are there any other ways of storing a "global" variable for a Session? (Firma_ID)?
Thanks
If you only need to log in as your users, I went for a ticket-method.
I have a special login-page that can take a ticket-id. This ticket is created in the admin-gui when the admin wants to log in as another user. The login-page checks the ticket in the database, logs in the wanted user, and then deletes/marks the ticket as used. As an added security, a ticket is only valid for 10 seconds after creation.
Another option is to make answers from users available from the admin-gui...
also you can do in your log-in script override
so you have at present something like
if user name and password match string then user is logged in and based on this you get user permissions
instead have admin page,
where you can select user and then you can apply permissions of the user instead of admin.

sfGuardAuth across multiple apps

I've got 3 apps: Backend, Frontend and Members.
And I've got 2 credentials: Administrators and Members.
Administrators are imported by default but Members are created in the Backend.
When a Member is created, an event handler also inserts this Member as a sf_guard_user and of course, the proper relations in sf_guard_user_group and sf_guard_user_permission.
That's the boring part, now the fun:
Frontend is not secured, but Members is and using these credentials: [administrator, member].
According to all this, Members created in the Backend that also get inserted (correctly as far as I can tell) should be able to login to the Members secured app, since they get the member group/permission/credential.
This is not happenning, the only ones that can login to the Members app are the administrators, which is not wrong, but either is the fact that correctly created Member users can't login to it.
The error thrown by the guard is the classic: The username and/or password is invalid.
Now that I edit the error, the salt comes to mind: How would one emulate the inserting of the salt as the guard does it? Maybe that's what I'm not inserting correctly (or inserting at all) and thus the password invalid error (and in fact everythine else I've described is ok! omg)
And that's my problem.
Thanks in advance :)
[administrator, member] means both are required, I believe.
I think you want [[administrator, member]] for the credential requirement.
Also, yes, you will want to make sure you use a salt, and set the algo.
parent::_set('password', call_user_func_array($algorithm, array($salt.$password)));
Salt before password, as well.

Generate temporary URL to reset password

I am looking to implement a Forgot Password feature on my website. I like the option where an email containing a temporary one-time use URL that expires after some time is sent to the user.
I have looked at the following pages to get these ideas but I am not sure how to implement this using ASP.NET and C#. As one of the users indicated, if I can implement this without storing this information inside the database, that will be ideal. Please advise.
Password reset by emailing temporary passwords
Thanks.
Probably the easiest way is going to be to modify your users table to add 2 extra columns, OR if you don't want to modify the existing table you could add a new dependent table called "UserPasswordReset" or something like that. The columns are like this:
PasswordResetToken UNIQUEIDENTIFIER,
PasswordResetExpiration DATETIME
If you go with the additional table route, you could do also add the UserID column, make it a primary key and a foriegn key reference back to your users table. A UNIQUE constraint would also be recommended. Then you simply use a Guid in your asp.net application as the token.
The flow could be something like this:
User requests password reset for their account
You insert a new record in the table (or update their user record) by setting the PasswordResetExpiration to a date in the future (DateTime.Now.AddDays(1)), and set the token to Guid.NewGuid()
Email the user a link to your ResetPassword.aspx page with the guid in the query string (http://www.yoursite.com/ResetPassword.aspx?token=Guid-here)
Use the ResetPassword.aspx page to validate the token and expiration fields. (I.E. Make sure DateTime.Now < PasswordResetExpiration)
Provide a simple form that allows the user to reset this password.
I know you wanted to avoid modifying the database, but it really is probably the simplest method.
#Alex
You can also use System.Security.Cryptography classes in .NET for the hash algorithms. For example:
using System.Security.Cryptography;
...
var hash = SHA256CryptoServiceProvider.Create().ComputeHash(myTokenToHash);
...
Here, the System.Guid class in your friend, as it will generate a unique (well, unique enough) 128-bit number:
Generate a new Guid ( System.Guid.NewGuid() )
Store that Guid somewhere (Application object maybe?)
Send a custom URL in an email with that Guid
When the user hits the site, make them enter the password you sent in the email
If the passwords match, go ahead and force them to enter a new password
I used a Hashing Class to create unique automatic logins made up of the current date/time and the users email address:
string strNow = DateTime.Now.ToString();
string strHash = strNow + strEmail;
strHash = Hash.GetHash(strHash, Hash.HashType.SHA1);
get the Hash Class from: http://www.developerfusion.com/code/4601/create-hashes-md5-sha1-sha256-sha384-sha512/
Then just take it from the URL using:
if (Request.QueryString["hash"] != null)
{
//extract Hash from the URL
string strHash = Request.QueryString["hash"];
}
I would definitely include the database in this process. Once a reset is requested, it's a good idea to indicate that the account is locked out.
For example, if you are changing your pw because you think your account may have been compromised, you definitely don't want it to remain accessible while you go about the change process.
Also, inclusion of "real" information in the reset token could be decoded if someone really wants it and has the horsepower. It would be safer to generate a random string, save it in the db in the row for that user, and then key back to it when the link is clicked.
This gives you two things:
1) There's nothing to decrypt, and therefore nothing of value can be gained from it.
2) The presence of the token in the user record indicates that reset is in progress and the account should be treated as locked out.
The goal of sending some data|string to user email is validation of account owner. Please care about some points:
Avoid sending important information in reset or activate link.
It's best way to store unique string data conjunction with user
account and send it as that link. but be aware if you send just one
section as link to user email and just check it in page, your
application may be in dangerous by brute-force or dictionary
attacker. It's enough to check a list of string to find some links
and change password. I know that has a little chance, but not zero.
Result:
I think it's better if you
combine user email with string link then encrypt them
(not hash because hashed value can't be reverse) and send to user
email.
User click and your page get the encrypted value.
decrypt value.
extract user email.
find email in database.
compare string from received link with other one attached to user
email in database.
Good luck.
I'd use a hash code to validate details in the password reset url. This can all be done without writing anything to the DB or sending any privileged info to an attaker.
To briefly explain normal password salt and hashing; say the salt is 1111 and the pasword is password, you'd concatenate the two and hash the string 1111password, say this gives you a hash of 9999, you'd then store the original salt 1111 and hash 9999 in your user record.
When you are validating a password you use the stored salt, concatenate the password attempt, hash it and compare with the stored hash. For example asecret becomes 1111asecret but hashes to 8888. This doesn't match the original hash so the password match fails.
Of course the salt and hash would normally be properly generated and calculated with established crypto libraries (don't invent your own!).
For the password reset URL I'd put in the unique identifier for the user, i.e. email address, the date the request is made, and a new hash. This hash would be generated from those details concatenated together plus the salt and hash already stored for the user.
For example:
Email: user#example.com
Request Date: 2014-07-17
Salt: 1111
Hash: 9999
Generate a new hash of those concatenated, i.e. 'user#example.com2014-07-1711119999', say this gives a hash of 7777.
The URL I then generate would then have the email, request date and the new hash:
https:\\www.example.com\ResetPassword?email=user#example.com&requestdate=2014-07-17&hash=7777
The server will combine the email and supplied date with it's salt and hash and confirm the hash it generated is the same as the supplied one. If this is Ok then it will show the reset form with the same three parameters hidden behind it, otherwise an error. These get resubmitted and rechecked when the new password is entered to prevent that form being spoofed.
The email address needs to be supplied to make the request and it is only sent out in an email to the same address. the date is hardly priveleged info and the hash is not reversible so gives nothing anyway. Nothing has been written to the database and any tampering with the parameters causes the hash to fail and the URL to report an error.
There is an issues with this approach. A safe hash makes the token really long. Either you integrate the salt into the hash itself (makes it about 20 charactes longer), or you store this unique salt in the database. If you store the salt in the database, you could as well store a random token which is not derrived from any existing
Depending on your needs, you could encrypt information, in a format similar to the following format
(UserId)-(ExpireDate)
Encrypt the data, make that the link, then decrypt the data and take action from there...
Crude, but most likely usable, and not requiring DB usage

Resources