Skip to content

Commit f8528b6

Browse files
committed
Merge branch 'jul/oidc-auth-code-verification' into 'master'
Add authorization code verification method See merge request TankerHQ/sdk-rust!178
2 parents f1bdc77 + e248d63 commit f8528b6

File tree

15 files changed

+478
-192
lines changed

15 files changed

+478
-192
lines changed

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,13 @@ base64 = "<0.20"
3434
# This dep really depends on feature http, but can't express that due to https://github.com/rust-lang/cargo/issues/6915
3535
async-std = { version = "1.12.0", features = ["attributes"] }
3636

37+
# Self reference to enable "test only" features
38+
tankersdk = { path = ".", features = ["experimental-oidc"], default-features = false }
39+
3740
[features]
3841
default = ["http"]
3942
http = ["tokio/rt-multi-thread", "dep:reqwest", "dep:bytes"]
43+
experimental-oidc = []
4044
# Use system certificate store in addition to WebPKI roots.
4145
# Rustls only supports the sytem store on Windows/Linux/Mac, but this feature also enables the SSL_CERT_FILE env var.
4246
# If you use self-signed certificates, enable this feature and add your cert to SSL_CERT_FILE or to the system store.

src/core.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,21 @@ impl Core {
368368
pub fn prehash_password(password: &str) -> Result<String, Error> {
369369
block_on(CTankerLib::get().prehash_password(password))
370370
}
371+
372+
#[cfg(feature = "experimental-oidc")]
373+
pub async fn authenticate_with_idp(
374+
&self,
375+
provider_id: &str,
376+
cookie: &str,
377+
) -> Result<Verification, Error> {
378+
let provider_id = CString::new(provider_id).unwrap();
379+
let cookie = CString::new(cookie).unwrap();
380+
Ok(unsafe {
381+
CTankerLib::get()
382+
.authenticate_with_idp(self.ctanker, provider_id.as_ref(), cookie.as_ref())
383+
.await?
384+
})
385+
}
371386
}
372387

373388
impl Drop for Core {

src/ctanker.rs

Lines changed: 52 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -20,32 +20,40 @@ macro_rules! tanker_call_ext {
2020
}
2121

2222
mod cfuture;
23-
2423
pub(crate) use cfuture::*;
25-
use std::marker::PhantomData;
2624

27-
mod cstream;
25+
#[cfg(feature = "http")]
26+
pub mod chttp;
27+
#[cfg(feature = "http")]
28+
pub(crate) use chttp::*;
2829

30+
mod cstream;
2931
pub use cstream::*;
3032

33+
use crate::http::HttpClient;
34+
use crate::{
35+
AttachResult, EncryptionOptions, Error, ErrorCode, LogRecord, LogRecordLevel, Options, Padding,
36+
SharingOptions, Status, VerificationMethod, VerificationOptions,
37+
};
38+
use lazy_static::lazy_static;
39+
use num_enum::UnsafeFromPrimitive;
40+
use std::convert::TryFrom;
41+
use std::ffi::{c_void, CStr, CString};
42+
use std::marker::PhantomData;
43+
use std::os::raw::c_char;
44+
use std::ptr::NonNull;
45+
use std::sync::{Arc, Mutex, Once};
46+
3147
use self::bindings::*;
3248

3349
pub type CVerification = tanker_verification;
3450
pub type CEmailVerification = tanker_email_verification;
3551
pub type CPhoneNumberVerification = tanker_phone_number_verification;
52+
pub type COIDCAuthorizationCodeVerification = tanker_oidc_authorization_code_verification;
3653
pub type CVerificationMethod = tanker_verification_method;
3754
pub type CPreverifiedOIDCVerification = tanker_preverified_oidc_verification;
3855
pub type CEncSessPtr = *mut tanker_encryption_session_t;
3956
pub type LogHandlerCallback = Box<dyn Fn(LogRecord) + Send>;
40-
#[cfg(feature = "http")]
41-
pub type CHttpRequestHandle = *mut tanker_http_request_handle_t;
42-
43-
#[derive(Debug)]
44-
#[cfg_attr(not(feature = "http"), allow(dead_code))]
45-
pub struct CHttpRequest(pub(crate) *mut tanker_http_request_t);
46-
47-
// SAFETY: ctanker is thread-safe
48-
unsafe impl Send for CHttpRequest {}
4957

5058
#[derive(Copy, Clone, Debug)]
5159
pub struct CTankerPtr(pub(crate) *mut tanker_t);
@@ -62,19 +70,6 @@ unsafe impl Send for CVerificationPtr {}
6270
// SAFETY: ctanker is thread-safe
6371
unsafe impl Send for tanker_http_options {}
6472

65-
use crate::http::HttpClient;
66-
use crate::{
67-
AttachResult, EncryptionOptions, Error, ErrorCode, LogRecord, LogRecordLevel, Options, Padding,
68-
SharingOptions, Status, VerificationMethod, VerificationOptions,
69-
};
70-
use lazy_static::lazy_static;
71-
use num_enum::UnsafeFromPrimitive;
72-
use std::convert::TryFrom;
73-
use std::ffi::{c_void, CStr, CString};
74-
use std::os::raw::c_char;
75-
use std::ptr::NonNull;
76-
use std::sync::{Arc, Mutex, Once};
77-
7873
pub(crate) static RUST_SDK_VERSION: &str = env!("CARGO_PKG_VERSION");
7974
pub(crate) static RUST_SDK_TYPE: &str = "client-rust";
8075

@@ -128,50 +123,6 @@ unsafe extern "C" fn log_handler_thunk(clog: *const tanker_log_record) {
128123
callback(record);
129124
}
130125

131-
#[cfg(feature = "http")]
132-
unsafe extern "C" fn send_http_request(
133-
creq_ptr: *mut tanker_http_request,
134-
data: *mut c_void,
135-
) -> CHttpRequestHandle {
136-
let client = data as *const HttpClient;
137-
138-
// client is an Arc on the Rust side, but it's a raw pointer held by native, so Rust can't
139-
// fully track its lifetime. Since Arc::drop will decrement the count, we must increment it
140-
unsafe { Arc::increment_strong_count(client) };
141-
142-
// SAFETY: data is set to an Arc<HttpClient> in the tanker_options struct,
143-
// and we trust native to not send requests after Core has been dropped
144-
let client = unsafe { Arc::from_raw(client) };
145-
146-
// SAFETY: We trust the request struct from native
147-
let req = unsafe { crate::http::HttpRequest::new(CHttpRequest(creq_ptr)) };
148-
let req_handle = client.send_request(req);
149-
150-
// NOTE: If/when strict provenance is stabilized, this should be a std::ptr::invalid()
151-
req_handle as CHttpRequestHandle
152-
}
153-
154-
#[cfg(feature = "http")]
155-
unsafe extern "C" fn cancel_http_request(
156-
creq_ptr: *mut tanker_http_request,
157-
handle: CHttpRequestHandle,
158-
data: *mut c_void,
159-
) {
160-
let client = data as *const HttpClient;
161-
162-
// client is an Arc on the Rust side, but it's a raw pointer held by native, so Rust can't
163-
// fully track its lifetime. Since Arc::drop will decrement the count, we must increment it
164-
unsafe { Arc::increment_strong_count(client) };
165-
166-
// SAFETY: data is set to an Arc<HttpClient> in the tanker_options struct,
167-
// and we trust native to not send requests after Core has been dropped
168-
let client = unsafe { Arc::from_raw(client) };
169-
170-
// SAFETY: We trust the request struct from native
171-
let req = unsafe { crate::http::HttpRequest::new(CHttpRequest(creq_ptr)) };
172-
client.cancel_request(req, handle as usize);
173-
}
174-
175126
pub struct CTankerLib {
176127
#[cfg(target_family = "windows")]
177128
ctanker_api: ctanker_api,
@@ -217,8 +168,8 @@ impl CTankerLib {
217168
let http_options = match http_client {
218169
#[cfg(feature = "http")]
219170
Some(client) => tanker_http_options {
220-
send_request: Some(send_http_request),
221-
cancel_request: Some(cancel_http_request),
171+
send_request: Some(chttp::send_http_request),
172+
cancel_request: Some(chttp::cancel_http_request),
222173
data: Arc::as_ptr(&client) as *mut c_void,
223174
},
224175
_ => tanker_http_options {
@@ -262,34 +213,6 @@ impl CTankerLib {
262213
let _: Result<(), _> = fut.await; // Ignore errors, nothing useful we can do if destroy() fails
263214
}
264215

265-
#[cfg(feature = "http")]
266-
pub unsafe fn http_handle_response(
267-
&self,
268-
request: CHttpRequest,
269-
response: crate::http::HttpResponse,
270-
) {
271-
let mut cresponse = tanker_http_response_t {
272-
error_msg: response
273-
.error_msg
274-
.as_ref()
275-
.map(|s| s.as_ptr())
276-
.unwrap_or(std::ptr::null()),
277-
content_type: response
278-
.content_type
279-
.as_ref()
280-
.map(|s| s.as_ptr())
281-
.unwrap_or(std::ptr::null()),
282-
body: response
283-
.body
284-
.as_ref()
285-
.map(|s| s.as_ptr() as *const c_char)
286-
.unwrap_or(std::ptr::null()),
287-
body_size: response.body.as_ref().map(|v| v.len()).unwrap_or(0) as i64,
288-
status_code: response.status_code as i32,
289-
};
290-
unsafe { tanker_call!(self, tanker_http_handle_response(request.0, &mut cresponse)) };
291-
}
292-
293216
pub unsafe fn status(&self, ctanker: CTankerPtr) -> Status {
294217
let status = unsafe { tanker_call!(self, tanker_status(ctanker.0)) };
295218
// SAFETY: The native lib never returns invalid status codes
@@ -642,6 +565,36 @@ impl CTankerLib {
642565
fut.await
643566
}
644567

568+
#[cfg(feature = "experimental-oidc")]
569+
pub async unsafe fn authenticate_with_idp(
570+
&self,
571+
ctanker: CTankerPtr,
572+
provider_id: &CStr,
573+
cookie: &CStr,
574+
) -> Result<crate::Verification, Error> {
575+
let fut = unsafe {
576+
CFuture::<*mut tanker_oidc_authorization_code_verification>::new(tanker_call!(
577+
self,
578+
tanker_authenticate_with_idp(ctanker.0, provider_id.as_ptr(), cookie.as_ptr(),)
579+
))
580+
};
581+
let cresult: &mut tanker_oidc_authorization_code_verification = unsafe { &mut *fut.await? };
582+
583+
// SAFETY: If we get a valid OIDCAuthorizationCode verification method, every field is a valid string
584+
let c_authorization_code = unsafe { CStr::from_ptr(cresult.authorization_code) };
585+
let authorization_code = c_authorization_code.to_str().unwrap().into();
586+
let c_state = unsafe { CStr::from_ptr(cresult.state) };
587+
let state = c_state.to_str().unwrap().into();
588+
589+
unsafe { tanker_call!(self, tanker_free_authenticate_with_idp_result(cresult)) }
590+
591+
Ok(crate::Verification::OIDCAuthorizationCode {
592+
provider_id: provider_id.to_str().unwrap().into(),
593+
authorization_code,
594+
state,
595+
})
596+
}
597+
645598
pub async unsafe fn encryption_session_open(
646599
&self,
647600
ctanker: CTankerPtr,

src/ctanker/chttp.rs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
use crate::ctanker::*;
2+
3+
pub type CHttpRequestHandle = *mut tanker_http_request_handle_t;
4+
pub type CHttpHeader = tanker_http_header;
5+
6+
#[derive(Debug)]
7+
pub struct CHttpRequest(pub(crate) *mut tanker_http_request_t);
8+
9+
// SAFETY: ctanker is thread-safe
10+
unsafe impl Send for CHttpRequest {}
11+
12+
pub unsafe extern "C" fn send_http_request(
13+
creq_ptr: *mut tanker_http_request,
14+
data: *mut c_void,
15+
) -> CHttpRequestHandle {
16+
let client = data as *const HttpClient;
17+
18+
// client is an Arc on the Rust side, but it's a raw pointer held by native, so Rust can't
19+
// fully track its lifetime. Since Arc::drop will decrement the count, we must increment it
20+
unsafe { Arc::increment_strong_count(client) };
21+
22+
// SAFETY: data is set to an Arc<HttpClient> in the tanker_options struct,
23+
// and we trust native to not send requests after Core has been dropped
24+
let client = unsafe { Arc::from_raw(client) };
25+
26+
// SAFETY: We trust the request struct from native
27+
let req = unsafe { crate::http::request::HttpRequest::new(CHttpRequest(creq_ptr)) };
28+
let req_handle = client.send_request(req);
29+
30+
// NOTE: If/when strict provenance is stabilized, this should be a std::ptr::invalid()
31+
req_handle as CHttpRequestHandle
32+
}
33+
34+
pub unsafe extern "C" fn cancel_http_request(
35+
creq_ptr: *mut tanker_http_request,
36+
handle: CHttpRequestHandle,
37+
data: *mut c_void,
38+
) {
39+
let client = data as *const HttpClient;
40+
41+
// client is an Arc on the Rust side, but it's a raw pointer held by native, so Rust can't
42+
// fully track its lifetime. Since Arc::drop will decrement the count, we must increment it
43+
unsafe { Arc::increment_strong_count(client) };
44+
45+
// SAFETY: data is set to an Arc<HttpClient> in the tanker_options struct,
46+
// and we trust native to not send requests after Core has been dropped
47+
let client = unsafe { Arc::from_raw(client) };
48+
49+
// SAFETY: We trust the request struct from native
50+
let req = unsafe { crate::http::request::HttpRequest::new(CHttpRequest(creq_ptr)) };
51+
client.cancel_request(req, handle as usize);
52+
}
53+
54+
impl CTankerLib {
55+
pub unsafe fn http_handle_response(
56+
&self,
57+
request: CHttpRequest,
58+
response: crate::http::response::HttpResponse,
59+
) {
60+
let cheaders = response
61+
.headers
62+
.iter()
63+
.map(|header| tanker_http_header_t {
64+
name: header.name.as_ptr() as *const c_char,
65+
value: header.value.as_ptr() as *const c_char,
66+
})
67+
.collect::<Vec<_>>();
68+
69+
let mut cresponse = tanker_http_response_t {
70+
error_msg: response
71+
.error_msg
72+
.as_ref()
73+
.map(|s| s.as_ptr())
74+
.unwrap_or(std::ptr::null()),
75+
headers: cheaders.as_ptr() as *mut tanker_http_header_t,
76+
num_headers: response.headers.len() as i32,
77+
body: response
78+
.body
79+
.as_ref()
80+
.map(|s| s.as_ptr() as *const c_char)
81+
.unwrap_or(std::ptr::null()),
82+
body_size: response.body.as_ref().map(|v| v.len()).unwrap_or(0) as i64,
83+
status_code: response.status_code as i32,
84+
};
85+
86+
unsafe { tanker_call!(self, tanker_http_handle_response(request.0, &mut cresponse)) };
87+
}
88+
}

src/http.rs

Lines changed: 3 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,6 @@
1-
#[cfg_attr(not(feature = "http"), path = "http/client_disabled.rs")]
21
mod client;
3-
pub use client::HttpClient;
4-
5-
#[cfg(feature = "http")]
6-
mod response;
7-
#[cfg(feature = "http")]
8-
pub use response::HttpResponse;
2+
pub mod request;
3+
pub mod response;
94

10-
#[cfg(feature = "http")]
5+
pub use client::HttpClient;
116
pub type HttpRequestId = usize;
12-
13-
use crate::ctanker::CHttpRequest;
14-
15-
// NOTE: The member lifetimes are marked as 'static, but they really have the lifetime of crequest,
16-
// which cannot easily be expressed (lifetime is essentially 'self, without self-references).
17-
#[derive(Debug)]
18-
#[cfg_attr(not(feature = "http"), allow(dead_code))]
19-
pub struct HttpRequest {
20-
pub(crate) crequest: CHttpRequest,
21-
pub(crate) method: &'static str,
22-
pub(crate) url: &'static str,
23-
pub(crate) instance_id: &'static str,
24-
pub(crate) authorization: Option<&'static str>,
25-
pub(crate) body: &'static [u8],
26-
}
27-
28-
#[cfg(feature = "http")]
29-
impl HttpRequest {
30-
pub unsafe fn new(crequest: CHttpRequest) -> Self {
31-
use std::ffi::CStr;
32-
33-
// SAFETY: We trust that native strings are UTF-8 and the pointer/sizes are valid
34-
let creq = unsafe { &*crequest.0 };
35-
let authorization = if creq.authorization.is_null() {
36-
None
37-
} else {
38-
Some(CStr::from_ptr(creq.authorization).to_str().unwrap())
39-
};
40-
Self {
41-
crequest,
42-
method: CStr::from_ptr(creq.method).to_str().unwrap(),
43-
url: CStr::from_ptr(creq.url).to_str().unwrap(),
44-
instance_id: CStr::from_ptr(creq.instance_id).to_str().unwrap(),
45-
authorization,
46-
body: std::slice::from_raw_parts(creq.body as *const u8, creq.body_size as usize),
47-
}
48-
}
49-
}

0 commit comments

Comments
 (0)