Skip to content

Commit

Permalink
feat(e2ei)!: remove refreshToken handling from WASM altogether as it …
Browse files Browse the repository at this point in the history
…is not used
  • Loading branch information
beltram committed Jan 24, 2024
1 parent 1c333e9 commit 1d84dbb
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 107 deletions.
24 changes: 2 additions & 22 deletions crypto-ffi/bindings/js/CoreCrypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2353,19 +2353,16 @@ export class E2eiEnrollment {
* Creates a new challenge request for Wire Oidc challenge.
*
* @param idToken you get back from Identity Provider
* @param refreshToken you get back from Identity Provider to renew the access token
* @param previousNonce `replay-nonce` response header from `POST /acme/{provisioner-name}/authz/{authz-id}`
* @see https://www.rfc-editor.org/rfc/rfc8555.html#section-7.5.1
*/
async newOidcChallengeRequest(
idToken: string,
refreshToken: string,
previousNonce: string
): Promise<JsonRawData> {
return await CoreCryptoError.asyncMapErr(
this.#enrollment.new_oidc_challenge_request(
idToken,
refreshToken,
previousNonce
)
);
Expand All @@ -2378,15 +2375,9 @@ export class E2eiEnrollment {
* @param challenge HTTP response body
* @see https://www.rfc-editor.org/rfc/rfc8555.html#section-7.5.1
*/
async newOidcChallengeResponse(
cc: CoreCrypto,
challenge: JsonRawData
): Promise<void> {
async newOidcChallengeResponse(challenge: JsonRawData): Promise<void> {
return await CoreCryptoError.asyncMapErr(
this.#enrollment.new_oidc_challenge_response(
cc.inner() as CoreCryptoFfiTypes.CoreCrypto,
challenge
)
this.#enrollment.new_oidc_challenge_response(challenge)
);
}

Expand Down Expand Up @@ -2455,17 +2446,6 @@ export class E2eiEnrollment {
this.#enrollment.certificate_request(previousNonce)
);
}

/**
* Lets clients retrieve the OIDC refresh token to try to renew the user's authorization.
* If it's expired, the user needs to reauthenticate and they will update the refresh token
* in {@link newOidcChallengeRequest}
*/
async getRefreshToken(): Promise<String> {
return await CoreCryptoError.asyncMapErr(
this.#enrollment.get_refresh_token()
);
}
}

/**
Expand Down
11 changes: 3 additions & 8 deletions crypto-ffi/bindings/js/test/CoreCrypto.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -924,7 +924,7 @@ test("proteus", async () => {
test("end-to-end-identity", async () => {
const [ctx, page] = await initBrowser();

let refreshToken = await page.evaluate(async () => {
await page.evaluate(async () => {
const { CoreCrypto, Ciphersuite, CoreCryptoError } = await import("./corecrypto.js");

const ciphersuite = Ciphersuite.MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519;
Expand Down Expand Up @@ -1050,9 +1050,7 @@ test("end-to-end-identity", async () => {
enrollment = await cc.e2eiEnrollmentStashPop(storeHandle);

const idToken = "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2NzU5NjE3NTYsImV4cCI6MTY3NjA0ODE1NiwibmJmIjoxNjc1OTYxNzU2LCJpc3MiOiJodHRwOi8vaWRwLyIsInN1YiI6ImltcHA6d2lyZWFwcD1OREV5WkdZd05qYzJNekZrTkRCaU5UbGxZbVZtTWpReVpUSXpOVGM0TldRLzY1YzNhYzFhMTYzMWMxMzZAZXhhbXBsZS5jb20iLCJhdWQiOiJodHRwOi8vaWRwLyIsIm5hbWUiOiJTbWl0aCwgQWxpY2UgTSAoUUEpIiwiaGFuZGxlIjoiaW1wcDp3aXJlYXBwPWFsaWNlLnNtaXRoLnFhQGV4YW1wbGUuY29tIiwia2V5YXV0aCI6IlNZNzR0Sm1BSUloZHpSdEp2cHgzODlmNkVLSGJYdXhRLi15V29ZVDlIQlYwb0ZMVElSRGw3cjhPclZGNFJCVjhOVlFObEw3cUxjbWcifQ.0iiq3p5Bmmp8ekoFqv4jQu_GrnPbEfxJ36SCuw-UvV6hCi6GlxOwU7gwwtguajhsd1sednGWZpN8QssKI5_CDQ";
const refreshToken = "refresh-token";
const oidcChallengeReq = await enrollment.newOidcChallengeRequest(idToken, refreshToken, previousNonce);
const storedRefreshToken = await enrollment.getRefreshToken();
const oidcChallengeReq = await enrollment.newOidcChallengeRequest(idToken, previousNonce);

const oidcChallengeResp = {
"type": "wire-oidc-01",
Expand All @@ -1061,7 +1059,7 @@ test("end-to-end-identity", async () => {
"token": "2FpTOmNQvNfWDktNWt1oIJnjLE3MkyFb",
"target": "http://example.com/target"
};
await enrollment.newOidcChallengeResponse(cc, jsonToByteArray(oidcChallengeResp));
await enrollment.newOidcChallengeResponse(jsonToByteArray(oidcChallengeResp));

const orderUrl = "https://example.com/acme/wire-acme/order/C7uOXEgg5KPMPtbdE3aVMzv7cJjwUVth";
const checkOrderReq = await enrollment.checkOrderRequest(orderUrl, previousNonce);
Expand Down Expand Up @@ -1115,11 +1113,8 @@ test("end-to-end-identity", async () => {
await enrollment.finalizeResponse(jsonToByteArray(finalizeResp));

const certificateReq = await enrollment.certificateRequest(previousNonce);
return storedRefreshToken;
});

expect(refreshToken).toBe("refresh-token");

await page.close();
await ctx.close();
});
Expand Down
28 changes: 4 additions & 24 deletions crypto-ffi/src/wasm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2798,33 +2798,25 @@ impl E2eiEnrollment {
}

/// See [core_crypto::e2e_identity::WireE2eIdentity::new_oidc_challenge_request]
pub fn new_oidc_challenge_request(
&mut self,
id_token: String,
refresh_token: String,
previous_nonce: String,
) -> Promise {
pub fn new_oidc_challenge_request(&mut self, id_token: String, previous_nonce: String) -> Promise {
let this = self.0.clone();
future_to_promise(
async move {
let mut this = this.write().await;
let chall = this.new_oidc_challenge_request(id_token, refresh_token, previous_nonce)?;
let chall = this.new_oidc_challenge_request(id_token, previous_nonce)?;
WasmCryptoResult::Ok(Uint8Array::from(chall.as_slice()).into())
}
.err_into(),
)
}

/// See [core_crypto::e2e_identity::WireE2eIdentity::new_oidc_challenge_response]
pub fn new_oidc_challenge_response(&mut self, cc: &CoreCrypto, challenge: Uint8Array) -> Promise {
let cc = cc.inner.clone();
pub fn new_oidc_challenge_response(&mut self, challenge: Uint8Array) -> Promise {
let this = self.0.clone();
future_to_promise(
async move {
let cc = cc.write().await;
let mut this = this.write().await;
this.new_oidc_challenge_response(cc.provider(), challenge.to_vec())
.await?;
this.new_oidc_challenge_response(challenge.to_vec()).await?;
WasmCryptoResult::Ok(JsValue::UNDEFINED)
}
.err_into(),
Expand Down Expand Up @@ -2893,18 +2885,6 @@ impl E2eiEnrollment {
.err_into(),
)
}

/// See [core_crypto::e2e_identity::WireE2eIdentity::get_refresh_token]
pub fn get_refresh_token(&mut self) -> Promise {
let this = self.0.clone();
future_to_promise(
async move {
let this = this.read().await;
WasmCryptoResult::Ok(this.get_refresh_token()?.to_string().into())
}
.err_into(),
)
}
}

/// Holds URLs of all the standard ACME endpoint supported on an ACME server.
Expand Down
117 changes: 72 additions & 45 deletions crypto/src/e2e_identity/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use error::*;
use mls_crypto_provider::MlsCryptoProvider;

use crate::{
e2e_identity::{crypto::E2eiSignatureKeypair, id::QualifiedE2eiClientId, refresh_token::RefreshToken},
e2e_identity::{crypto::E2eiSignatureKeypair, id::QualifiedE2eiClientId},
mls::credential::x509::CertificatePrivateKey,
prelude::{id::ClientId, identifier::ClientIdentifier, CertificateBundle, MlsCentral, MlsCiphersuite},
CryptoError, CryptoResult,
Expand All @@ -27,6 +27,7 @@ pub mod enabled;
pub mod error;
pub(crate) mod id;
pub(crate) mod identity;
#[cfg(not(target_family = "wasm"))]
pub(crate) mod refresh_token;
pub(crate) mod rotate;
pub(crate) mod stash;
Expand Down Expand Up @@ -62,7 +63,6 @@ impl MlsCentral {
ciphersuite: MlsCiphersuite,
) -> CryptoResult<E2eiEnrollment> {
let signature_keypair = None; // fresh install without a Basic client. Supplying None will generate a new keypair
let refresh_token = None; // fresh install so no refresh token registered yet
E2eiEnrollment::try_new(
client_id,
display_name,
Expand All @@ -72,7 +72,8 @@ impl MlsCentral {
&self.mls_backend,
ciphersuite,
signature_keypair,
refresh_token,
#[cfg(not(target_family = "wasm"))]
None, // fresh install so no refresh token registered yet
)
}

Expand Down Expand Up @@ -289,12 +290,13 @@ pub struct E2eiEnrollment {
expiry: core::time::Duration,
directory: Option<types::E2eiAcmeDirectory>,
account: Option<wire_e2e_identity::prelude::E2eiAcmeAccount>,
user_authz: Option<wire_e2e_identity::prelude::E2eiAcmeAuthorization>,
device_authz: Option<wire_e2e_identity::prelude::E2eiAcmeAuthorization>,
user_authz: Option<E2eiAcmeAuthorization>,
device_authz: Option<E2eiAcmeAuthorization>,
valid_order: Option<wire_e2e_identity::prelude::E2eiAcmeOrder>,
finalize: Option<wire_e2e_identity::prelude::E2eiAcmeFinalize>,
ciphersuite: MlsCiphersuite,
refresh_token: Option<RefreshToken>,
#[cfg(not(target_family = "wasm"))]
refresh_token: Option<refresh_token::RefreshToken>,
}

impl std::ops::Deref for E2eiEnrollment {
Expand Down Expand Up @@ -324,7 +326,7 @@ impl E2eiEnrollment {
backend: &MlsCryptoProvider,
ciphersuite: MlsCiphersuite,
sign_keypair: Option<E2eiSignatureKeypair>,
refresh_token: Option<RefreshToken>,
#[cfg(not(target_family = "wasm"))] refresh_token: Option<refresh_token::RefreshToken>,
) -> CryptoResult<Self> {
let alg = ciphersuite.try_into()?;
let sign_sk = match sign_keypair {
Expand All @@ -350,6 +352,7 @@ impl E2eiEnrollment {
valid_order: None,
finalize: None,
ciphersuite,
#[cfg(not(target_family = "wasm"))]
refresh_token,
})
}
Expand Down Expand Up @@ -558,11 +561,14 @@ impl E2eiEnrollment {
pub fn new_oidc_challenge_request(
&mut self,
id_token: String,
refresh_token: String,
#[cfg(not(target_family = "wasm"))] refresh_token: String,
previous_nonce: String,
) -> E2eIdentityResult<Json> {
if refresh_token.is_empty() {
return Err(E2eIdentityError::InvalidRefreshToken);
#[cfg(not(target_family = "wasm"))]
{
if refresh_token.is_empty() {
return Err(E2eIdentityError::InvalidRefreshToken);
}
}
let authz = self.user_authz.as_ref().ok_or(E2eIdentityError::OutOfOrderEnrollment(
"You must first call 'newAuthzResponse()'",
Expand All @@ -576,7 +582,10 @@ impl E2eiEnrollment {
))?;
let challenge = self.acme_oidc_challenge_request(id_token, challenge, account, previous_nonce)?;
let challenge = serde_json::to_vec(&challenge)?;
self.refresh_token.replace(refresh_token.into());
#[cfg(not(target_family = "wasm"))]
{
self.refresh_token.replace(refresh_token.into());
}
Ok(challenge)
}

Expand All @@ -588,19 +597,23 @@ impl E2eiEnrollment {
/// * `challenge` - http response body
pub async fn new_oidc_challenge_response(
&mut self,
backend: &MlsCryptoProvider,
#[cfg(not(target_family = "wasm"))] backend: &MlsCryptoProvider,
challenge: Json,
) -> E2eIdentityResult<()> {
let challenge = serde_json::from_slice(&challenge[..])?;
self.acme_new_challenge_response(challenge)?;
// Now that the OIDC challenge is valid, we can store the refresh token for future uses. Note
// that we could have persisted it at the end of the enrollment but what if the next enrollment
// steps fail ? Is it a reason good enough not to persist the token and ask the user to
// authenticate again: probably not.
let refresh_token = self.refresh_token.take().ok_or(E2eIdentityError::OutOfOrderEnrollment(
"You must first call 'new_oidc_challenge_request()'",
))?;
self.replace_refresh_token(backend, refresh_token).await?;

#[cfg(not(target_family = "wasm"))]
{
// Now that the OIDC challenge is valid, we can store the refresh token for future uses. Note
// that we could have persisted it at the end of the enrollment but what if the next enrollment
// steps fail ? Is it a reason good enough not to persist the token and ask the user to
// authenticate again: probably not.
let refresh_token = self.refresh_token.take().ok_or(E2eIdentityError::OutOfOrderEnrollment(
"You must first call 'new_oidc_challenge_request()'",
))?;
self.replace_refresh_token(backend, refresh_token).await?;
}
Ok(())
}

Expand Down Expand Up @@ -712,13 +725,11 @@ pub mod tests {
use serde_json::json;
use wasm_bindgen_test::*;

use crate::e2e_identity::refresh_token::RefreshToken;
use crate::prelude::MlsCredentialType;
use crate::{
e2e_identity::id::QualifiedE2eiClientId,
prelude::{
CertificateBundle, E2eIdentityError, E2eIdentityResult, E2eiConversationState, E2eiEnrollment, MlsCentral,
INITIAL_KEYING_MATERIAL_COUNT,
MlsCredentialType, INITIAL_KEYING_MATERIAL_COUNT,
},
test_utils::{central::TEAM, *},
CryptoResult,
Expand Down Expand Up @@ -801,25 +812,32 @@ pub mod tests {
// used to verify persisting the instance actually does restore it entirely
restore: impl Fn(E2eiEnrollment, MlsCentral) -> RestoreFnReturn<'a>,
) -> E2eIdentityResult<(MlsCentral, E2eiEnrollment, String)> {
if is_renewal {
let initial_refresh_token = RefreshToken::from("initial-refresh-token".to_string());
let initial_refresh_token = E2eiRefreshToken::from(initial_refresh_token);
let mut conn = cc.mls_backend.key_store().borrow_conn().await?;
initial_refresh_token.replace(&mut conn).await.unwrap();
#[cfg(not(target_family = "wasm"))]
{
if is_renewal {
let initial_refresh_token =
crate::e2e_identity::refresh_token::RefreshToken::from("initial-refresh-token".to_string());
let initial_refresh_token = E2eiRefreshToken::from(initial_refresh_token);
let mut conn = cc.mls_backend.key_store().borrow_conn().await?;
initial_refresh_token.replace(&mut conn).await.unwrap();
}
}

let wrapper = E2eiInitWrapper { cc: &cc, case };
let mut enrollment = init(wrapper).await.map_err(|_| E2eIdentityError::ImplementationError)?;

if is_renewal {
assert!(enrollment.refresh_token.is_some());
assert!(cc.find_refresh_token().await.is_ok());
} else {
assert!(matches!(
enrollment.get_refresh_token().unwrap_err(),
E2eIdentityError::OutOfOrderEnrollment(_)
));
assert!(cc.find_refresh_token().await.is_err());
#[cfg(not(target_family = "wasm"))]
{
if is_renewal {
assert!(enrollment.refresh_token.is_some());
assert!(cc.find_refresh_token().await.is_ok());
} else {
assert!(matches!(
enrollment.get_refresh_token().unwrap_err(),
E2eIdentityError::OutOfOrderEnrollment(_)
));
assert!(cc.find_refresh_token().await.is_err());
}
}

let (display_name, handle) = (enrollment.display_name.clone(), &enrollment.handle.clone());
Expand Down Expand Up @@ -953,13 +971,16 @@ pub mod tests {
let (mut enrollment, cc) = restore(enrollment, cc).await;

let id_token = "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2NzU5NjE3NTYsImV4cCI6MTY3NjA0ODE1NiwibmJmIjoxNjc1OTYxNzU2LCJpc3MiOiJodHRwOi8vaWRwLyIsInN1YiI6ImltcHA6d2lyZWFwcD1OREV5WkdZd05qYzJNekZrTkRCaU5UbGxZbVZtTWpReVpUSXpOVGM0TldRLzY1YzNhYzFhMTYzMWMxMzZAZXhhbXBsZS5jb20iLCJhdWQiOiJodHRwOi8vaWRwLyIsIm5hbWUiOiJTbWl0aCwgQWxpY2UgTSAoUUEpIiwiaGFuZGxlIjoiaW1wcDp3aXJlYXBwPWFsaWNlLnNtaXRoLnFhQGV4YW1wbGUuY29tIiwia2V5YXV0aCI6IlNZNzR0Sm1BSUloZHpSdEp2cHgzODlmNkVLSGJYdXhRLi15V29ZVDlIQlYwb0ZMVElSRGw3cjhPclZGNFJCVjhOVlFObEw3cUxjbWcifQ.0iiq3p5Bmmp8ekoFqv4jQu_GrnPbEfxJ36SCuw-UvV6hCi6GlxOwU7gwwtguajhsd1sednGWZpN8QssKI5_CDQ".to_string();
#[cfg(not(target_family = "wasm"))]
let new_refresh_token = "new-refresh-token";
let _oidc_chall_req = enrollment.new_oidc_challenge_request(
id_token,
#[cfg(not(target_family = "wasm"))]
new_refresh_token.to_string(),
previous_nonce.to_string(),
)?;

#[cfg(not(target_family = "wasm"))]
assert!(enrollment.get_refresh_token().is_ok());

let oidc_chall_resp = json!({
Expand All @@ -970,14 +991,20 @@ pub mod tests {
"target": "http://example.com/target"
});
let oidc_chall_resp = serde_json::to_vec(&oidc_chall_resp)?;
enrollment
.new_oidc_challenge_response(&cc.mls_backend, oidc_chall_resp)
.await?;

// Now Refresh token is persisted in the keystore
assert_eq!(cc.find_refresh_token().await.unwrap().as_str(), new_refresh_token);
// No reason at this point to have the refresh token in memory
assert!(enrollment.get_refresh_token().is_err());

#[cfg(not(target_family = "wasm"))]
{
enrollment
.new_oidc_challenge_response(&cc.mls_backend, oidc_chall_resp)
.await?;
// Now Refresh token is persisted in the keystore
assert_eq!(cc.find_refresh_token().await.unwrap().as_str(), new_refresh_token);
// No reason at this point to have the refresh token in memory
assert!(enrollment.get_refresh_token().is_err());
}

#[cfg(target_family = "wasm")]
enrollment.new_oidc_challenge_response(oidc_chall_resp).await?;

let (mut enrollment, cc) = restore(enrollment, cc).await;

Expand Down
Loading

0 comments on commit 1d84dbb

Please sign in to comment.