Include hex key validation in signup process - node.js

I am following a tutorial to create a web app to do user authentication (signup/login): https://scotch.io/tutorials/easy-node-authentication-setup-and-local
I was wondering how I could add validation to the signup part. Let me clarify. I will be selling a product to users. People who buy the product will receive a 128-bit HEX key with the product so that they can register it.
I have a list of, let's say, 1000 of those keys that I generated. How can I ensure that the user registering is inputting a valid key? (a previously generated one) I am pretty sure I would have to put those keys in a database, but what next?
Thanks

Simple. Store the key reference along with the user(since they bought your product I am assuming you have their details previously) in a table or a document or any other. So that you can validate that the key is valid and allowed for that user in a single lookup. It has nothing to do with the tutorial mentioned. HEX key can be anything a coupon for example.
Cheers.

Related

How to generate one time url to get a feedback in nodejs

I am developing an app for traveling agents in an insurance company with MEAN stack. When a traveling agent is done with the customer, a unique URL must be emailed to the customer to get feedback. How can I generate that unique one-time URL? Or is there any other way to achieve this without creating an account for the customer? (like sending a responsive email to the customer and get feedback)
One way you could do this is to 'encrypt' the query string. So lets say you want this to be a 'one-time' URL.
On your node server, you generate a one time string like customerID=12343&timestap=time&action=something
Next, on the server you would run the string through an encryption algo to encrypt the string so after it may look something like sd8753454sd548787e54sd54SDe85432 <--(this is not valid just an example but would be unreadable)
Now, the email you send out with the link would be something like https://example.com/validate/sd8753454sd548787e54sd54SDe85432
The validate route will require the string, grab that and then 'decrypt' the string. You should now have the valid string customerID=12343&timestap=time&action=something, so your DB updates etc and flag in the DB that it was clicked OR use the timestamp to check if the URL is expired
This is just one way, and when talking about encryption and how strong you need it is a huge topic, this is just one method of many ways.

Generate secure shareable URL for access to web app (NodeJS)

I am building an application in NodeJS + Express where teams can share information with one and other and chat (kind of like an internal messaging forum).
Sometimes there is a need for the team's clients to view and edit some of this stored information on a case by case basis (e.g. a client asks a question and wants to message back and forth with the team, using my app). I don't want the client to have to sign up for an account in this case.
I am thus wondering what is the most secure strategy for generating a URL where anyone with the URL can view and edit a document/POST data to my app within the confines of a single document, without signing in?
(I've seen a couple of posts on this topic but they're quite old and don't focus on this specific case.)
First of all, I can absolutely understand the benefits, but still it is not an optimal idea. However, I would like to summarize some thoughts and recommendations that will help you with the development:
A link like this should not be able to perform critical actions or read highly sensitive data.
Access should be unique and short-lived. For example, the customer could enter his e-mail address or mobile phone number and receive an access code.
If you generate random URLs, they should be generated in a secure random manner (e.g. uuid provides a way to create cryptographically-strong random values).
If I had to design this I would provide as little functionality as possible. Also, the administrator would have to enter a trusted email address and/or mobile phone number when releasing the document. The URL with a UUIDv4 is then sent to this channel and when the customer clicks on the link, he gets a short-lived access code on a separate channel if possible (on the same channel if only one was configured). This way you prevent the danger of an unauthorized person accessing the document in case a customer forwards the original URL out of stupidity.

How to implement expiring to the account activation link?

I am trying to implement expiring to the activation link I send to the user's email when they register a new account.
The link should expire after 24 hours if the user doesn't click on it.
Up until this point I am able to send the link to the user's email upon registration, the link looks like this 'http://localhost:3000/auth/activate/${verificationKey}', everything works smoothly.
But like I said, I want the link to expires after 24 hours and I just don't have any idea of how to do it.
From what I've gathered so far I think one way to do this would be to delete the verificationKey value from my User entity/model after 24 hours, then if the verificationKey value is falsy I need to send another link to the user.
Now my question is, how do I check if a value (in this case user.verification_key) has been generated for over 24 hours?
This is the relevant code to register a new user:
const user = new User();
user.username = username;
user.password = await bcrypt.hash(password, salt);
user.is_verified = false;
user.verification_key = cryptoRandomString({
length: 10,
type: 'url-safe',
});
Some people have suggested to use Redis for this, which I don't know anything about other than it's a in-memory store, and while I'm ok reading more about this tool, I would like to know if there are other ways to do this. I don't know if by installing Redis I would need extra configuration for the server when I host my app, I'd like to avoid that.
Since you already have some database set up, it makes sense to store some verification key and an expiration time for it. You don't need to actually delete that verification key... just need to store when it expires.
Perhaps you have a separate model for RegVerificationKey, with fields key (randomly generated string), expiration (set to a date/time 24 hours after you create it), and userId (the ID of the user this is associated with). Create this key. When you go to activate, just check to see if there is a key associated with the requested user that hasn't expired yet.
Some people have suggested to use Redis for this
No need here, you already have a database you can put data in.
I would like to know if there are other ways to do this
There's an alternative, where you cryptographically sign your URL. Basically, you would store the key and its expiration data in the URL itself, and include some calculated proof that you (the person with the private key) created this URL. When your system receives this URL, it can verify the URL was signed correctly without even having to consult a database. This method can be complicated and probably isn't useful in your case. I'm just mentioning it here as an alternative. Check out JWT for one possible implementation: https://jwt.io/
Recently I was needed to implement this kind of implementation in my web application. So I just followed the below points to achieve it.
1- Create the URL (web link) and append the current date and time along with an encrypted key which you would store in the database as mentioned below.
2- Create a column in the database table (the table where you store any user specific details) to store a randomly generated key which you have encrypted and appended in the URL.
3- When you would receive this URL on server you would check the encrypted date and time in the URL and would decide whether it is still valid depends on your criteria of retaining a link (e.g. 24 hours)
4- Next you would decrypt that key in the URL that you have appended in it at the time of creating it and would match it with what you have stored in the table.
So by implementing above logic you could achieve the desired functionality.
Hope its useful for any one who wants similar type of implementation
I understood that you already found a solution, by storing two fields in the database: one for the key and another one for he expiration timestamp. Everything depends on the use cases and it is definately one way to do it. However I will explain Redis and JWT as a solution in comparison to yours.
Redis is an in-memory datastore (that also allows persistence to disk) as you pointed out and I think the reason why people suggested it is, that you can define an expiration time for a record. Redis will remove that record automatically for you then. Reference: https://redis.io/commands/expire
Redis would take the work off of your shoulders to check if the 24hrs already passed. If you can’t fetch the key anymore, the key probably expired. Another benefit of Redis is, that is super quick compared to your normal database query. But if you only need it for the activation link, which is a one-time-action for the user, the time benefit is negligible. Also you would introduce a new technology just for that use case.
#Brad already suggested using JWT (Json Web Token) instead of implementing your own token solution and I would also suggest that for the following reasons.
A JWT is a self-contained token consisting of three parts: Header, Payload, Signature. The header contains the name of the algorithm that was used to create the token. The paylod contains some standardized fields (e.g. creation date, expiration date, subject the token was issued for like username) and you can also add custom fields. The third part is a signature that ensures that no one changed the payload after it was issued by your token service.
Self-contained means that the token contains everything to validate it, including the expiration timestamp. In your case the expiration time is not part of your token but stored in the database. If you create another microservice that needs to verify your token, that service needs to contact your main service which contains the logic to check the expiration database field.
With JWT the Microservice would only need to know the secret key that was used to sign the token and then you can just import some standard JWT library to verify the token. These libraries validate the signature as well as the expiration timestamp which is an optional field in the payload of the token.
By the way, the payload can be read without knowing the secret key from the signature. So it is even possible to read the payload for example on client side to check the expiration time.
With your solution you have additional database calls, which are potentially slow. For an activation link that is acceptable, but for tokens with recurring use within a short timespan (i.e. API requests that require authentication) additional database calls should be avoided. Also you need to implement token generation and verification yourself, whereas JWT provides standard libraries. This is a benefit when you want to have another Microservice in Java instead of NestJS for example. You can quickly knit them together by using standard libs instead of porting your implemtation or being forced to decide for a centralized token verification service.
One limitation of JWT you have to workaround yourself, is the use case where you want to have a „one time token“. You can only define an expiration date but you can not say that a token can only be used x times. Here you need a centralized service again, which keeps track of how often a token was used (by making use of some datastore) and all other services around need to contact that service.
A good starting point for JWT with NestJS is the official NestJS documentation.

What's the correct/standard practice to authenticate a user after registration?

I'm trying to authenticate a user after registration. What's the correct or standard way to go about it?
Using this method as the way to implement it, in step 3, how can I generate the random hash to send to the users email? I see two different options:
crypto
JWT token
I'm currently using JWT for login, so would it make sense to use the same token for user verification? Why or why not, and if not, what's the correct way?
The answer to your question of whether you should use a crypto hash or a token is neither.
The hash you are generating to use as a verification method does not need to be cryptographically secure, it only needs to be a unique verification hash that is not easy to guess.
In the past I have used a v4 UUID with the UUID lib and it works just fine. You could also base64 some known piece of information about the user, like their id or email concatenated with something random, like the time in mircoseconds and a random hex string with substantial length, but honestly the time it takes to build out something like that is wasted when UUID v4 works just fine.
Your hash also doesn't need to be unique (different for each user, yes, but avoid all potential collisions? No) - hitting an endpoint with only the hash is not a great idea. The endpoint should also take an identifier for your user combined with the verification hash. This way, you don't need to worry about the hash being unique in your datastore. Find user by ID, check that verification hashes match, verify. I would only suggest that you obfuscate the user's know information in a way that you can decode on your end (ex: base64 encode their user ID + email + some const string you use).
[EDIT]
Verifying or validating a user is really just asking them to prove that the email address (or phone number) they entered does in fact exist and that it belongs to the user. This is an attempt to make sure the user didn't enter the information incorrectly or that the registration is spam. For this we don't need cryptographic authentication, a simple shared secret is more than enough.
When you store your user's registration data, you generate the shared secret you will use to verify the account. This can be anything that is (relatively) unique and contains enough length and entropy that it is not easy to be guessed. We aren't encoding or encrypting information that will be unpacked later, we are doing a literal string comparison to make sure the secret we provided to the user was echoed back to us intact. This is why a simple one-way hash is OK to use. I suggested a UUID v4 because the components of this hash are generated from random information (other versions of UUID make use of the machine's MAC or the time or other known pieces of information). You can use any method you like as long as it can't be easily decoded or guessed.
After you generate the verification hash you send it to the user (in a nicely formatted URL that they only need to click) in order for them to complete their account registration. URL guidelines are totally up to you, but here are some suggestions:
BAD
/verify/<verification hash>
or
/verify?hash=<verification hash>
With only the verification hash in the URL, you are relying on this value to be globally unique in your datastore. If you can reliably generate unique values that never contain collisions, then it would be OK, but why would you want to worry about that? Don't rely on the verification hash by itself.
GOOD
/users/<id>/verify/<verification hash>
or
/users/<id>?action=verify&hash=<verification hash>
Out of these two examples you can see that the point is to provide two pieces of data, 1. is a way to identify the user, and 2. the verification hash you are checking.
In this process you start by finding the user in your datastore by ID, and then literally compare the secret you generated and stored against the value given in the URL. If the user is found and the verification hashes match, set their account to Active and you're good to go. If the user is found but the hashes don't match... either you provided a malformed URL or someone is trying to brute force your verification. What you do here is up to you, but to be safe you might regenerate the hash and send out a new email and try the process again. This leads very quickly into a black hole about how to prevent spam and misuse of your system, which is a different conversation.
The above URL schemas really only work if your user IDs are safe for public display. As a general rule you should never use your datastore IDs in a URL, especially if they are sequential INTs. There are many options for IDs that you would use in a URL like UUID v1 or HashIDs or any implementation of a short ID.
ALSO
A good way to see how this is done in the wild is to look at the emails you have received from other systems asking you to verify your own email address. Many may use the format:
/account/verify/<very long hash>
In this instance, the "very long hash" is usually generated by a library that either creates a datastore table just for the purpose of account verification (and the hash is stored in that table) or is decoded to reveal a user identifier as well as some sort of verification hash. This string is encoded in a way that is not easily reversible so it can not be guessed or brute forced. This is typically done by encoding the components with some sort of unique salt value for each string.
NOTE - while this method may be the most "secure", I only mention this because it is based on the typical methods used by third-party libs which do not make assumptions about your user data model. You can implement this style if you want, but it would be more work. My answer is focused your intent to do basic verification based on data in your user model.
BONUS
Many verification systems are also time constricted so that the verification URL expires after some period of time. This is easily able to be set up by also storing a future timestamp with your user data that is checked when the verification endpoint is hit and the user is found. What to do when an expired link is clicked is up to you, but the main benefit is to help you more easily clean up dead registrations that you know cannot be verified.

How to remember users with cookies in a secure way?

So lets say i have a member base website and when the user signs in i put put a cookie (or a session) with a key value pair remembering who the user is. But its just come to my attention which information i should use to remember the user so that its secure. I cant use username=username or user_id = user_id (because my user_id will be 1), because people then can just simply guess what the cookie values are and logged in as that user. So what key/value pair should i use to be able to identify users and still connect their information to the database securely? Thanks.
Ben, there are a few different types of attacks you need to be concerned with. For example simply encrypting the identifier with a private key doesn't prevent someone who can intercept the encrypted value from simply replaying it to your server (and appear to be the user). Some common security risks are detailed here (and in associated links at bottom of this page):
https://www.owasp.org/index.php/Session_hijacking_attack
Session management can be quite complex and depending on the level of security you require, it is not something you want to tackle yourself, because likely your development environment / framework already has a solution that has been vetted moreso than a homebrew solution. Here is a link detailing some things to consider, unfortunately this topic has more to it than a simple Stack Overflow post:
https://www.owasp.org/index.php/Session_Management
If you dont prefer encryption for whatever reason, then a simpler solution could be to use a GUID to identify the user. This way, a hacker would have to launch a denial of service kind-of attack on your application to be able to run through even a very small fraction of the GUIDs.
If you want to do this properly, then you should have a look at http://jaspan.com/improved_persistent_login_cookie_best_practice also.
I'm definitely not an expert in security, but I have recently implemented user management tool and I have done the following.
Don't use encryption, its slow and most of the time for simple implementation its just a waste of time.
Here is what you do need to store on the server - in order to authenticate each request.
UserId (obvious)
CookieHash (made out of userId, some secret private key and crypto randomly generated number)
LastLogin
SessionRenewed (useful for when to cancel someone's session eg. renew cookieHash every 10 min, otherwise log out user)
LastIP
What I store in cookie is following
UserId
CookieHash
How to use this basic security
Simply when user logs in you check username/password etc. (just the usual) If everything is fine then log in user and generate new cookiehash and fill those values given above.
Every request check UserId against its hash. If someone gave UserId = 4 but hash didnt match then automatically drop a session and forward user to login screen. Possible log is good to see how often people try to play around with your hard work.
I hope this helps.
You can just encrypt the user id with a private encryption key that you keep on the server. There are a few things to watch out for with this approach:
Every call to the server will require you to decrypt the cookie to get the id of the user. This will add overhead to each request.
If the key is ever compromised, you will be forced to abandon the current name for the cookie you use and use another encryption key when assigning to the new cookie name; this will cause the user to have to re-login, of course.
While I don't think that these are major hurdles, they might be to you, and you would have to evaluate the impact on your site for yourself.

Resources