lots more refactoring

This commit is contained in:
Grant Limberg 2021-11-29 14:11:29 -08:00
parent 87fdd644d4
commit 5f548705dd
No known key found for this signature in database
GPG key ID: 2BA62CCABBB4095A
3 changed files with 256 additions and 201 deletions

View file

@ -147,6 +147,161 @@ size_t curlResponseWrite(void *ptr, size_t size, size_t nmemb, std::string *data
namespace ZeroTier { namespace ZeroTier {
// Configured networks
class NetworkState
{
public:
NetworkState()
: _webPort(9993)
, _tap((EthernetTap *)0)
, _idc(nullptr)
, _ainfo(nullptr)
{
// Real defaults are in network 'up' code in network event handler
_settings.allowManaged = true;
_settings.allowGlobal = false;
_settings.allowDefault = false;
_settings.allowDNS = false;
memset(&_config, 0, sizeof(ZT_VirtualNetworkConfig));
}
~NetworkState()
{
this->_managedRoutes.clear();
this->_tap.reset();
if (_ainfo) {
zeroidc::zeroidc_auth_info_delete(_ainfo);
_ainfo = nullptr;
}
if (_idc) {
zeroidc::zeroidc_stop(_idc);
zeroidc::zeroidc_delete(_idc);
_idc = nullptr;
}
}
void setWebPort(unsigned int port) {
_webPort = port;
}
void setTap(std::shared_ptr<EthernetTap> tap) {
this->_tap = tap;
}
std::shared_ptr<EthernetTap> tap() const {
return _tap;
}
OneService::NetworkSettings settings() const {
return _settings;
}
void setSettings(const OneService::NetworkSettings &settings) {
_settings = settings;
}
void setAllowManaged(bool allow) {
_settings.allowManaged = allow;
}
bool allowManaged() const {
return _settings.allowManaged;
}
void setAllowGlobal(bool allow) {
_settings.allowGlobal = allow;
}
bool allowGlobal() const {
return _settings.allowGlobal;
}
void setAllowDefault(bool allow) {
_settings.allowDefault = allow;
}
bool allowDefault() const {
return _settings.allowDefault;
}
void setAllowDNS(bool allow) {
_settings.allowDNS = allow;
}
bool allowDNS() const {
return _settings.allowDNS;
}
std::vector<InetAddress> allowManagedWhitelist() const {
return _settings.allowManagedWhitelist;
}
void addToAllowManagedWhiteList(const InetAddress& addr) {
_settings.allowManagedWhitelist.push_back(addr);
}
const ZT_VirtualNetworkConfig& config() {
return _config;
}
void setConfig(const ZT_VirtualNetworkConfig *nwc) {
memcpy(&_config, nwc, sizeof(ZT_VirtualNetworkConfig));
if (_config.ssoEnabled && _config.ssoVersion == 1) {
if (_idc == nullptr) {
assert(_config.issuerURL[0] != nullptr);
assert(_config.ssoClientID != nullptr);
assert(_config.centralAuthURL != nullptr);
_idc = zeroidc::zeroidc_new(
_config.issuerURL,
_config.ssoClientID,
_config.centralAuthURL,
_webPort
);
}
if (_ainfo != nullptr) {
zeroidc::zeroidc_auth_info_delete(_ainfo);
_ainfo = nullptr;
}
_ainfo = zeroidc::zeroidc_get_auth_info(
_idc,
_config.ssoState,
_config.ssoNonce
);
const char* url = zeroidc::zeroidc_get_auth_url(_ainfo);
memcpy(_config.authenticationURL, url, strlen(url));
_config.authenticationURL[strlen(url)] = 0;
}
}
std::vector<InetAddress>& managedIps() {
return _managedIps;
}
void setManagedIps(const std::vector<InetAddress> &managedIps) {
_managedIps = managedIps;
}
std::map< InetAddress, SharedPtr<ManagedRoute> >& managedRoutes() {
return _managedRoutes;
}
private:
unsigned int _webPort;
std::shared_ptr<EthernetTap> _tap;
ZT_VirtualNetworkConfig _config; // memcpy() of raw config from core
std::vector<InetAddress> _managedIps;
std::map< InetAddress, SharedPtr<ManagedRoute> > _managedRoutes;
OneService::NetworkSettings _settings;
zeroidc::ZeroIDC *_idc;
zeroidc::AuthInfo *_ainfo;
};
namespace { namespace {
static const InetAddress NULL_INET_ADDR; static const InetAddress NULL_INET_ADDR;
@ -173,12 +328,12 @@ static std::string _trimString(const std::string &s)
return s.substr(start,end - start); return s.substr(start,end - start);
} }
static void _networkToJson(nlohmann::json &nj,const ZT_VirtualNetworkConfig *nc,const std::string &portDeviceName,const OneService::NetworkSettings &localSettings) static void _networkToJson(nlohmann::json &nj,NetworkState &ns)
{ {
char tmp[256]; char tmp[256];
const char *nstatus = "",*ntype = ""; const char *nstatus = "",*ntype = "";
switch(nc->status) { switch(ns.config().status) {
case ZT_NETWORK_STATUS_REQUESTING_CONFIGURATION: nstatus = "REQUESTING_CONFIGURATION"; break; case ZT_NETWORK_STATUS_REQUESTING_CONFIGURATION: nstatus = "REQUESTING_CONFIGURATION"; break;
case ZT_NETWORK_STATUS_OK: nstatus = "OK"; break; case ZT_NETWORK_STATUS_OK: nstatus = "OK"; break;
case ZT_NETWORK_STATUS_ACCESS_DENIED: nstatus = "ACCESS_DENIED"; break; case ZT_NETWORK_STATUS_ACCESS_DENIED: nstatus = "ACCESS_DENIED"; break;
@ -187,65 +342,68 @@ static void _networkToJson(nlohmann::json &nj,const ZT_VirtualNetworkConfig *nc,
case ZT_NETWORK_STATUS_CLIENT_TOO_OLD: nstatus = "CLIENT_TOO_OLD"; break; case ZT_NETWORK_STATUS_CLIENT_TOO_OLD: nstatus = "CLIENT_TOO_OLD"; break;
case ZT_NETWORK_STATUS_AUTHENTICATION_REQUIRED: nstatus = "AUTHENTICATION_REQUIRED"; break; case ZT_NETWORK_STATUS_AUTHENTICATION_REQUIRED: nstatus = "AUTHENTICATION_REQUIRED"; break;
} }
switch(nc->type) { switch(ns.config().type) {
case ZT_NETWORK_TYPE_PRIVATE: ntype = "PRIVATE"; break; case ZT_NETWORK_TYPE_PRIVATE: ntype = "PRIVATE"; break;
case ZT_NETWORK_TYPE_PUBLIC: ntype = "PUBLIC"; break; case ZT_NETWORK_TYPE_PUBLIC: ntype = "PUBLIC"; break;
} }
OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.16llx",nc->nwid); OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.16llx",ns.config().nwid);
nj["id"] = tmp; nj["id"] = tmp;
nj["nwid"] = tmp; nj["nwid"] = tmp;
OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(unsigned int)((nc->mac >> 40) & 0xff),(unsigned int)((nc->mac >> 32) & 0xff),(unsigned int)((nc->mac >> 24) & 0xff),(unsigned int)((nc->mac >> 16) & 0xff),(unsigned int)((nc->mac >> 8) & 0xff),(unsigned int)(nc->mac & 0xff)); OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(unsigned int)((ns.config().mac >> 40) & 0xff),(unsigned int)((ns.config().mac >> 32) & 0xff),(unsigned int)((ns.config().mac >> 24) & 0xff),(unsigned int)((ns.config().mac >> 16) & 0xff),(unsigned int)((ns.config().mac >> 8) & 0xff),(unsigned int)(ns.config().mac & 0xff));
nj["mac"] = tmp; nj["mac"] = tmp;
nj["name"] = nc->name; nj["name"] = ns.config().name;
nj["status"] = nstatus; nj["status"] = nstatus;
nj["type"] = ntype; nj["type"] = ntype;
nj["mtu"] = nc->mtu; nj["mtu"] = ns.config().mtu;
nj["dhcp"] = (bool)(nc->dhcp != 0); nj["dhcp"] = (bool)(ns.config().dhcp != 0);
nj["bridge"] = (bool)(nc->bridge != 0); nj["bridge"] = (bool)(ns.config().bridge != 0);
nj["broadcastEnabled"] = (bool)(nc->broadcastEnabled != 0); nj["broadcastEnabled"] = (bool)(ns.config().broadcastEnabled != 0);
nj["portError"] = nc->portError; nj["portError"] = ns.config().portError;
nj["netconfRevision"] = nc->netconfRevision; nj["netconfRevision"] = ns.config().netconfRevision;
nj["portDeviceName"] = portDeviceName; nj["portDeviceName"] = ns.tap()->deviceName();
OneService::NetworkSettings localSettings = ns.settings();
nj["allowManaged"] = localSettings.allowManaged; nj["allowManaged"] = localSettings.allowManaged;
nj["allowGlobal"] = localSettings.allowGlobal; nj["allowGlobal"] = localSettings.allowGlobal;
nj["allowDefault"] = localSettings.allowDefault; nj["allowDefault"] = localSettings.allowDefault;
nj["allowDNS"] = localSettings.allowDNS; nj["allowDNS"] = localSettings.allowDNS;
nlohmann::json aa = nlohmann::json::array(); nlohmann::json aa = nlohmann::json::array();
for(unsigned int i=0;i<nc->assignedAddressCount;++i) { for(unsigned int i=0;i<ns.config().assignedAddressCount;++i) {
aa.push_back(reinterpret_cast<const InetAddress *>(&(nc->assignedAddresses[i]))->toString(tmp)); aa.push_back(reinterpret_cast<const InetAddress *>(&(ns.config().assignedAddresses[i]))->toString(tmp));
} }
nj["assignedAddresses"] = aa; nj["assignedAddresses"] = aa;
nlohmann::json ra = nlohmann::json::array(); nlohmann::json ra = nlohmann::json::array();
for(unsigned int i=0;i<nc->routeCount;++i) { for(unsigned int i=0;i<ns.config().routeCount;++i) {
nlohmann::json rj; nlohmann::json rj;
rj["target"] = reinterpret_cast<const InetAddress *>(&(nc->routes[i].target))->toString(tmp); rj["target"] = reinterpret_cast<const InetAddress *>(&(ns.config().routes[i].target))->toString(tmp);
if (nc->routes[i].via.ss_family == nc->routes[i].target.ss_family) if (ns.config().routes[i].via.ss_family == ns.config().routes[i].target.ss_family)
rj["via"] = reinterpret_cast<const InetAddress *>(&(nc->routes[i].via))->toIpString(tmp); rj["via"] = reinterpret_cast<const InetAddress *>(&(ns.config().routes[i].via))->toIpString(tmp);
else rj["via"] = nlohmann::json(); else rj["via"] = nlohmann::json();
rj["flags"] = (int)nc->routes[i].flags; rj["flags"] = (int)ns.config().routes[i].flags;
rj["metric"] = (int)nc->routes[i].metric; rj["metric"] = (int)ns.config().routes[i].metric;
ra.push_back(rj); ra.push_back(rj);
} }
nj["routes"] = ra; nj["routes"] = ra;
nlohmann::json mca = nlohmann::json::array(); nlohmann::json mca = nlohmann::json::array();
for(unsigned int i=0;i<nc->multicastSubscriptionCount;++i) { for(unsigned int i=0;i<ns.config().multicastSubscriptionCount;++i) {
nlohmann::json m; nlohmann::json m;
m["mac"] = MAC(nc->multicastSubscriptions[i].mac).toString(tmp); m["mac"] = MAC(ns.config().multicastSubscriptions[i].mac).toString(tmp);
m["adi"] = nc->multicastSubscriptions[i].adi; m["adi"] = ns.config().multicastSubscriptions[i].adi;
mca.push_back(m); mca.push_back(m);
} }
nj["multicastSubscriptions"] = mca; nj["multicastSubscriptions"] = mca;
nlohmann::json m; nlohmann::json m;
m["domain"] = nc->dns.domain; m["domain"] = ns.config().dns.domain;
m["servers"] = nlohmann::json::array(); m["servers"] = nlohmann::json::array();
for(int j=0;j<ZT_MAX_DNS_SERVERS;++j) { for(int j=0;j<ZT_MAX_DNS_SERVERS;++j) {
InetAddress a(nc->dns.server_addr[j]); InetAddress a(ns.config().dns.server_addr[j]);
if (a.isV4() || a.isV6()) { if (a.isV4() || a.isV6()) {
char buf[256]; char buf[256];
m["servers"].push_back(a.toIpString(buf)); m["servers"].push_back(a.toIpString(buf));
@ -253,9 +411,9 @@ static void _networkToJson(nlohmann::json &nj,const ZT_VirtualNetworkConfig *nc,
} }
nj["dns"] = m; nj["dns"] = m;
nj["authenticationURL"] = nc->authenticationURL; nj["authenticationURL"] = ns.config().authenticationURL;
nj["authenticationExpiryTime"] = nc->authenticationExpiryTime; nj["authenticationExpiryTime"] = ns.config().authenticationExpiryTime;
nj["ssoEnabled"] = nc->ssoEnabled; nj["ssoEnabled"] = ns.config().ssoEnabled;
} }
static void _peerToJson(nlohmann::json &pj,const ZT_Peer *peer) static void _peerToJson(nlohmann::json &pj,const ZT_Peer *peer)
@ -527,117 +685,7 @@ public:
// Deadline for the next background task service function // Deadline for the next background task service function
volatile int64_t _nextBackgroundTaskDeadline; volatile int64_t _nextBackgroundTaskDeadline;
// Configured networks
class NetworkState
{
public:
NetworkState()
: _tap((EthernetTap *)0)
, _idc(nullptr)
{
// Real defaults are in network 'up' code in network event handler
_settings.allowManaged = true;
_settings.allowGlobal = false;
_settings.allowDefault = false;
_settings.allowDNS = false;
memset(&_config, 0, sizeof(ZT_VirtualNetworkConfig));
}
~NetworkState()
{
this->_managedRoutes.clear();
this->_tap.reset();
if (_idc) {
zeroidc::zeroidc_delete(_idc);
_idc = nullptr;
}
}
void setTap(std::shared_ptr<EthernetTap> tap) {
this->_tap = tap;
}
std::shared_ptr<EthernetTap> tap() const {
return _tap;
}
NetworkSettings settings() const {
return _settings;
}
void setSettings(const NetworkSettings &settings) {
_settings = settings;
}
void setAllowManaged(bool allow) {
_settings.allowManaged = allow;
}
bool allowManaged() const {
return _settings.allowManaged;
}
void setAllowGlobal(bool allow) {
_settings.allowGlobal = allow;
}
bool allowGlobal() const {
return _settings.allowGlobal;
}
void setAllowDefault(bool allow) {
_settings.allowDefault = allow;
}
bool allowDefault() const {
return _settings.allowDefault;
}
void setAllowDNS(bool allow) {
_settings.allowDNS = allow;
}
bool allowDNS() const {
return _settings.allowDNS;
}
std::vector<InetAddress> allowManagedWhitelist() const {
return _settings.allowManagedWhitelist;
}
void addToAllowManagedWhiteList(const InetAddress& addr) {
_settings.allowManagedWhitelist.push_back(addr);
}
const ZT_VirtualNetworkConfig& config() {
return _config;
}
void setConfig(const ZT_VirtualNetworkConfig *nwc) {
memcpy(&_config, nwc, sizeof(ZT_VirtualNetworkConfig));
}
std::vector<InetAddress>& managedIps() {
return _managedIps;
}
void setManagedIps(const std::vector<InetAddress> &managedIps) {
_managedIps = managedIps;
}
std::map< InetAddress, SharedPtr<ManagedRoute> >& managedRoutes() {
return _managedRoutes;
}
private:
std::shared_ptr<EthernetTap> _tap;
ZT_VirtualNetworkConfig _config; // memcpy() of raw config from core
std::vector<InetAddress> _managedIps;
std::map< InetAddress, SharedPtr<ManagedRoute> > _managedRoutes;
NetworkSettings _settings;
zeroidc::ZeroIDC *_idc;
};
std::map<uint64_t,NetworkState> _nets; std::map<uint64_t,NetworkState> _nets;
Mutex _nets_m; Mutex _nets_m;
@ -1251,26 +1299,17 @@ public:
virtual bool setNetworkSettings(const uint64_t nwid,const NetworkSettings &settings) virtual bool setNetworkSettings(const uint64_t nwid,const NetworkSettings &settings)
{ {
Mutex::Lock _l(_nets_m);
std::map<uint64_t,NetworkState>::iterator n(_nets.find(nwid));
if (n == _nets.end())
return false;
n->second.setSettings(settings);
char nlcpath[4096]; char nlcpath[4096];
OSUtils::ztsnprintf(nlcpath,sizeof(nlcpath),"%s" ZT_PATH_SEPARATOR_S "%.16llx.local.conf",_networksPath.c_str(),nwid); OSUtils::ztsnprintf(nlcpath,sizeof(nlcpath),"%s" ZT_PATH_SEPARATOR_S "%.16llx.local.conf",_networksPath.c_str(),nwid);
FILE *out = fopen(nlcpath,"w"); FILE *out = fopen(nlcpath,"w");
if (out) { if (out) {
fprintf(out,"allowManaged=%d\n",(int)n->second.allowManaged()); fprintf(out,"allowManaged=%d\n",(int)settings.allowManaged);
fprintf(out,"allowGlobal=%d\n",(int)n->second.allowGlobal()); fprintf(out,"allowGlobal=%d\n",(int)settings.allowGlobal);
fprintf(out,"allowDefault=%d\n",(int)n->second.allowDefault()); fprintf(out,"allowDefault=%d\n",(int)settings.allowDefault);
fprintf(out,"allowDNS=%d\n",(int)n->second.allowDNS()); fprintf(out,"allowDNS=%d\n",(int)settings.allowDNS);
fclose(out); fclose(out);
} }
if (n->second.tap())
syncManagedStuff(n->second,true,true,true);
return true; return true;
} }
@ -1474,18 +1513,16 @@ public:
} }
} else if (ps[0] == "network") { } else if (ps[0] == "network") {
ZT_VirtualNetworkList *nws = _node->networks(); Mutex::Lock _l(_nets_m);
if (nws) { if (!_nets.empty()) {
if (ps.size() == 1) { if (ps.size() == 1) {
// Return [array] of all networks // Return [array] of all networks
res = nlohmann::json::array(); res = nlohmann::json::array();
for(unsigned long i=0;i<nws->networkCount;++i) { for (auto it = _nets.begin(); it != _nets.end(); ++it) {
OneService::NetworkSettings localSettings; NetworkState &ns = it->second;
getNetworkSettings(nws->networks[i].nwid,localSettings);
nlohmann::json nj; nlohmann::json nj;
_networkToJson(nj,&(nws->networks[i]),portDeviceName(nws->networks[i].nwid),localSettings); _networkToJson(res, ns);
res.push_back(nj);
} }
scode = 200; scode = 200;
@ -1493,18 +1530,13 @@ public:
// Return a single network by ID or 404 if not found // Return a single network by ID or 404 if not found
const uint64_t wantnw = Utils::hexStrToU64(ps[1].c_str()); const uint64_t wantnw = Utils::hexStrToU64(ps[1].c_str());
for(unsigned long i=0;i<nws->networkCount;++i) { if (_nets.find(wantnw) != _nets.end()) {
if (nws->networks[i].nwid == wantnw) { res = json::object();
OneService::NetworkSettings localSettings; NetworkState& ns = _nets[wantnw];
getNetworkSettings(nws->networks[i].nwid,localSettings); _networkToJson(res, ns);
_networkToJson(res,&(nws->networks[i]),portDeviceName(nws->networks[i].nwid),localSettings);
scode = 200; scode = 200;
break;
} }
}
} else scode = 404; } else scode = 404;
_node->freeQueryResult((void *)nws);
} else scode = 500; } else scode = 500;
} else if (ps[0] == "peer") { } else if (ps[0] == "peer") {
ZT_PeerList *pl = _node->peers(); ZT_PeerList *pl = _node->peers();
@ -1667,37 +1699,41 @@ public:
uint64_t wantnw = Utils::hexStrToU64(ps[1].c_str()); uint64_t wantnw = Utils::hexStrToU64(ps[1].c_str());
_node->join(wantnw,(void *)0,(void *)0); // does nothing if we are a member _node->join(wantnw,(void *)0,(void *)0); // does nothing if we are a member
ZT_VirtualNetworkList *nws = _node->networks(); Mutex::Lock l(_nets_m);
if (nws) { if (!_nets.empty()) {
for(unsigned long i=0;i<nws->networkCount;++i) { if (_nets.find(wantnw) != _nets.end()) {
if (nws->networks[i].nwid == wantnw) { NetworkState& ns = _nets[wantnw];
OneService::NetworkSettings localSettings;
getNetworkSettings(nws->networks[i].nwid,localSettings);
try { try {
json j(OSUtils::jsonParse(body)); json j(OSUtils::jsonParse(body));
if (j.is_object()) {
json &allowManaged = j["allowManaged"]; json &allowManaged = j["allowManaged"];
if (allowManaged.is_boolean()) localSettings.allowManaged = (bool)allowManaged; if (allowManaged.is_boolean()) {
ns.setAllowManaged((bool)allowManaged);
}
json& allowGlobal = j["allowGlobal"]; json& allowGlobal = j["allowGlobal"];
if (allowGlobal.is_boolean()) localSettings.allowGlobal = (bool)allowGlobal; if (allowGlobal.is_boolean()) {
ns.setAllowGlobal((bool)allowGlobal);
}
json& allowDefault = j["allowDefault"]; json& allowDefault = j["allowDefault"];
if (allowDefault.is_boolean()) localSettings.allowDefault = (bool)allowDefault; if (allowDefault.is_boolean()) {
ns.setAllowDefault((bool)allowDefault);
}
json& allowDNS = j["allowDNS"]; json& allowDNS = j["allowDNS"];
if (allowDNS.is_boolean()) localSettings.allowDNS = (bool)allowDNS; if (allowDNS.is_boolean()) {
ns.setAllowDNS((bool)allowDNS);
} }
} catch (...) { } catch (...) {
// discard invalid JSON // discard invalid JSON
} }
setNetworkSettings(wantnw, ns.settings());
if (ns.tap()) {
syncManagedStuff(ns,true,true,true);
}
setNetworkSettings(nws->networks[i].nwid,localSettings); _networkToJson(res, ns);
_networkToJson(res,&(nws->networks[i]),portDeviceName(nws->networks[i].nwid),localSettings);
scode = 200; scode = 200;
break;
} }
}
_node->freeQueryResult((void *)nws);
} else scode = 500; } else scode = 500;
} else scode = 404; } else scode = 404;
@ -2498,9 +2534,9 @@ public:
{ {
Mutex::Lock _l(_nets_m); Mutex::Lock _l(_nets_m);
NetworkState &n = _nets[nwid]; NetworkState &n = _nets[nwid];
n.setWebPort(_primaryPort);
switch (op) { switch (op) {
case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_UP: case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_UP:
if (!n.tap()) { if (!n.tap()) {
try { try {
@ -2615,7 +2651,6 @@ public:
_nets.erase(nwid); _nets.erase(nwid);
} }
break; break;
} }
return 0; return 0;
} }

View file

@ -72,6 +72,16 @@ pub extern "C" fn zeroidc_stop(ptr: *mut ZeroIDC) {
idc.stop(); idc.stop();
} }
#[no_mangle]
pub extern "C" fn zeroidc_is_running(ptr: *mut ZeroIDC) -> bool {
let idc = unsafe {
assert!(!ptr.is_null());
&mut *ptr
};
idc.is_running()
}
#[no_mangle] #[no_mangle]
pub extern "C" fn zeroidc_get_auth_info( pub extern "C" fn zeroidc_get_auth_info(
ptr: *mut ZeroIDC, ptr: *mut ZeroIDC,

View file

@ -117,6 +117,16 @@ impl ZeroIDC {
} }
} }
fn is_running(&mut self) -> bool {
let local = Arc::clone(&self.inner);
if (*local.lock().unwrap()).running {
true
} else {
false
}
}
fn get_auth_info(&mut self, csrf_token: String, nonce: String) -> Option<AuthInfo> { fn get_auth_info(&mut self, csrf_token: String, nonce: String) -> Option<AuthInfo> {
let (pkce_challenge, pkce_verifier) = PkceCodeChallenge::new_random_sha256(); let (pkce_challenge, pkce_verifier) = PkceCodeChallenge::new_random_sha256();