Comments (30)
I've consolidates some of the pieces of advice in the various comments into the following step by step guide with a sample project:
https://www.jamesridgway.co.uk/implementing-a-two-step-otp-u2f-login-workflow-with-rails-and-devise/
from devise-two-factor.
I think this requires at least small section in README. Since every service implements 2FA in 2 steps - its really unclear how to implement it with current library.
from devise-two-factor.
GitLab uses devise-two-factor with a two-step login. I believe the login is in their SessionsController and this concern. I believe they store the user ID in the signed/encrypted Rails session.
I'd definitely welcome any clarifications to the README about this from someone who has implemented it. I believe GitLab's method addresses @borski's security concerns above.
from devise-two-factor.
@rikkipitt Sorry for the late reply. Unfortunately, I can't post my implementation to you because it's my company code that is not open source. However, I referred to the Gitlab open source code: Gitlab The two main files that I looked at were app/controllers/sessions_controller.rb
and app/controllers/concerns/authenticates_with_two_factor.rb
from devise-two-factor.
Updating my comment. I've ending up using https://github.com/wmlele/devise-otp and have spent a good amount of time activley contributing to it.
The gem resolved this flow and has a lot of flexibility. Again just an FYI for those looking for this feature inparticular.
from devise-two-factor.
We run a bug bounty program so our implementation should be pretty secure :)
from devise-two-factor.
@jeffreyyong Would you be kind enough to demonstrate your implementation please? When you get the time of course!
from devise-two-factor.
@connorshea Yes! Open source rocks! :)
from devise-two-factor.
Got mine working too with help of GitLab's implementation.
from devise-two-factor.
Sorry about the late reply!
I don't have any code examples of this, but I know of at least two websites who've managed to turn the flow from this gem into a two-step process.
They used the session to keep track of what stage the user is at in the login, and used a before_filter to redirect to the correct login page based on the stage. Unfortunately I'm a bit busy dealing with POODLE right now, so I can't put together an example, but if you have any questions I'd be happy to see what I can do to help.
from devise-two-factor.
I found a way to do this but am not 100% sure about it as it requires some form of user identification is passed to the second step form.
- User logs in
- A before filter fires on Session create
- Finds the user based on e-mail provided
- Validates the email and password
- Redirects to Two-Factor step two if two factor enabled
- Two-Factor step two is a second form requesting
ota_attempt
- A UID is stored in a hidden field on the form
- User submits
ota_attempt
and we validate to stored user
Maybe this is acceptable, We confirm user pass in step one and confirm the ota_attemp
to the stored user in step two. It just feels clunky. I feel like I should be able to make this work without having to pass user data in forms or session variables. Any advice?
from devise-two-factor.
@jonathansimmons Thanks for opening this issue - I'm attempting to do this as well. Would also love an example.
from devise-two-factor.
👍 any ideas?
from devise-two-factor.
@jonathansimmons Sorry about the late reply!
I think your approach is more or less correct. I agree that passing user data to the form is a little clunky, so I think sessions are the way to go. Given that Rails handles session signing for you, you won't have to worry about session tampering at this stage.
I think as long as you make sure the incomplete session expires within a reasonable amount of time, you should be good as far as security is concerned.
Unfortunately I can't see a nicer way of handling it than what you have already.
from devise-two-factor.
I'm closing this as I don't feel it's really @ShaneWilton's problem.
I went ahead and forked this repo, created an examples dir, moved the existing demo into /one-step and put my current (kinda ghetto) two-step example in /two-step
@ShaneWilton any input /improvements are welcomed...
https://github.com/jonathansimmons/devise-two-factor
from devise-two-factor.
Howdy folks, late to the discussion, but wondering if this might work: (modified from @jonathansimmons)
- User logs in
- A before filter fires on Session create
- Finds the user based on e-mail provided
- Validates the email and password
- renders the Two-Factor step two if two factor enabled
- Two-Factor step two is a second form requesting ota_attempt
- hidden fields store the plaintext email/password/remember_me
- User submits ota_attempt and we validate to stored user
Initially this seemed insecure, but how insecure is it really? The only vulnerability I can think of is if the user walks away from the computer while their browser is on the two-factor page, and a potential attacker comes and inspects the DOM for their plaintext password.... and this isn't really a vulnerability, right?
What am I missing? ;)
from devise-two-factor.
Although it seems innocent, putting a plaintext password on a page is a really really bad idea. There are a ton of reasons:
- Someone walking away from their computer is absolutely a vulnerability. It's the same reason it's important to disable autocomplete for password fields - people at an internet cafe or library will be vulnerable to this.
- Pages get cached. A page that gets cached in a browser on a public computer that includes your plaintext password is bad news.
- Pages get saved by accident. All it takes is one accidental hit of Ctrl-S and there's a page on the desktop for anyone to open up later and get the plaintext password.
- ...
You kind of get where this is going - including the plaintext password on a page is a really bad idea due to the myriad number of ways in which an attacker can later access it; even if they don't have local access to the computer, but they have a RAT set up, they can later look at the cache and pull down their password.
The best way to do this, I think, is to pass some sort of secure signature along that you can verify, server-side, as having been a user authenticated via password. That way you don't have to pass along a uid or anything like that either. Ideally, you'd want to time-bound and expire the signature somehow as well.
Additionally, rails sessions are conveniently signed already, so you could simply add a parameter to the session as well, but you may need to clean it up after they're logged in or if they choose not to complete the login process.
Does that make sense?
from devise-two-factor.
Yup, thanks for the sanity check. Pushing a fix to our integration right now.
from devise-two-factor.
Hey @jonathansimmons, I just stumbled upon this thread as I'm looking at creating a two-stage login using this gem and your fork is dead. Did you manage to get this working nicely with tinfoil/devise-two-factor before I jump in and try it myself? It would be good to hear what troubles you encountered before I tackle it myself if you've got a moment to comment.
Cheers,
Rikki
from devise-two-factor.
@rikkipitt The last time I used my fork it was working fine. Devise has been updated a few times since then.
I did make some significant customizations in my exact implementation. I needed it for a project a littler over a year ago. I tried to make some progress with the maintainer but he stopped communicating.
That being said it's lightweight and should work. Email me if you have questions and I'll see if I can help.
from devise-two-factor.
Cheers @jonathansimmons. What I've got so far is a normal login with a before_filter
to check if the user has otp_required_for_login
set to true - redirects to a verification form view. I'm storing the email/password in session (encrypted).
The problem I'm now having is merging the email/password params and the verification code before triggering the default sessions_controller create method. How did you do the auth step in the end?
Cheers
from devise-two-factor.
@rikkipitt email me at jon{at}jsdev.co so we can move it off this thread as it's not relevant to this repo.
from devise-two-factor.
Hi @jonathansimmons, I am wondering if the gem that you suggested: https://github.com/wmlele/devise-otp is building on top of tinfoil/devise-two-factor, or is it a solo gem by itself?
I have implemented all the backup codes and the verification code with tinfoil/devise-two-factor and I am wondering if I can combine the implementation with the gem that you suggested. I am currently stuck at the two-step log-in feature.
from devise-two-factor.
Hey, I am wondering if anyone managed to implement the second login step using tinfoil/devise-two-factor gem? I have implemented a bulk of the features using this gem, and the only thing that is lacking is the second login step.
from devise-two-factor.
@bsedat Thanks for letting me know that big project like Gitlab is using tinfoil/devise-two-factor as well. I will try to tinker with it and see if it works for my project.
from devise-two-factor.
@jeffreyyong no it does not build upon this gem. I believe it was based off a google authenticator gem.
It's been a while since I've used either of these gems personally but I know each of them can do what you're looking for with a little elbow grease.
from devise-two-factor.
@connorshea I managed to implement the two-step login in my system after looking at the GitLab example! Thank you very much.
from devise-two-factor.
@jeffreyyong that's really great to hear! Open source at work <3
from devise-two-factor.
A few of you have suggested using the session cookiestore to store the state of the user
I would recommend storing it in a custom cookie (specifically an encrypted one) so that you can set a separate expiry time (5 minutes) instead of using a rails session cookie (where setting a short expiry can be very inconvenient)
from devise-two-factor.
A security improvement idea. In addition to storing user's id in the session (to load the user on 2FA screen)
session[:otp_user_id] = user.id
I suggest storing a random token
session[:otp_user_token] = SecureRandom.alphanumeric(64)
Next, compute a bcrypt hash and its expiration time and store these in the users table. On 2FA screen, load the user by its :otp_user_id
, check if the :otp_user_token
token corresponds to the hash and check the token's expiration date. This gives additional security if you store your session in an encrypted cookie. It remains secure even if cookie encryption key leaks out. It also allows to invalidate the token on the server side.
from devise-two-factor.
Related Issues (20)
- Require email verification when enabling Authenticator App type
- Generator throws exception HOT 1
- `Devise.add_module(:two_factor_authenticatable)` should be inserted on top
- calling super in the two factor strategy is problematic...
- Remove the old `otp_secret_encryption_key` in the UPGRADING.md guide
- Git tag for v4.1.0 appears to be missing HOT 2
- No changelog for 4.1.0 HOT 1
- `user.current_otp` code sent to user are always invalid HOT 1
- [question] mass users update?
- Not required on every login HOT 1
- Rails 7.1 on 4.x HOT 1
- Support for Rails 7.1 HOT 3
- ActiveRecord::Encryption::Errors::Decryption error on login after upgrade to Rails 7.1 defaults HOT 3
- Backup codes aren't written to the database so can't be used HOT 1
- Clarification on GHSA-chcr-x7hc-8fp8 HOT 8
- Lockbox instead of ActiveRecord encrypted attributes HOT 1
- Remove faker or make it gem dependency? HOT 4
- Possible to set logo?
- Upgrading. Use this as a starting point for your task to migrate your user's OTP secrets - why have you not provided concise code
- Update rails version of demo
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from devise-two-factor.