diff --git a/shard.lock b/shard.lock index 182e0db6a9..5f5a121e91 100644 --- a/shard.lock +++ b/shard.lock @@ -12,10 +12,22 @@ shards: git: https://github.com/cloudamqp/amqp-client.cr.git version: 1.2.8 + bindata: + git: https://github.com/spider-gazelle/bindata.git + version: 2.1.0 + + jwt: + git: https://github.com/crystal-community/jwt.git + version: 1.6.1 + lz4: git: https://github.com/84codes/lz4.cr.git version: 1.0.0+git.commit.96d714f7593c66ca7425872fd26c7b1286806d3d + openssl_ext: + git: https://github.com/spider-gazelle/openssl_ext.git + version: 2.4.4 + systemd: git: https://github.com/84codes/systemd.cr.git version: 2.0.0 diff --git a/shard.yml b/shard.yml index 6e2eb48079..a0c00f52f7 100644 --- a/shard.yml +++ b/shard.yml @@ -32,6 +32,8 @@ dependencies: github: 84codes/systemd.cr lz4: github: 84codes/lz4.cr + jwt: + github: crystal-community/jwt development_dependencies: ameba: diff --git a/src/lavinmq/auth/auth_handler.cr b/src/lavinmq/auth/auth_handler.cr index df7adc9fcc..66a2d590d6 100644 --- a/src/lavinmq/auth/auth_handler.cr +++ b/src/lavinmq/auth/auth_handler.cr @@ -1,6 +1,8 @@ module LavinMQ abstract class AuthHandler + Log = LavinMQ::Log.for "auth.handler" property successor : AuthHandler? + @log = Logger.new(Log) abstract def authenticate(username : String, password : String) diff --git a/src/lavinmq/auth/handlers/basic_auth.cr b/src/lavinmq/auth/handlers/basic_auth.cr index aec63dcb4c..cd4cdecc6b 100644 --- a/src/lavinmq/auth/handlers/basic_auth.cr +++ b/src/lavinmq/auth/handlers/basic_auth.cr @@ -8,9 +8,8 @@ module LavinMQ def authenticate(username : String, password : String) user = @users[username] - # TODO: do not do authentication check if the user is not in the userstore, instead pass directly to the next handler return user if user && user.password && user.password.not_nil!.verify(password) - puts "Basic authentication failed" + @log.warn { "Basic authentication failed" } try_next(username, password) end end diff --git a/src/lavinmq/auth/handlers/http.cr b/src/lavinmq/auth/handlers/http.cr index fcafa03ad7..50ae2490c9 100644 --- a/src/lavinmq/auth/handlers/http.cr +++ b/src/lavinmq/auth/handlers/http.cr @@ -10,10 +10,10 @@ module LavinMQ def authenticate(username : String, password : String) # TODO: implement the HTTP authentication logic and permissions parser here if password.starts_with?("http") - puts "HTTP authentication successful" + @log.warn { "HTTP authentication successful" } return @users[username] else - puts "HTTP authentication failed" + @log.warn { "HTTP authentication failed" } return try_next(username, password) end end diff --git a/src/lavinmq/auth/handlers/oauth2.cr b/src/lavinmq/auth/handlers/oauth2.cr index 737f717c26..ce28fd9869 100644 --- a/src/lavinmq/auth/handlers/oauth2.cr +++ b/src/lavinmq/auth/handlers/oauth2.cr @@ -1,19 +1,36 @@ require "../auth_handler" +require "jwt" +require "../../config" module LavinMQ class OAuth2Handler < LavinMQ::AuthHandler def initialize(@users : UserStore) end + # Temporary for tests + @token : String = LavinMQ::Config.instance.token + @public_key : String = LavinMQ::Config.instance.public_key + def authenticate(username : String, password : String) - # TODO: implement the OAuth2 authentication logic and permissions parser here - if password.starts_with?("oauth") - puts "OAuth2 authentication successful" - @users[username] - else - puts "OAuth2 authentication failed" + begin + payload, header = JWT.decode(@token, key: @public_key, algorithm: JWT::Algorithm::RS256, verify: true, validate: true) + oauth_user + rescue ex : JWT::DecodeError + @log.warn { "OAuth2 authentication failed, could not decode token: #{ex}" } + try_next(username, password) + rescue ex : JWT::UnsupportedAlgorithmError + @log.warn { "OAuth2 authentication failed, unsupported algortihm: #{ex}" } + try_next(username, password) + rescue ex + @log.warn { "OAuth2 authentication failed: #{ex}" } try_next(username, password) end end + + def oauth_user + # TODO: Create a uset that will be deleted when it disconnects, but also cannot be authorised with basic auth. + # introduce the needed configs for validation, and parse the payload to get the user details + user = @users.create("oauth_user", "password") + end end end diff --git a/src/lavinmq/config.cr b/src/lavinmq/config.cr index 7623bad6a0..b69ddd26fd 100644 --- a/src/lavinmq/config.cr +++ b/src/lavinmq/config.cr @@ -65,6 +65,11 @@ module LavinMQ property http_auth_url : String? = "" property oauth_url : String? = "" property auth_backends = ["basic", "oauth", "http"] + #this will be fetched from an jwks endpoint + property public_key = "" + # this will come from the connect packet + property token = "" + @@instance : Config = self.new def self.instance : LavinMQ::Config