diff --git a/README.md b/README.md index f897a7d..4735dd2 100644 --- a/README.md +++ b/README.md @@ -185,6 +185,11 @@ Skip to [restart Postfix](#restart-postfix) below. #### Client Credentials +There's both the internally implemented token management, as well as the +possibility to use tokens managed externally (i. e. when already having +the proper OAuth2 tokens managed by a different application work-flow). + +##### Internal management of client credentials Follow [Microsoft's instructions to register an application](https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app#register-an-application). Use any name you like (it doesn't have to be "sasl-xoauth2"). Under "Platform @@ -205,7 +210,32 @@ points to Gmail by default): } ``` -We'll also need these credentials in the next step. +We'll also need these credentials in the next step ("Initial Access Token"). + +#### External token management + +When running multiple applications using the same OAuth2 access (i. e. fetchmail and Postfix), +you may already have the according token management processes in place (and providing an always +current token in a user-specific file). + +Under such circumstances, using another token manager may be problematic, leading to possible +token contention and other difficulties. + +Assuming that the token management is already in place and provides a token file per user (and of course +maintaining that file to always contain the then valid token), you can configure sasl-xoauth2 to +simply read that user token file, without further interaction with upstream token issuers. + +When doing so, you provide the token file name as the SASL password and need to set "external_token_manager" to "yes" +in sasl-xoauth's configuration file: + +```json +{ + "external_token_manager": "yes" +} +``` + +This setting will apply to all authentications handled by this plug-in. Please also make sure that the user +under which this plug-in is run, has access to the token file's content. #### A Note on Token Endpoints diff --git a/src/client.cc b/src/client.cc index 42a2614..3a5d66a 100644 --- a/src/client.cc +++ b/src/client.cc @@ -18,7 +18,9 @@ #include #include +#include +#include "config.h" #include "log.h" #include "token_store.h" @@ -207,8 +209,13 @@ int Client::InitialStep(sasl_client_params_t *params, if (err != SASL_OK) return err; user_ = auth_name; - token_ = TokenStore::Create(log_.get(), password); - if (!token_) return SASL_FAIL; + + if (Config::Get()->external_token_manager() == false) { + token_ = TokenStore::Create(log_.get(), password); + if (!token_) return SASL_FAIL; + } else { + tokenfile_ = password; + } err = SendToken(to_server, to_server_len); if (err != SASL_OK) return err; @@ -244,8 +251,10 @@ int Client::TokenSentStep(sasl_client_params_t *params, } if (status == "400" || status == "401") { - int err = token_->Refresh(); - if (err != SASL_OK) return err; + if (Config::Get()->external_token_manager() == false) { + int err = token_->Refresh(); + if (err != SASL_OK) return err; + } return SASL_TRYAGAIN; } @@ -260,8 +269,20 @@ int Client::TokenSentStep(sasl_client_params_t *params, int Client::SendToken(const char **to_server, unsigned int *to_server_len) { std::string token; - int err = token_->GetAccessToken(&token); - if (err != SASL_OK) return err; + + if (Config::Get()->external_token_manager() == false) { + int err = token_->GetAccessToken(&token); + if (err != SASL_OK) return err; + } else { + std::ifstream file(tokenfile_); + if (!file.good()) { + log_->Write("Client::SendToken: failed to open file %s: %s", tokenfile_.c_str(), + strerror(errno)); + return SASL_FAIL; + } else { + file >> token; + } + } response_ = "user=" + user_ + "\1auth=Bearer " + token + "\1\1"; log_->Write("Client::SendToken: response: %s", response_.c_str()); diff --git a/src/client.h b/src/client.h index 619f4a9..2231d0e 100644 --- a/src/client.h +++ b/src/client.h @@ -57,6 +57,7 @@ class Client { State state_ = State::kInitial; std::string user_; + std::string tokenfile_; std::string response_; // Order of destruction matters -- token_ holds a pointer to log_. diff --git a/src/config.cc b/src/config.cc index c4149ed..537ebe8 100644 --- a/src/config.cc +++ b/src/config.cc @@ -134,10 +134,7 @@ int Config::Init(const Json::Value &root) { try { int err; - err = Fetch(root, "client_id", false, &client_id_); - if (err != SASL_OK) return err; - - err = Fetch(root, "client_secret", false, &client_secret_); + err = Fetch(root, "external_token_manager", false, &external_token_manager_); if (err != SASL_OK) return err; err = Fetch(root, "log_to_syslog_on_failure", true, @@ -148,8 +145,18 @@ int Config::Init(const Json::Value &root) { &log_full_trace_on_failure_); if (err != SASL_OK) return err; - err = Fetch(root, "token_endpoint", true, &token_endpoint_); - if (err != SASL_OK) return err; + // the internal token manager needs a number of configuration parameters + if (!external_token_manager_) { + + err = Fetch(root, "client_id", false, &client_id_); + if (err != SASL_OK) return err; + + err = Fetch(root, "client_secret", false, &client_secret_); + if (err != SASL_OK) return err; + + err = Fetch(root, "token_endpoint", true, &token_endpoint_); + if (err != SASL_OK) return err; + } return 0; diff --git a/src/config.h b/src/config.h index 05dbcd9..b653d2e 100644 --- a/src/config.h +++ b/src/config.h @@ -29,6 +29,7 @@ class Config { static Config *Get(); + bool external_token_manager() const { return external_token_manager_; } std::string client_id() const { return client_id_; } std::string client_secret() const { return client_secret_; } bool log_to_syslog_on_failure() const { return log_to_syslog_on_failure_; } @@ -42,6 +43,7 @@ class Config { std::string client_id_; std::string client_secret_; + bool external_token_manager_ = false; bool log_to_syslog_on_failure_ = true; bool log_full_trace_on_failure_ = false; std::string token_endpoint_ = "https://accounts.google.com/o/oauth2/token"; diff --git a/src/sasl-xoauth2.conf b/src/sasl-xoauth2.conf index 0154171..ddee273 100644 --- a/src/sasl-xoauth2.conf +++ b/src/sasl-xoauth2.conf @@ -1,4 +1,5 @@ { + "external_token_manager": "no", "client_id": "CLIENT_ID_GOES_HERE", "client_secret": "CLIENT_SECRET_GOES_HERE" }