1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
use super::E2eiEnrollment;
use crate::{
    prelude::{E2eIdentityError, E2eIdentityResult},
    CryptoError, CryptoResult,
};
use core_crypto_keystore::connection::FetchFromDatabase;
use core_crypto_keystore::{entities::E2eiRefreshToken, CryptoKeystoreResult};
use mls_crypto_provider::MlsCryptoProvider;
use zeroize::Zeroize;

/// An OIDC refresh token managed by CoreCrypto to benefit from encryption-at-rest
#[derive(Debug, serde::Serialize, serde::Deserialize, Zeroize, derive_more::From, derive_more::Deref)]
#[zeroize(drop)]
pub struct RefreshToken(String);

impl RefreshToken {
    pub(crate) async fn find(key_store: &impl FetchFromDatabase) -> CryptoResult<RefreshToken> {
        key_store.find_unique::<E2eiRefreshToken>().await?.try_into()
    }

    pub(crate) async fn replace(self, backend: &MlsCryptoProvider) -> CryptoKeystoreResult<()> {
        let keystore = backend.keystore();
        let rt = E2eiRefreshToken::from(self);
        keystore.save(rt).await?;
        Ok(())
    }
}

impl E2eiEnrollment {
    /// 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 [E2eiEnrollment::new_oidc_challenge_request]
    pub fn get_refresh_token(&self) -> E2eIdentityResult<&str> {
        self.refresh_token
            .as_ref()
            .map(|rt| rt.as_str())
            .ok_or(E2eIdentityError::OutOfOrderEnrollment(
                "No OIDC refresh token registered yet or it has been persisted",
            ))
    }
}

impl TryFrom<E2eiRefreshToken> for RefreshToken {
    type Error = CryptoError;

    fn try_from(mut entity: E2eiRefreshToken) -> CryptoResult<Self> {
        let content = std::mem::take(&mut entity.content);
        Ok(Self(String::from_utf8(content)?))
    }
}

impl From<RefreshToken> for E2eiRefreshToken {
    fn from(mut rt: RefreshToken) -> Self {
        let content = std::mem::take(&mut rt.0);
        Self {
            content: content.into_bytes(),
        }
    }
}