mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-06-06 20:43:44 +02:00
chicken or egg problem.
member must exist in the database before we can generate a nonce & SSO URL
This commit is contained in:
parent
fed1846c6f
commit
74a678c1e1
2 changed files with 66 additions and 64 deletions
|
@ -1338,17 +1338,16 @@ void EmbeddedNetworkController::_request(
|
||||||
int64_t authenticationExpiryTime = (int64_t)OSUtils::jsonInt(member["authenticationExpiryTime"], 0);
|
int64_t authenticationExpiryTime = (int64_t)OSUtils::jsonInt(member["authenticationExpiryTime"], 0);
|
||||||
fprintf(stderr, "authExpiryTime: %lld\n", authenticationExpiryTime);
|
fprintf(stderr, "authExpiryTime: %lld\n", authenticationExpiryTime);
|
||||||
if ((authenticationExpiryTime == 0) || (authenticationExpiryTime < now)) {
|
if ((authenticationExpiryTime == 0) || (authenticationExpiryTime < now)) {
|
||||||
|
|
||||||
Dictionary<1024> authInfo;
|
|
||||||
std::string authenticationURL = _db.getSSOAuthURL(member);
|
std::string authenticationURL = _db.getSSOAuthURL(member);
|
||||||
if (!authenticationURL.empty()) {
|
if (!authenticationURL.empty()) {
|
||||||
|
Dictionary<1024> authInfo;
|
||||||
authInfo.add("aU", authenticationURL.c_str());
|
authInfo.add("aU", authenticationURL.c_str());
|
||||||
|
fprintf(stderr, "sending auth URL: %s\n", authenticationURL.c_str());
|
||||||
|
DB::cleanMember(member);
|
||||||
|
_db.save(member,true);
|
||||||
|
_sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_AUTHENTICATION_REQUIRED, authInfo.data(), authInfo.sizeBytes());
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
fprintf(stderr, "sending auth URL: %s\n", authenticationURL.c_str());
|
|
||||||
DB::cleanMember(member);
|
|
||||||
_db.save(member,true);
|
|
||||||
_sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_AUTHENTICATION_REQUIRED, authInfo.data(), authInfo.sizeBytes());
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -330,70 +330,73 @@ std::string PostgreSQL::getSSOAuthURL(const nlohmann::json &member)
|
||||||
|
|
||||||
std::string nonce = "";
|
std::string nonce = "";
|
||||||
|
|
||||||
// find an unused nonce, if one exists.
|
// check if the member exists first.
|
||||||
pqxx::result r = w.exec_params("SELECT nonce FROM ztc_sso_expiry "
|
pqxx::row count = w.exec_params1("SELECT count(id) FROM ztc_member WHERE id = $1 AND network_id = $2", memberId, networkId);
|
||||||
"WHERE network_id = $1 AND member_id = $2 "
|
if (count[0].as<int>() == 1) {
|
||||||
"AND authentication_expiry_time IS NULL AND ((NOW() AT TIME ZONE 'UTC') <= nonce_expiration)",
|
// find an unused nonce, if one exists.
|
||||||
networkId, memberId);
|
pqxx::result r = w.exec_params("SELECT nonce FROM ztc_sso_expiry "
|
||||||
|
"WHERE network_id = $1 AND member_id = $2 "
|
||||||
|
"AND authentication_expiry_time IS NULL AND ((NOW() AT TIME ZONE 'UTC') <= nonce_expiration)",
|
||||||
|
networkId, memberId);
|
||||||
|
|
||||||
if (r.size() == 1) {
|
if (r.size() == 1) {
|
||||||
// we have an existing nonce. Use it
|
// we have an existing nonce. Use it
|
||||||
nonce = r.at(0)[0].as<std::string>();
|
nonce = r.at(0)[0].as<std::string>();
|
||||||
} else if (r.empty()) {
|
} else if (r.empty()) {
|
||||||
// create a nonce
|
// create a nonce
|
||||||
char randBuf[16] = {0};
|
char randBuf[16] = {0};
|
||||||
Utils::getSecureRandom(randBuf, 16);
|
Utils::getSecureRandom(randBuf, 16);
|
||||||
char nonceBuf[256] = {0};
|
char nonceBuf[256] = {0};
|
||||||
Utils::hex(randBuf, sizeof(randBuf), nonceBuf);
|
Utils::hex(randBuf, sizeof(randBuf), nonceBuf);
|
||||||
nonce = std::string(nonceBuf);
|
nonce = std::string(nonceBuf);
|
||||||
|
|
||||||
pqxx::result ir = w.exec_params0("INSERT INTO ztc_sso_expiry "
|
pqxx::result ir = w.exec_params0("INSERT INTO ztc_sso_expiry "
|
||||||
"(nonce, nonce_expiration, network_id, member_id) VALUES "
|
"(nonce, nonce_expiration, network_id, member_id) VALUES "
|
||||||
"($1, TO_TIMESTAMP($2::double precision/1000), $3, $4)",
|
"($1, TO_TIMESTAMP($2::double precision/1000), $3, $4)",
|
||||||
nonce, OSUtils::now() + 300000, networkId, memberId);
|
nonce, OSUtils::now() + 300000, networkId, memberId);
|
||||||
} else {
|
} else {
|
||||||
// > 1 ?!? Thats an error!
|
// > 1 ?!? Thats an error!
|
||||||
fprintf(stderr, "> 1 unused nonce!\n");
|
fprintf(stderr, "> 1 unused nonce!\n");
|
||||||
exit(6);
|
exit(6);
|
||||||
}
|
}
|
||||||
|
|
||||||
r = w.exec_params("SELECT org.client_id, org.authorization_endpoint "
|
r = w.exec_params("SELECT org.client_id, org.authorization_endpoint "
|
||||||
"FROM ztc_network AS nw, ztc_org AS org "
|
"FROM ztc_network AS nw, ztc_org AS org "
|
||||||
"WHERE nw.id = $1 AND nw.sso_enabled = true AND org.owner_id = nw.owner_id", networkId);
|
"WHERE nw.id = $1 AND nw.sso_enabled = true AND org.owner_id = nw.owner_id", networkId);
|
||||||
|
|
||||||
std::string client_id = "";
|
std::string client_id = "";
|
||||||
std::string authorization_endpoint = "";
|
std::string authorization_endpoint = "";
|
||||||
|
|
||||||
if (r.size() == 1) {
|
if (r.size() == 1) {
|
||||||
client_id = r.at(0)[0].as<std::string>();
|
client_id = r.at(0)[0].as<std::string>();
|
||||||
authorization_endpoint = r.at(0)[1].as<std::string>();
|
authorization_endpoint = r.at(0)[1].as<std::string>();
|
||||||
} else if (r.size() > 1) {
|
} else if (r.size() > 1) {
|
||||||
fprintf(stderr, "ERROR: More than one auth endpoint for an organization?!?!? NetworkID: %s\n", networkId.c_str());
|
fprintf(stderr, "ERROR: More than one auth endpoint for an organization?!?!? NetworkID: %s\n", networkId.c_str());
|
||||||
}
|
}
|
||||||
// no catch all else because we don't actually care if no records exist here. just continue as normal.
|
// no catch all else because we don't actually care if no records exist here. just continue as normal.
|
||||||
|
|
||||||
if ((!client_id.empty())&&(!authorization_endpoint.empty())) {
|
if ((!client_id.empty())&&(!authorization_endpoint.empty())) {
|
||||||
have_auth = true;
|
have_auth = true;
|
||||||
|
|
||||||
uint8_t state[48];
|
uint8_t state[48];
|
||||||
HMACSHA384(_ssoPsk, nonce.data(), (unsigned int)nonce.length(), state);
|
HMACSHA384(_ssoPsk, nonce.data(), (unsigned int)nonce.length(), state);
|
||||||
char state_hex[256];
|
char state_hex[256];
|
||||||
Utils::hex(state, 48, state_hex);
|
Utils::hex(state, 48, state_hex);
|
||||||
|
|
||||||
const char *redirect_url = "redirect_uri=https%3A%2F%2Fmy.zerotier.com%2Fapi%2Fnetwork%2Fsso-auth"; // TODO: this should be configurable
|
const char *redirect_url = "redirect_uri=https%3A%2F%2Fmy.zerotier.com%2Fapi%2Fnetwork%2Fsso-auth"; // TODO: this should be configurable
|
||||||
OSUtils::ztsnprintf(authenticationURL, sizeof(authenticationURL),
|
OSUtils::ztsnprintf(authenticationURL, sizeof(authenticationURL),
|
||||||
"%s?response_type=id_token&response_mode=form_post&scope=openid+email+profile&redriect_uri=%s&nonce=%s&state=%s&client_id=%s",
|
"%s?response_type=id_token&response_mode=form_post&scope=openid+email+profile&redriect_uri=%s&nonce=%s&state=%s&client_id=%s",
|
||||||
authorization_endpoint.c_str(),
|
authorization_endpoint.c_str(),
|
||||||
redirect_url,
|
redirect_url,
|
||||||
nonce.c_str(),
|
nonce.c_str(),
|
||||||
state_hex,
|
state_hex,
|
||||||
client_id.c_str());
|
client_id.c_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_pool->unborrow(c);
|
_pool->unborrow(c);
|
||||||
} catch (std::exception &e) {
|
} catch (std::exception &e) {
|
||||||
fprintf(stderr, "ERROR: Error updating member on load: %s\n", e.what());
|
fprintf(stderr, "ERROR: Error updating member on load: %s\n", e.what());
|
||||||
exit(-1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::string(authenticationURL);
|
return std::string(authenticationURL);
|
||||||
|
|
Loading…
Add table
Reference in a new issue