This depends strongly on your application. The web approach maybe more comfortable for the user but will put you and your application in certain responsibilities and risks. From my understanding you have at least this three possibilities.
If you are running a pure Web-Portal like an Exchange this maybe the way to go. You would kind of provide the wallet for the user. But this has some security issues. If your db gets hacked or your system somehow infiltrated (steal, delete) your user can loose everything related to that keys and this would be your fault. As a crypto user I would personally have some concerns using a new plattform and expose or generate keys there when desktop tools are available.
Workaround: Proxy Service
Like written in the comments and other answers it maybe a bad practice to store personal stuff like the private keys of your users. However another thing you could implement would be a proxy solution which will be your application. Your app would then function as a kind of middleware and only use the key of your applications account on the EOS mainnet. You store the emails and passwords like a normal WebApp and would do the authorization by your backend and send transactions only from your account. This would minimize the number of accounts needed and maybe save some RAM. Your smart contract would then need some kind of identification mechanism for the users actions e.g. a field in a multi_index table for some kind of nickname from your WebApp.
Another use case could be an application that stores the information locally (like the greymass vote tool) or some docker bundled application that includes the eosio CLI tools. Advantage of this solution is, that the user is responsible for his actions and you do not have the store all user keys centralized.
Third option is to use the EOS Permission system. Users could add a special permission for your smart contracts. So they can sign transactions with their regular keys and would accept actions that are signed by your contracts account. Also users can remove permissions anytime they want and are not stuck with potential accounts that use their token for RAM.
Personally I would go with the third option because this is the cleanest version and has major advantages. But like I mentioned before it depends on your use case.
- Maybe the eosjs library is interesting for your use case.
- Also the RPC documentaion could be worth a look.
- There are also a few Tools that give the user build in
functionalities like Scatter.