Initial implementation, plus test programs.
This commit is contained in:
parent
e559e1dbdb
commit
f09ff2c9f7
|
|
@ -1,4 +1,4 @@
|
||||||
cmake_minimum_required(VERSION 3.5)
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
# https://github.com/Sarcasm/cmake-superbuild/
|
# https://github.com/Sarcasm/cmake-superbuild/
|
||||||
option (USE_SUPERBUILD "Whether or not a superbuild should be invoked" ON)
|
option (USE_SUPERBUILD "Whether or not a superbuild should be invoked" ON)
|
||||||
|
|
@ -14,7 +14,15 @@ endif()
|
||||||
find_package(jwt-cpp)
|
find_package(jwt-cpp)
|
||||||
find_package(OpenSSL)
|
find_package(OpenSSL)
|
||||||
|
|
||||||
add_library(jwp-plugin SHARED src/jwp-plugin.cpp)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
|
||||||
|
add_library(jwp-plugin SHARED src/jwp-plugin.cpp src/AuthList.cpp src/Authorizer.cpp)
|
||||||
|
target_include_directories(jwp-plugin PUBLIC
|
||||||
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||||
|
$<INSTALL_INTERFACE:include>
|
||||||
|
${jwt-cpp_INCLUDE_DIR}
|
||||||
|
)
|
||||||
|
target_link_libraries(jwp-plugin OpenSSL::Crypto)
|
||||||
|
|
||||||
add_executable(jwt-example src/jwt-example.cpp)
|
add_executable(jwt-example src/jwt-example.cpp)
|
||||||
target_include_directories(jwt-example PRIVATE ${jwt-cpp_INCLUDE_DIR})
|
target_include_directories(jwt-example PRIVATE ${jwt-cpp_INCLUDE_DIR})
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
#pragma once
|
||||||
|
#include <forward_list>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class AuthList
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AuthList();
|
||||||
|
|
||||||
|
void add(const std::string& username);
|
||||||
|
void remove(const std::string& username);
|
||||||
|
bool confirm(const std::string& username);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::forward_list<std::string> _allowedUsernames;
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <jwt-cpp/jwt.h>
|
||||||
|
#include <jwp-plugin/AuthList.hpp>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
class Authorizer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Authorizer(const std::string& pub_key, const std::string& issuer);
|
||||||
|
static std::optional<std::string> read_key(const std::string& key_file);
|
||||||
|
void add_unknown(const std::string& username);
|
||||||
|
bool is_unknown(const std::string& username);
|
||||||
|
bool add(const std::string& token, const std::string& username);
|
||||||
|
bool can_read(const std::string& username);
|
||||||
|
bool can_write(const std::string& username);
|
||||||
|
void logout(const std::string& username);
|
||||||
|
private:
|
||||||
|
AuthList _writeList;
|
||||||
|
AuthList _readList;
|
||||||
|
AuthList _unknownList;
|
||||||
|
|
||||||
|
std::string _pub_key;
|
||||||
|
std::string _issuer;
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MC4CAQAwBQYDK2VwBCIEID6d/A9UnVV5xXf9RAvXSNTk/a1QNUrzfvawzEAWDh3e
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
-----BEGIN PUBLIC KEY-----
|
||||||
|
MCowBQYDK2VwAyEA+IYMWskcPLcC8IsUy6xsj3whqlzYwFWuAmVR7ue/LLw=
|
||||||
|
-----END PUBLIC KEY-----
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
#include <algorithm>
|
||||||
|
#include <jwp-plugin/AuthList.hpp>
|
||||||
|
|
||||||
|
AuthList::AuthList():
|
||||||
|
_allowedUsernames{}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void AuthList::add(const std::string& username)
|
||||||
|
{
|
||||||
|
// Is the username already in the list?
|
||||||
|
// If not add it.
|
||||||
|
const auto found = std::find(std::begin(_allowedUsernames), std::end(_allowedUsernames), username);
|
||||||
|
if(found == std::end(_allowedUsernames))
|
||||||
|
{
|
||||||
|
_allowedUsernames.emplace_front(username);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AuthList::remove(const std::string& username)
|
||||||
|
{
|
||||||
|
const auto found = std::find(std::begin(_allowedUsernames), std::end(_allowedUsernames), username);
|
||||||
|
if(found != std::end(_allowedUsernames))
|
||||||
|
{
|
||||||
|
_allowedUsernames.remove(username);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AuthList::confirm(const std::string& username)
|
||||||
|
{
|
||||||
|
const auto found = std::find(std::begin(_allowedUsernames), std::end(_allowedUsernames), username);
|
||||||
|
if(found != std::end(_allowedUsernames))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,114 @@
|
||||||
|
#include <jwp-plugin/Authorizer.hpp>
|
||||||
|
#include <string>
|
||||||
|
#include <jwt-cpp/jwt.h>
|
||||||
|
#include <jwp-plugin/AuthList.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
|
||||||
|
Authorizer::Authorizer(const std::string& pub_key, const std::string& issuer):
|
||||||
|
_pub_key{pub_key},
|
||||||
|
_issuer{issuer}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> Authorizer::read_key(const std::string& key_file)
|
||||||
|
{
|
||||||
|
// Read key from file.
|
||||||
|
std::ifstream key_stream(key_file, std::ios::binary);
|
||||||
|
if(not key_stream)
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << key_stream.rdbuf();
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Authorizer::add_unknown(const std::string& username)
|
||||||
|
{
|
||||||
|
_unknownList.add(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Authorizer::is_unknown(const std::string& username)
|
||||||
|
{
|
||||||
|
return _unknownList.confirm(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Authorizer::add(const std::string& token, const std::string& username)
|
||||||
|
{
|
||||||
|
const auto decoded_token = jwt::decode(token);
|
||||||
|
|
||||||
|
// Is the token valid?
|
||||||
|
const auto verifier = jwt::verify()
|
||||||
|
.with_issuer(_issuer)
|
||||||
|
.allow_algorithm(jwt::algorithm::rs256(_pub_key));
|
||||||
|
try
|
||||||
|
{
|
||||||
|
verifier.verify(decoded_token);
|
||||||
|
}
|
||||||
|
catch(jwt::error::token_verification_exception& exception)
|
||||||
|
{
|
||||||
|
std::cout << exception.what() << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto claims = decoded_token.get_payload_claims();
|
||||||
|
|
||||||
|
// Check username matches.
|
||||||
|
if(not claims.contains("upn"))
|
||||||
|
{
|
||||||
|
std::cout << "Missing upn." << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(claims["upn"].as_string() != username)
|
||||||
|
{
|
||||||
|
std::cout << "Wrong username." << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for mqtt-write claim value.
|
||||||
|
if(not (claims.contains("mqtt-write") and claims.contains("mqtt-read")))
|
||||||
|
{
|
||||||
|
std::cout << "Missing mqtt-write or mqtt-read." << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool can_write = claims["mqtt-write"].as_bool();
|
||||||
|
bool can_read = claims["mqtt-read"].as_bool();
|
||||||
|
if(not (can_write or can_read))
|
||||||
|
{
|
||||||
|
std::cout << "Can't write or can't read." << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(can_write)
|
||||||
|
{
|
||||||
|
_writeList.add(username);
|
||||||
|
}
|
||||||
|
if(can_read)
|
||||||
|
{
|
||||||
|
_readList.add(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Authorizer::can_read(const std::string& username)
|
||||||
|
{
|
||||||
|
return _readList.confirm(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Authorizer::can_write(const std::string& username)
|
||||||
|
{
|
||||||
|
return _writeList.confirm(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Authorizer::logout(const std::string& username)
|
||||||
|
{
|
||||||
|
_writeList.remove(username);
|
||||||
|
_readList.remove(username);
|
||||||
|
_unknownList.remove(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -4,6 +4,8 @@ extern "C" {
|
||||||
#include "mosquitto_plugin.h"
|
#include "mosquitto_plugin.h"
|
||||||
}
|
}
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
#include <jwp-plugin/Authorizer.hpp>
|
||||||
|
|
||||||
// Stuff we're "exporting" for the dynamic loading.
|
// Stuff we're "exporting" for the dynamic loading.
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
@ -14,10 +16,12 @@ extern "C" {
|
||||||
// My functions
|
// My functions
|
||||||
int jwp_auth_basic_auth_callback(int event, void *event_data, void *userdata);
|
int jwp_auth_basic_auth_callback(int event, void *event_data, void *userdata);
|
||||||
int jwp_acl_check_callback(int event, void *event_data, void *userdata);
|
int jwp_acl_check_callback(int event, void *event_data, void *userdata);
|
||||||
|
int jwp_disconnect_callback(int event, void *event_data, void *userdata);
|
||||||
|
|
||||||
|
|
||||||
// Mosquitto Globals
|
// Mosquitto Globals
|
||||||
static mosquitto_plugin_id_t *plugin_id = nullptr;
|
static mosquitto_plugin_id_t *plugin_id = nullptr;
|
||||||
|
static std::unique_ptr<Authorizer> authorizer = nullptr;
|
||||||
|
|
||||||
|
|
||||||
int mosquitto_plugin_version(int supported_version_count, const int *supported_versions)
|
int mosquitto_plugin_version(int supported_version_count, const int *supported_versions)
|
||||||
|
|
@ -36,8 +40,47 @@ int mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **userdata, st
|
||||||
{
|
{
|
||||||
plugin_id = identifier;
|
plugin_id = identifier;
|
||||||
|
|
||||||
|
|
||||||
|
if(option_count != 2)
|
||||||
|
{
|
||||||
|
return MOSQ_ERR_INVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string public_key;
|
||||||
|
std::string issuer;
|
||||||
|
for(int index = 0; index < option_count; index++)
|
||||||
|
{
|
||||||
|
const auto key = std::string(options[index].key);
|
||||||
|
if(key == "public_key")
|
||||||
|
{
|
||||||
|
const auto key = Authorizer::read_key(std::string(options[index].value));
|
||||||
|
if(key)
|
||||||
|
{
|
||||||
|
public_key = *key;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return MOSQ_ERR_INVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(key == "issuer")
|
||||||
|
{
|
||||||
|
issuer = std::string(options[index].value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(public_key.empty() or issuer.empty())
|
||||||
|
{
|
||||||
|
mosquitto_log_printf(MOSQ_LOG_ERR, "public_key or issue not set.");
|
||||||
|
return MOSQ_ERR_INVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
authorizer = std::make_unique<Authorizer>(public_key, issuer);
|
||||||
|
|
||||||
|
// Register the callbacks.
|
||||||
mosquitto_callback_register(plugin_id, MOSQ_EVT_BASIC_AUTH, jwp_auth_basic_auth_callback, NULL, NULL);
|
mosquitto_callback_register(plugin_id, MOSQ_EVT_BASIC_AUTH, jwp_auth_basic_auth_callback, NULL, NULL);
|
||||||
mosquitto_callback_register(plugin_id, MOSQ_EVT_ACL_CHECK, jwp_acl_check_callback, NULL, NULL);
|
mosquitto_callback_register(plugin_id, MOSQ_EVT_ACL_CHECK, jwp_acl_check_callback, NULL, NULL);
|
||||||
|
mosquitto_callback_register(plugin_id, MOSQ_EVT_DISCONNECT, jwp_disconnect_callback, NULL, NULL);
|
||||||
// May want MOSQ_EVT_RELOAD as well.
|
// May want MOSQ_EVT_RELOAD as well.
|
||||||
|
|
||||||
return MOSQ_ERR_SUCCESS;
|
return MOSQ_ERR_SUCCESS;
|
||||||
|
|
@ -49,6 +92,7 @@ int mosquitto_plugin_cleanup(void *userdata, struct mosquitto_opt *options, int
|
||||||
{
|
{
|
||||||
mosquitto_callback_unregister(plugin_id, MOSQ_EVT_BASIC_AUTH, jwp_auth_basic_auth_callback, NULL);
|
mosquitto_callback_unregister(plugin_id, MOSQ_EVT_BASIC_AUTH, jwp_auth_basic_auth_callback, NULL);
|
||||||
mosquitto_callback_unregister(plugin_id, MOSQ_EVT_ACL_CHECK, jwp_acl_check_callback, NULL);
|
mosquitto_callback_unregister(plugin_id, MOSQ_EVT_ACL_CHECK, jwp_acl_check_callback, NULL);
|
||||||
|
mosquitto_callback_unregister(plugin_id, MOSQ_EVT_DISCONNECT, jwp_disconnect_callback, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
return MOSQ_ERR_SUCCESS;
|
return MOSQ_ERR_SUCCESS;
|
||||||
|
|
@ -56,45 +100,69 @@ int mosquitto_plugin_cleanup(void *userdata, struct mosquitto_opt *options, int
|
||||||
|
|
||||||
int jwp_auth_basic_auth_callback(int event, void *event_data, void *userdata)
|
int jwp_auth_basic_auth_callback(int event, void *event_data, void *userdata)
|
||||||
{
|
{
|
||||||
|
if(not authorizer)
|
||||||
|
{
|
||||||
|
// Not sure this is possible.
|
||||||
|
return MOSQ_ERR_AUTH;
|
||||||
|
}
|
||||||
|
|
||||||
struct mosquitto_evt_basic_auth *auth_data = static_cast<struct mosquitto_evt_basic_auth*>(event_data);
|
struct mosquitto_evt_basic_auth *auth_data = static_cast<struct mosquitto_evt_basic_auth*>(event_data);
|
||||||
|
|
||||||
if(!auth_data->username or !auth_data->password)
|
if(!auth_data->username or !auth_data->password)
|
||||||
{
|
{
|
||||||
mosquitto_log_printf(MOSQ_LOG_ERR, "No username or password.");
|
authorizer->add_unknown(std::string(auth_data->username));
|
||||||
return MOSQ_ERR_PLUGIN_DEFER;
|
return MOSQ_ERR_PLUGIN_DEFER;
|
||||||
}
|
}
|
||||||
mosquitto_log_printf(MOSQ_LOG_ERR, "Username: %s Password: %s",
|
bool is_authed = authorizer->add(std::string(auth_data->password), std::string(auth_data->username));
|
||||||
auth_data->username, auth_data->password);
|
|
||||||
|
|
||||||
return MOSQ_ERR_SUCCESS; // MOSQ_ERR_AUTH;
|
if(is_authed)
|
||||||
|
{
|
||||||
|
return MOSQ_ERR_SUCCESS;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return MOSQ_ERR_AUTH;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int jwp_acl_check_callback(int event, void *event_data, void *userdata)
|
int jwp_acl_check_callback(int event, void *event_data, void *userdata)
|
||||||
{
|
{
|
||||||
|
if(not authorizer)
|
||||||
|
{
|
||||||
|
return MOSQ_ERR_ACL_DENIED;
|
||||||
|
}
|
||||||
|
|
||||||
struct mosquitto_evt_acl_check *acl_data = static_cast<struct mosquitto_evt_acl_check *>(event_data);
|
struct mosquitto_evt_acl_check *acl_data = static_cast<struct mosquitto_evt_acl_check *>(event_data);
|
||||||
|
|
||||||
std::string event_name = "none";
|
const std::string username = std::string(mosquitto_client_username(acl_data->client));
|
||||||
|
|
||||||
|
if(authorizer->is_unknown(username))
|
||||||
|
{
|
||||||
|
return MOSQ_ERR_PLUGIN_DEFER;
|
||||||
|
}
|
||||||
|
|
||||||
switch(acl_data->access)
|
switch(acl_data->access)
|
||||||
{
|
{
|
||||||
case MOSQ_ACL_SUBSCRIBE:
|
case MOSQ_ACL_SUBSCRIBE:
|
||||||
event_name = "subscribe";
|
return MOSQ_ERR_PLUGIN_DEFER;
|
||||||
break;
|
|
||||||
case MOSQ_ACL_UNSUBSCRIBE:
|
case MOSQ_ACL_UNSUBSCRIBE:
|
||||||
event_name = "unsubscribe";
|
return MOSQ_ERR_PLUGIN_DEFER;
|
||||||
break;
|
|
||||||
case MOSQ_ACL_WRITE:
|
case MOSQ_ACL_WRITE:
|
||||||
event_name = "write";
|
return (authorizer->can_write(username) ? MOSQ_ERR_SUCCESS : MOSQ_ERR_ACL_DENIED);
|
||||||
break;
|
|
||||||
case MOSQ_ACL_READ:
|
case MOSQ_ACL_READ:
|
||||||
event_name = "read";
|
return (authorizer->can_read(username) ? MOSQ_ERR_SUCCESS : MOSQ_ERR_ACL_DENIED);
|
||||||
break;
|
default:
|
||||||
|
return MOSQ_ERR_ACL_DENIED;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mosquitto_log_printf(MOSQ_LOG_ERR, "Topic: %s Event: %s",
|
int jwp_disconnect_callback(int event, void *event_data, void *userdata)
|
||||||
acl_data->topic, event_name.c_str());
|
{
|
||||||
|
struct mosquitto_evt_disconnect *disconnect_data = static_cast<struct mosquitto_evt_disconnect*>(event_data);
|
||||||
return MOSQ_ERR_SUCCESS; // MOSQ_ERR_ACL_DENIED;
|
const std::string username = std::string(mosquitto_client_username(disconnect_data->client));
|
||||||
|
|
||||||
|
authorizer->logout(username);
|
||||||
|
return MOSQ_ERR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
-----BEGIN PUBLIC KEY-----
|
||||||
|
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlIKdtC04YbRMO0L4ID4YOWLr2AxYpQZYZ3g9BNpVm+IjDdn4H5HaYwYvOcbdjKyRdmwm+rsrIbWxCGYQCD5TtaCnq1IGwOueoprgCTDNSpTxsKQ+JuEUIhKc4rygVhX7JKIvVikfWimKVuNJBVhut/O+/N0AarasszAyinc3gjwtu2SyLBdZtIe3Krs1MIvYb786J2RhK3GfLzrXVzmKjA2/ThB9D6sS7dtZCe//37kYZzGUv5+xFkjkKwZr2aULMlmpUosFd/S2w3zsZkGRELLTvdRf5PVKeGpk40EneETJAHwiMjX6+jO/vlFQIj/Ye66ypVhCCI+NizE/hWbdawIDAQAB
|
||||||
|
-----END PUBLIC KEY-----
|
||||||
|
|
@ -9,6 +9,8 @@ log_type all
|
||||||
allow_anonymous true
|
allow_anonymous true
|
||||||
|
|
||||||
auth_plugin /home/jimmy/Develop/mosquitto-plugin/build/libjwp-plugin.so
|
auth_plugin /home/jimmy/Develop/mosquitto-plugin/build/libjwp-plugin.so
|
||||||
|
auth_opt_issuer https://auth.jpace121.net/realms/jpace121-main
|
||||||
|
auth_opt_public_key /home/jimmy/Develop/mosquitto-plugin/test/key.pem
|
||||||
|
|
||||||
# -----------------------------------------------------------------
|
# -----------------------------------------------------------------
|
||||||
# External authentication and topic access plugin options
|
# External authentication and topic access plugin options
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
import requests
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
import paho.mqtt.client
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
# Get urls.
|
||||||
|
well_known = requests.get("https://auth.jpace121.net/realms/jpace121-main/.well-known/openid-configuration").json()
|
||||||
|
auth_url = well_known['device_authorization_endpoint']
|
||||||
|
token_url = well_known['token_endpoint']
|
||||||
|
|
||||||
|
header = {"Content-Type":"application/x-www-form-urlencoded"}
|
||||||
|
data = {"client_id" : "jpace-mqtt"}
|
||||||
|
|
||||||
|
# Request login.
|
||||||
|
r = requests.post(auth_url, headers=header, data=data)
|
||||||
|
r_json = r.json()
|
||||||
|
print("Go to: {} to login.".format(r_json['verification_uri_complete']))
|
||||||
|
|
||||||
|
# Wait for token.
|
||||||
|
data['grant_type'] = "urn:ietf:params:oauth:grant-type:device_code"
|
||||||
|
data['device_code'] = r_json['device_code']
|
||||||
|
for index in range(1, 20):
|
||||||
|
r = requests.post(token_url, headers=header, data=data)
|
||||||
|
if 'access_token' in r.json():
|
||||||
|
break
|
||||||
|
time.sleep(r_json['interval'])
|
||||||
|
|
||||||
|
token = r.json()['access_token']
|
||||||
|
print(token)
|
||||||
|
|
||||||
|
client = paho.mqtt.client.Client(protocol=paho.mqtt.client.MQTTv5,
|
||||||
|
transport="tcp")
|
||||||
|
client.username_pw_set("jimmy", password=token)
|
||||||
|
client.connect("localhost", port=8081)
|
||||||
|
|
||||||
|
print("Waiting on connection.")
|
||||||
|
time.sleep(20)
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
import requests
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
import jwt
|
||||||
|
|
||||||
|
# Get urls.
|
||||||
|
well_known = requests.get("https://auth.jpace121.net/realms/jpace121-main/.well-known/openid-configuration")
|
||||||
|
well_known = well_known.json()
|
||||||
|
auth_url = well_known['device_authorization_endpoint']
|
||||||
|
token_url = well_known['token_endpoint']
|
||||||
|
|
||||||
|
header = {"Content-Type":"application/x-www-form-urlencoded"}
|
||||||
|
data = {"client_id" : "jpace-mqtt"}
|
||||||
|
|
||||||
|
r = requests.post(auth_url, headers=header, data=data)
|
||||||
|
r_json = r.json()
|
||||||
|
|
||||||
|
print("Go to: {} to login.".format(r_json['verification_uri_complete']))
|
||||||
|
|
||||||
|
data['grant_type'] = "urn:ietf:params:oauth:grant-type:device_code"
|
||||||
|
data['device_code'] = r_json['device_code']
|
||||||
|
|
||||||
|
for index in range(1, 20):
|
||||||
|
r = requests.post(token_url, headers=header, data=data)
|
||||||
|
if 'access_token' in r.json():
|
||||||
|
break
|
||||||
|
time.sleep(r_json['interval'])
|
||||||
|
|
||||||
|
public_key = b"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlIKdtC04YbRMO0L4ID4YOWLr2AxYpQZYZ3g9BNpVm+IjDdn4H5HaYwYvOcbdjKyRdmwm+rsrIbWxCGYQCD5TtaCnq1IGwOueoprgCTDNSpTxsKQ+JuEUIhKc4rygVhX7JKIvVikfWimKVuNJBVhut/O+/N0AarasszAyinc3gjwtu2SyLBdZtIe3Krs1MIvYb786J2RhK3GfLzrXVzmKjA2/ThB9D6sS7dtZCe//37kYZzGUv5+xFkjkKwZr2aULMlmpUosFd/S2w3zsZkGRELLTvdRf5PVKeGpk40EneETJAHwiMjX6+jO/vlFQIj/Ye66ypVhCCI+NizE/hWbdawIDAQAB\n-----END PUBLIC KEY-----"
|
||||||
|
|
||||||
|
#decoded = jwt.decode(r.json()['access_token'], audience=None, options={"verify_signature": False, 'verify_aud': False})
|
||||||
|
decoded = jwt.decode(r.json()['access_token'], public_key, algorithms=['RS256'])
|
||||||
|
print('Decoded: {}'.format(decoded))
|
||||||
Loading…
Reference in New Issue