stas / otp-jwt Goto Github PK
View Code? Open in Web Editor NEWOne time password (email, SMS) authentication support for HTTP APIs.
Home Page: https://rubygems.org/gems/otp-jwt
License: MIT License
One time password (email, SMS) authentication support for HTTP APIs.
Home Page: https://rubygems.org/gems/otp-jwt
License: MIT License
I have a User table that has preexisting data, and I start using otp-jwt recently.
It is not working at the beginning because otp_secret
column is always empty for preexisting User record
The active record does give the User record a default otp_secret
otp-jwt/lib/otp/active_record.rb
Lines 79 to 81 in 37ca55c
My workaround is to go through each user record and save the default otp_secret
User.all.each do |u|
u.save()
end
Not sure whether we can make it better by updating the documentation, or always save the default secret if there are otp_secret
column is empty
First of all, awesome gem! :-)
In the docs is stated that verify_otp will return true on success and false on failure. In my case if it's true it returns the otp_counter and if its false nil. I made a local patch that return !otp_status.nil?
instead of otp_status
.
Is retuning the counter or nil intended?
Just a placeholder issue where I can upload logos lol
I think include OTP::ActiveRecord
should be this include OTP::JWT::ActiveRecord
.
Hi ๐
First of all, amazing work in this library! Simple & straight to the point.
I was wondering why is this the default behavior for the verify_otp
method:
otp-jwt/lib/otp/active_record.rb
Line 42 in 1a76c1d
Testing locally, if I send an incorrect OTP, the previous one gets invalidated and the users has to generate another one.
Is this the behavior or am I missing some configuration?
Hey @stas
First off thanks so much for your work on this project. I want to use it on a small project that I am working on and just wanted to get your thoughts on if you think its worthwhile to encrypt the the OTP secret/counter in the database.
I know most of the time that sensitive information such as one time use tokens or passwords are stored in the database it is usually hashed (I think devise does this with its reset password links), but that wont be possible with this project as we would need to get the data back, so was thinking encryption was another option.
Was just thinking that if anyone was to get read access to the database (for example if a database backup was leaked) they would be able to get the secrets/counters for all the users and possibly log in as anyone.
Was thinking i could use a project like lockbox to handle the encryption for me which should work seamlessly with this project. Do you think this would be worthwhile or unnecessary and wont really improve anything.
I set the jwt_algorithm
to RS256
and provided a valid RSA private key to it, but for some reason, the from_jwt
started to return a nil value. I managed to track down a possible issue. The jwt_algorithm
isn't being considered in the current implementation of the OTP::JWT::Token.verify
method and the from_jwt
doesn't allow us to pass otps
to the verify method.
The block bellow should solve the issue.
def self.verify(token, opts = nil)
verify = self.jwt_algorithm != 'none'
default_opts = verify ? { algorithm: self.jwt_algorithm } : {}
::JWT.decode(token.to_s, self.jwt_signature_key, verify, opts || default_opts)
end
I don't see anything about this gem supporting two user models, however, the documentation does seem to indicate the use on multiple active record models. So this could be a bug if used on multiple models.
I have a User and Admin model
I generate a JWT for each instance and then can find that instance using from_jwt
as expected.
However, what I didn't expect was to be able to use the same JWT for an Admin instance and find a User instance. Either the JWT is not unique, or the from_jwt
method has a bug.
Here is a JWT for a particular User instance eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOjEsImV4cCI6MTU2MjcxNDcxNX0.gZvAxDmz4eBQQLmc_-Zxj0bWUkvaWnWKzKOLHQw-uRM
I can find an Admin using that same JWT as shown below
[47] pry(#<Api::V1::Admins::UsersController>)> Admin.from_jwt("eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOjEsImV4cCI6MTU2MjcxNDcxNX0.gZvAxDmz4eBQQLmc_-Zxj0bWUkvaWnWKzKOLHQw-uRM")
CACHE Admin Load (0.1ms) SELECT "admins".* FROM "admins" WHERE "admins"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
=> #<Admin id: 1, email: "[email protected]", created_at: "2019-07-04 22:27:33", updated_at: "2019-07-05 21:06:25", account_id: 1, role: "super", first_name: "Super", last_name: "Test", title: "yes", authentication_token: "zavi5k8_5HLNxqLSYN2N", otp_secret: "4bpzyy7ugnsb5x3v3u2d634cpbsnztjd", otp_counter: 0>
[48] pry(#<Api::V1::Admins::UsersController>)> User.from_jwt("eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOjEsImV4cCI6MTU2MjcxNDcxNX0.gZvAxDmz4eBQQLmc_-Zxj0bWUkvaWnWKzKOLHQw-uRM")
CACHE User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
=> #<User id: 1, email: "[email protected]", created_at: "2019-07-04 22:27:35", updated_at: "2019-07-05 21:06:34", account_id: 1, first_name: "Sherill", last_name: "test", employee_number: nil, tenure: nil, title: "GM", department: nil, gender: nil, region: nil, location: nil, brand: nil, ancestry: "2", manager_id: nil, hire_date: nil, relocate: nil, short_term_plan: nil, long_term_plan: nil, last_email_sent: nil, authentication_token: "WpYq7HypQzytMp1GxkiE", otp_secret: "7r6ooydbzlsodbxluyhswcuxpubfybfj", otp_counter: 0>
[49] pry(#<Api::V1::Admins::UsersController>)>
Here is a complete terminal output to highlight each instances and their respective JWT's.
[39] pry(#<Api::V1::Admins::UsersController>)> @user
=> #<User id: 1, email: "[email protected]", created_at: "2019-07-04 22:27:35", updated_at: "2019-07-05 21:06:34", account_id: 1, first_name: "Sherill", last_name: "test", employee_number: nil, tenure: nil, title: "GM", department: nil, gender: nil, region: nil, location: nil, brand: nil, ancestry: "2", manager_id: nil, hire_date: nil, relocate: nil, short_term_plan: nil, long_term_plan: nil, last_email_sent: nil, authentication_token: "WpYq7HypQzytMp1GxkiE", otp_secret: "7r6ooydbzlsodbxluyhswcuxpubfybfj", otp_counter: 0>
[40] pry(#<Api::V1::Admins::UsersController>)> @admin
=> #<Admin id: 1, email: "[email protected]", created_at: "2019-07-04 22:27:33", updated_at: "2019-07-05 21:06:25", account_id: 1, role: "super", first_name: "Super", last_name: "Test", title: "yes", authentication_token: "zavi5k8_5HLNxqLSYN2N", otp_secret: "4bpzyy7ugnsb5x3v3u2d634cpbsnztjd", otp_counter: 0>
[41] pry(#<Api::V1::Admins::UsersController>)> @admin.to_jwt
=> "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOjEsImV4cCI6MTU2MjcxNDcxNX0.gZvAxDmz4eBQQLmc_-Zxj0bWUkvaWnWKzKOLHQw-uRM"
[42] pry(#<Api::V1::Admins::UsersController>)> @user.to_jwt
=> "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOjEsImV4cCI6MTU2MjcxNDcxOH0._Uqisk_hpYYdIgS4MNfl8mwW-_lfCprzhOXkJ7OI8FM"
[43] pry(#<Api::V1::Admins::UsersController>)> Admin.from_jwt("eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOjEsImV4cCI6MTU2MjcxNDcxNX0.gZvAxDmz4eBQQLmc_-Zxj0bWUkvaWnWKzKOLHQw-uRM")
CACHE Admin Load (0.1ms) SELECT "admins".* FROM "admins" WHERE "admins"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
=> #<Admin id: 1, email: "[email protected]", created_at: "2019-07-04 22:27:33", updated_at: "2019-07-05 21:06:25", account_id: 1, role: "super", first_name: "Super", last_name: "Test", title: "yes", authentication_token: "zavi5k8_5HLNxqLSYN2N", otp_secret: "4bpzyy7ugnsb5x3v3u2d634cpbsnztjd", otp_counter: 0>
[44] pry(#<Api::V1::Admins::UsersController>)> User.from_jwt("eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOjEsImV4cCI6MTU2MjcxNDcxOH0._Uqisk_hpYYdIgS4MNfl8mwW-_lfCprzhOXkJ7OI8FM")
CACHE User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
=> #<User id: 1, email: "[email protected]", created_at: "2019-07-04 22:27:35", updated_at: "2019-07-05 21:06:34", account_id: 1, first_name: "Sherill", last_name: "test", employee_number: nil, tenure: nil, title: "GM", department: nil, gender: nil, region: nil, location: nil, brand: nil, ancestry: "2", manager_id: nil, hire_date: nil, relocate: nil, short_term_plan: nil, long_term_plan: nil, last_email_sent: nil, authentication_token: "WpYq7HypQzytMp1GxkiE", otp_secret: "7r6ooydbzlsodbxluyhswcuxpubfybfj", otp_counter: 0>
[45] pry(#<Api::V1::Admins::UsersController>)> Admin.from_jwt("eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOjEsImV4cCI6MTU2MjcxNDcxOH0._Uqisk_hpYYdIgS4MNfl8mwW-_lfCprzhOXkJ7OI8FM")
CACHE Admin Load (0.1ms) SELECT "admins".* FROM "admins" WHERE "admins"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
=> #<Admin id: 1, email: "[email protected]", created_at: "2019-07-04 22:27:33", updated_at: "2019-07-05 21:06:25", account_id: 1, role: "super", first_name: "Super", last_name: "Test", title: "yes", authentication_token: "zavi5k8_5HLNxqLSYN2N", otp_secret: "4bpzyy7ugnsb5x3v3u2d634cpbsnztjd", otp_counter: 0>
[46] pry(#<Api::V1::Admins::UsersController>)> User.from_jwt("eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOjEsImV4cCI6MTU2MjcxNDcxNX0.gZvAxDmz4eBQQLmc_-Zxj0bWUkvaWnWKzKOLHQw-uRM")
CACHE User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
=> #<User id: 1, email: "[email protected]", created_at: "2019-07-04 22:27:35", updated_at: "2019-07-05 21:06:34", account_id: 1, first_name: "Sherill", last_name: "test", employee_number: nil, tenure: nil, title: "GM", department: nil, gender: nil, region: nil, location: nil, brand: nil, ancestry: "2", manager_id: nil, hire_date: nil, relocate: nil, short_term_plan: nil, long_term_plan: nil, last_email_sent: nil, authentication_token: "WpYq7HypQzytMp1GxkiE", otp_secret: "7r6ooydbzlsodbxluyhswcuxpubfybfj", otp_counter: 0>
[47] pry(#<Api::V1::Admins::UsersController>)>
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.