A simple JWT implementation in Crystal.
Add this to your application's shard.yml
:
dependencies:
jwt:
github: rogeriozambon/jwt.cr
adapter = JWT::Adapters::HS256.new("secret")
payload = {"foo" => "bar"}
JWT.encode(payload, adapter)
#=> eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.dtxWM6MIcgoeMgH87tGvsNDY6cHWL6MGW4LeYvnm1JA
adapter = JWT::Adapters::HS256.new("secret")
token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.dtxWM6MIcgoeMgH87tGvsNDY6cHWL6MGW4LeYvnm1JA"
JWT.decode(token, adapter)
#=> {{"alg" => "HS256", "typ" => "JWT"}, {"foo" => "bar"}}
require "jwt"
require "http/server"
adapter = JWT::Adapters::HS256.new("secret")
server = HTTP::Server.new("0.0.0.0", 8080, [
JWT::Middleware.new(adapter)
])
puts "Listening on 8080 with JWT middleware.."
server.listen
- None
- HMAC (HS256, HS384, HS512)
- RSA
- ECDSA
require "jwt"
adapter = JWT::Adapters::HS256.new("secret")
payload = {"foo" => "bar", "aud" => ["guest", "visitor"]}
JWT.encode(payload, adapter)
#=> eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJhdWQiOlsiZ3Vlc3QiLCJ2aXNpdG9yIl19.S5BkkjkhjagYET12wz4W2cF8ATF1iVfpJ9OvbjMdCRU
JWT.decode(token, adapter)
#=> {{"alg" => "HS256", "typ" => "JWT"}, {"foo" => "bar", "aud" => ["guest", "visitor"]}}
When an audience doesn't match with that was passed to payload, an exception (JWT::Errors::Audience
) will be thrown.
JWT.decode(token, adapter, {"aud" => "admin"})
#=> Invalid audience. (JWT::Errors::Audience)
require "jwt"
adapter = JWT::Adapters::HS256.new("secret")
payload = {"foo" => "bar", "exp" => Time.now.epoch + 60}
JWT.encode(payload, adapter)
#=> eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJleHAiOjE0MDcxNTMyMzF9.ClzhXRBkpaJZw1QscBT_CD8yA1IQmWOTaokQn4fIGFc
JWT.decode(token, adapter)
#=> {{"alg" => "HS256", "typ" => "JWT"}, {"foo" => "bar", "exp" => 1407153257}}
After 60 seconds, the token expires and an exception (JWT::Errors::Expired
) will be thrown:
JWT.decode(token, adapter)
#=> Token has expired. (JWT::Errors::Expired)
require "jwt"
adapter = JWT::Adapters::HS256.new("secret")
payload = {"foo" => "bar", "iss" => "jwt.cr"}
JWT.encode(payload, adapter)
#=> eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpc3MiOiJqd3QuY3IifQ.4lj020gUcpRg0r_4n8L7ZSn28BcSWGBeF3I7BaBSDDY
JWT.decode(token, adapter)
#=> {{"alg" => "HS256", "typ" => "JWT"}, {"foo" => "bar", "iss" => "jwt.cr"}}
When an issuer doesn't match with that was passed to payload, an exception (JWT::Errors::Issuer
) will be thrown.
JWT.decode(token, adapter, {"iss" => "jwt"})
#=> Invalid issuer. (JWT::Errors::Issuer)
require "jwt"
adapter = JWT::Adapters::HS256.new("secret")
payload = {"foo" => "bar", "nbf" => Time.now.epoch + 60}
JWT.encode(payload, adapter)
#= > eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJuYmYiOjE0MDcxNTMyMDV9.s6PJj6dBxZPWpdzYjxw9h846x6IU05nRB41y43JNVMs
Before the 60 seconds ends, the token will not ready to use and an exception (JWT::Errors::NotReady
) will be thrown:
JWT.decode(token, adapter)
#=> Token not ready. (JWT::Errors::NotReady)
require "jwt"
adapter = JWT::Adapters::HS256.new("secret")
payload = {"foo" => "bar", "sub" => "github"}
JWT.encode(payload, adapter)
#=> eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJzdWIiOiJnaXRodWIifQ.7r7_7tV4xzrQN1atQ7OP7O7eRVtb1l37dIYBjv5yJvg
JWT.decode(token, adapter)
#=> {{"alg" => "HS256", "typ" => "JWT"}, {"foo" => "bar", "sub" => "github"}}
When a subject doesn't match with that was passed to payload, an exception (JWT::Errors::Subject
) will be thrown.
JWT.decode(token, adapter, {"sub" => "gitlab"})
#=> Invalid subject. (JWT::Errors::Subject)
It's possible to add a custom logger to replace the default behavior. You can add a logger that outputs to a file, for example. (Thanks @hugoabonizio)
log_file = File.open("./jwt.log", "w")
JWT::Logger.instance = Logger.new(log_file)
$ crystal spec
- rogeriozambon Rogério Zambon - creator, maintainer