Crystal is still a young language, there aren’t a lot of libraries available yet. For some this could be offputting, but for others this is a chance to learn about a language and provide useful tools for those also starting to use it.
I’ve given a number of talks over time, some of which were about two factor authentication. A while back I took the opportunity to improve my knowledge and implement the one time password algorithm in Crystal.
This lead me to build the CrOTP library for one time passwords in Crystal.
If you want to check out the code, most of the important implementation can be found in the CrOTP::OTP
module. The CrOTP::HOTP
and CrOTP::TOTP
classes take care of the different ways the counter is treated.
Using CrOTP
To use the CrOTP library in your own Crystal project you need to add it to the dependencies in your shard.yml
file.
dependencies:
crotp:
github: philnash/crotp
Then install the dependencies with shards install
.
Now you can require "crotp"
and use it in your application. Most uses on the web of one time passwords are time based tokens for two factor authentication. Here’s how to generate and verify time based OTPs.
Getting started
First require the library and generate a random string to use as the secret.
require "crotp"
secret = Random.new.hex(16)
Then create an instance of the CrOTP::TOTP
class with that secret.
totp = CrOTP::TOTP.new(secret)
Sharing secrets
You can generate a URI that can be shared with authenticator apps like Authy or Google Authenticator. To do so call authenticator_uri
with the name of your app as the issuer and the user account’s username as the user.
totp.authenticator_uri(issuer: "Test app", user: "philnash@example.com")
Turn the resulting URI into a QR code and users can scan it to add the account to their app.
Generating and verifying
Now you can use the totp
object to generate a new one time password that will be valid within the current 30 second period of time.
password = totp.generate
To verify a code, you can use the same object, calling verify
instead.
totp.verify(password)
You can also allow a drift, to give users more time to enter their code. The drift is the number of periods (periods are 30 seconds long by default) that can pass after the token was generated.
totp.verify(password, at: Time.now, allowed_drift: 2)
There are more examples in the project repo.
There’s more work to be done
CrOTP does the basic work for generating and verifying one time passwords. Of course, there’s more to do on the project when I find the time.
I’d love to hear if this project is useful to you or if you’re interested in helping implement the missing features. Let me know on Twitter at @philnash.