JWT::Authorizer
makes authorization with JWT tokens simple. It allows creating and verifying JWT tokens according to claims set on specific JWT::Token
class.
Add this line to your application's Gemfile:
gem 'jwt-authorizer'
And then execute:
$ bundle
Or install it yourself as:
$ gem install jwt-authorizer
You can configure your JWT::Token
classes with .configuration
and .configure
options:
JWT::Token.configuration
JWT::Token.configure do |config|
config.expiry = 12 * 60 * 60
config.algorithm = "RS256"
config.rsa.authorized_keys = [OpenSSL::PKey::RSA.new(ENV["SECRET_KEY"])]
end
JWT::Token
have following options available:
algorithm
- determines algorithm used on signing and verifying JWT tokens. Defaults to"HS256"
.hmac
-HMAC
configuration:hmac.key
- symmetric key used by HMAC algorithm
rsa
|ecdsa
-RSA
andECDSA
configuration:rsa.authorized_keys
|ecdsa.authorized_keys
-Array
ofOpenSSL::PKey::PKey
objects with allowed public keysrsa.authorized_keys_file
|ecdsa.authorized_keys_file
- path to file containing authorized public keys in PEM format
expiry
- sets default expiry for generated tokens. Defaults to 1 hour. It can be set tonil
in order to not includeexp
claim in the tokenissuer
- setsiss
claim in the token. Defaults tonil
.allowed_issuers
- array of issuers that will be allowed on token verification. Defaults to empty array, tokens with any value iniss
claim (and without this claim) will be valid. If array contains any elements, only listed issuers will be valid.
Default claims can be overriden during instantiation of JWT::Token
classes:
JWT::Token.configuration.expiry #=> 3600 (offset)
JWT::Token.new(expiry: Time.utc(2018, 3, 1)).expiry #=> 1519862400 (timestamp)
To generate JWT token, create instance of JWT::Token
. It accepts hash of additional claims you want in your token.
class MyToken < JWT::Token
claim :level, required: true
end
token = MyToken.new(level: :admin)
token.to_s # or token.to_jwt
#=> "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MjA0MTI2MTcsImxldmVsIjoiYWRtaW4ifQ.Ak8qDlxSG9IcPVHYnelQHPK5U6Rj5hBYQ5mmoznuYso"
To verify token, use JWT::Token.verify
method.
token = "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MjA0MTI2Nzd9.EgiqWfDjXzlJHTwaFn26X3iOl2gBkQv3fADtMsFIQDY"
JWT::Token.verify(token)
#=> #<JWT::Token @claims={"exp"=>1520412677, "iss"=>nil}>
JWT::Token.verify(nil)
# JWT::DecodeError: Nil JSON web token
JWT::Token.verify("eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjB9.nooope")
# JWT::VerificationError: Signature verification raised
You can use claims to define and verify non-standard claims.
class AdminToken < JWT::Token
claim :level, key: "lvl", required: true do |value|
raise JWT::DecodeError, "Level must be admin" unless value == "admin"
end
end
valid_token = "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MjA0MTI3ODksImxldmVsIjoiYWRtaW4ifQ.GGD0dXWg7v8BiEg8fsjmdCXQBryAHRpx_8AihyNVmgs"
AdminToken.verify(valid_token)
#=> #<AdminToken @claims={"exp"=>1520412789, "iss"=>nil, "lvl"=>"admin"}>
missing_claim = "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MjA0MTI3ODl9.efq_LuSpfp5VRwFl3rIf0FC_b2CCrpEC_oeDssvLDy4"
AdminToken.verify(missing_claim)
# JWT::Token::MissingClaim: Token is missing required claim: lvl
invalid_value = "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MjA0NTI3ODksImx2bCI6InJlZ3VsYXIifQ.EjXX9zhE4SpzFSlGIPD5l0xKtMKgWSbWa5smw3OvBEo"
AdminToken.verify(invalid_value)
# JWT::DecodeError: Level must be admin
required
option is by default set to false
. If set to true
, given claim must be present in verified token.
key
options is by default the same as claim name. It corresponds to JSON inside JWT.
You can pass additional context to claims:
class AdminToken < JWT::Token
claim :path do |value, rack_request|
raise JWT::DecodeError, "invalid path" unless value == rack_request.path
end
end
AdminToken.verify(token, rack_request)
See JWT::EndpointToken
and it's spec for examples.
Gem currently supports two of the standard claims: exp
and iss
.
You can set expiry
option on configuration to a preferred offset for generated tokens:
class LongLivedToken < JWT::Token
configuration.expiry = 2 * 365 * 24 * 60 * 60
end
token = LongLivedToken.new
Time.at token.expiry
#=> 2020-03-06 08:59:52 +0100
Note that expiry
option in configuration is an offset, while on token instance it's a timestamp.
On instance you can either assign timestamp, or a Time
instance.
token = JWT::Token.new
token.expiry = Time.utc(2021, 1, 1)
token.expiry
#=> 1609459200
JWT::Token.new(expiry: Time.utc(2021, 1, 1)).expiry
#=> 1609459200
exp
claim will be validated if present.
In order to validate issuer
claim, set allowed_issuers
on token class:
class MicroserviceToken < JWT::Token
configuration.allowed_issuers = ["apiservice", "cronservice"]
end
MicroserviceToken.verify(MicroserviceToken.new(issuer: "apiservice").to_jwt)
#=> #<MicroserviceToken @claims={"exp"=>1520413510, "iss"=>"apiservice"}>
MicroserviceToken.verify(MicroserviceToken.new(issuer: "otherservice").to_jwt)
# JWT::InvalidIssuerError: Invalid issuer. Expected ["apiservice", "cronservice"], received otherservice
MicroserviceToken.verify(MicroserviceToken.new(issuer: nil).to_jwt)
# JWT::InvalidIssuerError: Invalid issuer. Expected ["apiservice", "cronservice"], received <none>
After checking out the repo, run bin/setup
to install dependencies. Then, run rake spec
to run the tests. You can also run bin/console
for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install
. To release a new version, update the version number in version.rb
, and then run bundle exec rake release
, which will create a git tag for the version, push git commits and tags, and push the .gem
file to rubygems.org.
Bug reports and pull requests are welcome on GitHub at https://github.com/codesthq/jwt-authorizer. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.
The gem is available as open source under the terms of the MIT License.
Everyone interacting in the JWT::Token project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.