refactor out the separate AuthInfo struct

consolidated everything into the single IDC struct.  Should help keep from rotating the pkce token as often & causing issues with the login window flapping
This commit is contained in:
Grant Limberg 2021-12-15 14:16:17 -08:00
parent 2ee595553a
commit df9a7497b1
No known key found for this signature in database
GPG Key ID: 2BA62CCABBB4095A
3 changed files with 177 additions and 151 deletions

View File

@ -155,7 +155,6 @@ public:
: _webPort(9993)
, _tap((EthernetTap *)0)
, _idc(nullptr)
, _ainfo(nullptr)
{
// Real defaults are in network 'up' code in network event handler
_settings.allowManaged = true;
@ -170,11 +169,6 @@ public:
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);
@ -284,18 +278,13 @@ public:
// fprintf(stderr, "idc created (%s, %s, %s)\n", _config.issuerURL, _config.ssoClientID, _config.centralAuthURL);
}
if (_ainfo != nullptr) {
zeroidc::zeroidc_auth_info_delete(_ainfo);
_ainfo = nullptr;
}
_ainfo = zeroidc::zeroidc_get_auth_info(
zeroidc::zeroidc_set_nonce_and_csrf(
_idc,
_config.ssoState,
_config.ssoNonce
);
const char* url = zeroidc::zeroidc_get_auth_url(_ainfo);
const char* url = zeroidc::zeroidc_get_auth_url(_idc);
memcpy(_config.authenticationURL, url, strlen(url));
_config.authenticationURL[strlen(url)] = 0;
}
@ -314,28 +303,27 @@ public:
}
const char* getAuthURL() {
if (_ainfo != nullptr) {
return zeroidc::zeroidc_get_auth_url(_ainfo);
if (_idc != nullptr) {
return zeroidc::zeroidc_get_auth_url(_idc);
}
fprintf(stderr, "_ainfo is null\n");
fprintf(stderr, "_idc is null\n");
return "";
}
void doTokenExchange(const char *code) {
if (_ainfo == nullptr || _idc == nullptr) {
if (_idc == nullptr) {
fprintf(stderr, "ainfo or idc null\n");
return;
}
zeroidc::zeroidc_token_exchange(_idc, _ainfo, code);
zeroidc::zeroidc_auth_info_delete(_ainfo);
_ainfo = zeroidc::zeroidc_get_auth_info(
zeroidc::zeroidc_token_exchange(_idc, code);
zeroidc::zeroidc_set_nonce_and_csrf(
_idc,
_config.ssoState,
_config.ssoNonce
);
const char* url = zeroidc::zeroidc_get_auth_url(_ainfo);
const char* url = zeroidc::zeroidc_get_auth_url(_idc);
memcpy(_config.authenticationURL, url, strlen(url));
_config.authenticationURL[strlen(url)] = 0;
}
@ -357,7 +345,6 @@ private:
std::map< InetAddress, SharedPtr<ManagedRoute> > _managedRoutes;
OneService::NetworkSettings _settings;
zeroidc::ZeroIDC *_idc;
zeroidc::AuthInfo *_ainfo;
};
namespace {

View File

@ -2,7 +2,7 @@ use std::ffi::{CStr, CString};
use std::os::raw::c_char;
use url::{Url};
use crate::{AuthInfo, ZeroIDC};
use crate::ZeroIDC;
#[no_mangle]
pub extern "C" fn zeroidc_new(
@ -120,11 +120,10 @@ pub extern "C" fn zeroidc_get_exp_time(ptr: *mut ZeroIDC) -> u64 {
// }
#[no_mangle]
pub extern "C" fn zeroidc_get_auth_info(
pub extern "C" fn zeroidc_set_nonce_and_csrf(
ptr: *mut ZeroIDC,
csrf_token: *const c_char,
nonce: *const c_char,
) -> *mut AuthInfo {
nonce: *const c_char) {
let idc = unsafe {
assert!(!ptr.is_null());
&mut *ptr
@ -132,12 +131,12 @@ pub extern "C" fn zeroidc_get_auth_info(
if csrf_token.is_null() {
println!("csrf_token is null");
return std::ptr::null_mut();
return;
}
if nonce.is_null() {
println!("nonce is null");
return std::ptr::null_mut();
return;
}
let csrf_token = unsafe { CStr::from_ptr(csrf_token) }
@ -149,46 +148,30 @@ pub extern "C" fn zeroidc_get_auth_info(
.unwrap()
.to_string();
match idc.get_auth_info(csrf_token, nonce) {
Some(a) => Box::into_raw(Box::new(a)),
None => std::ptr::null_mut(),
}
idc.set_nonce_and_csrf(csrf_token, nonce);
}
#[no_mangle]
pub extern "C" fn zeroidc_auth_info_delete(ptr: *mut AuthInfo) {
if ptr.is_null() {
return;
}
unsafe {
Box::from_raw(ptr);
}
}
#[no_mangle]
pub extern "C" fn zeroidc_get_auth_url(ptr: *mut AuthInfo) -> *const c_char {
pub extern "C" fn zeroidc_get_auth_url(ptr: *mut ZeroIDC) -> *const c_char {
if ptr.is_null() {
println!("passed a null object");
return std::ptr::null_mut();
}
let ai = unsafe {
let idc = unsafe {
&mut *ptr
};
let s = CString::new(ai.url.to_string()).unwrap();
let s = CString::new(idc.auth_url()).unwrap();
return s.into_raw();
}
#[no_mangle]
pub extern "C" fn zeroidc_token_exchange(idc: *mut ZeroIDC, ai: *mut AuthInfo, code: *const c_char ) {
pub extern "C" fn zeroidc_token_exchange(idc: *mut ZeroIDC, code: *const c_char ) {
if idc.is_null() {
println!("idc is null");
return
}
if ai.is_null() {
println!("ai is null");
return
}
if code.is_null() {
println!("code is null");
return
@ -196,12 +179,10 @@ pub extern "C" fn zeroidc_token_exchange(idc: *mut ZeroIDC, ai: *mut AuthInfo, c
let idc = unsafe {
&mut *idc
};
let ai = unsafe {
&mut *ai
};
let code = unsafe{CStr::from_ptr(code)}.to_str().unwrap();
idc.do_token_exchange(ai, code);
idc.do_token_exchange( code);
}
#[no_mangle]

View File

@ -30,6 +30,18 @@ struct Inner {
access_token: Option<AccessToken>,
refresh_token: Option<RefreshToken>,
exp_time: u64,
url: Option<Url>,
csrf_token: Option<CsrfToken>,
nonce: Option<Nonce>,
pkce_verifier: Option<PkceCodeVerifier>,
}
impl Inner {
#[inline]
fn as_opt(&mut self) -> Option<&mut Inner> {
Some(self)
}
}
#[derive(Debug, Serialize, Deserialize)]
@ -45,13 +57,6 @@ fn nonce_func(nonce: String) -> Box<dyn Fn() -> Nonce> {
return Box::new(move || Nonce::new(nonce.to_string()));
}
pub struct AuthInfo {
url: Url,
csrf_token: CsrfToken,
nonce: Nonce,
pkce_verifier: Option<PkceCodeVerifier>,
}
fn systemtime_strftime<T>(dt: T, format: &str) -> String
where T: Into<OffsetDateTime>
{
@ -87,6 +92,11 @@ impl ZeroIDC {
access_token: None,
refresh_token: None,
exp_time: 0,
url: None,
csrf_token: None,
nonce: None,
pkce_verifier: None,
})),
};
@ -264,9 +274,78 @@ impl ZeroIDC {
return (*self.inner.lock().unwrap()).exp_time;
}
fn do_token_exchange(&mut self, auth_info: &mut AuthInfo, code: &str) {
if let Some(verifier) = auth_info.pkce_verifier.take() {
let token_response = (*self.inner.lock().unwrap()).oidc_client.as_ref().map(|c| {
fn set_nonce_and_csrf(&mut self, csrf_token: String, nonce: String) {
let local = Arc::clone(&self.inner);
(*local.lock().expect("can't lock inner")).as_opt().map(|i| {
let mut csrf_diff = false;
let mut nonce_diff = false;
let mut need_verifier = false;
match i.pkce_verifier {
None => {
need_verifier = true;
},
_ => (),
}
if let Some(csrf) = i.csrf_token.clone() {
if *csrf.secret() != csrf_token {
csrf_diff = true;
}
}
if let Some(n) = i.nonce.clone() {
if *n.secret() != nonce {
nonce_diff = true;
}
}
if need_verifier || csrf_diff || nonce_diff {
let (pkce_challenge, pkce_verifier) = PkceCodeChallenge::new_random_sha256();
let r = i.oidc_client.as_ref().map(|c| {
let (auth_url, csrf_token, nonce) = c
.authorize_url(
AuthenticationFlow::<CoreResponseType>::AuthorizationCode,
csrf_func(csrf_token),
nonce_func(nonce),
)
.add_scope(Scope::new("profile".to_string()))
.add_scope(Scope::new("email".to_string()))
.add_scope(Scope::new("offline_access".to_string()))
.add_scope(Scope::new("openid".to_string()))
.set_pkce_challenge(pkce_challenge)
.url();
(auth_url, csrf_token, nonce)
});
if let Some(r) = r {
i.url = Some(r.0);
i.csrf_token = Some(r.1);
i.nonce = Some(r.2);
i.pkce_verifier = Some(pkce_verifier);
}
}
});
}
fn auth_url(&self) -> String {
let url = (*self.inner.lock().expect("can't lock inner")).as_opt().map(|i| {
match i.url.clone() {
Some(u) => u.to_string(),
_ => "".to_string(),
}
});
match url {
Some(url) => url.to_string(),
None => "".to_string(),
}
}
fn do_token_exchange(&mut self, code: &str) {
let local = Arc::clone(&self.inner);
(*local.lock().unwrap()).as_opt().map(|i| {
if let Some(verifier) = i.pkce_verifier.take() {
let token_response = i.oidc_client.as_ref().map(|c| {
let r = c.exchange_code(AuthorizationCode::new(code.to_string()))
.set_pkce_verifier(verifier)
.request(http_client);
@ -286,9 +365,17 @@ impl ZeroIDC {
let id_token = tok.id_token().unwrap();
println!("ID token: {}", id_token.to_string());
let split = auth_info.csrf_token.secret().split("_");
let split = split.collect::<Vec<&str>>();
let mut split = "".to_string();
match i.csrf_token.clone() {
Some(csrf_token) => {
split = csrf_token.secret().to_owned();
},
_ => (),
}
let split = split.split("_").collect::<Vec<&str>>();
if split.len() == 2 {
let params = [("id_token", id_token.to_string()),("state", split[0].to_string())];
let client = reqwest::blocking::Client::new();
let res = client.post((*self.inner.lock().unwrap()).auth_endpoint.clone())
@ -325,41 +412,12 @@ impl ZeroIDC {
let refresh_token = tok.refresh_token();
println!("Refresh Token: {}", refresh_token.unwrap().secret());
}
} else {
println!("No pkce verifier! Can't exchange tokens!!!");
println!("invalid split length?!?");
}
}
}
fn get_auth_info(&mut self, csrf_token: String, nonce: String) -> Option<AuthInfo> {
let (pkce_challenge, pkce_verifier) = PkceCodeChallenge::new_random_sha256();
let network_id = self.get_network_id();
let r = (*self.inner.lock().unwrap()).oidc_client.as_ref().map(|c| {
let (auth_url, csrf_token, nonce) = c
.authorize_url(
AuthenticationFlow::<CoreResponseType>::AuthorizationCode,
csrf_func(csrf_token),
nonce_func(nonce),
)
.add_scope(Scope::new("profile".to_string()))
.add_scope(Scope::new("email".to_string()))
.add_scope(Scope::new("offline_access".to_string()))
.add_scope(Scope::new("openid".to_string()))
.set_pkce_challenge(pkce_challenge)
.add_extra_param("network_id", network_id)
.url();
// println!("URL: {}", auth_url);
return AuthInfo {
url: auth_url,
pkce_verifier: Some(pkce_verifier),
csrf_token,
nonce,
};
});
r
}
}