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
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
use crate::{
    acquire_token_silent, msal,
    msal::Msal,
    requests::{AuthorizationUrlRequest, RedirectRequest, SilentRequest},
    sso_silent, AuthenticationResult, Configuration, PublicClientApplication,
};
use wasm_bindgen::{JsCast, JsValue};

// TODO: Should i remove the on error since is only ever msal-browser error
pub struct RedirectApp<FSuccess>
where
    FSuccess: Fn(AuthenticationResult) + Clone,
    // FErr: Fn(JsValue),
{
    auth: msal::PublicClientApplication,
    on_redirect_success: FSuccess,
    // on_redirect_error: Option<FErr>,
}

impl<FSuccess> Clone for RedirectApp<FSuccess>
where
    FSuccess: Fn(AuthenticationResult) + Clone,
{
    fn clone(&self) -> Self {
        Self {
            auth: self.auth.clone().into(),
            on_redirect_success: self.on_redirect_success.clone(),
        }
    }
}

impl<FSuccess> Msal for RedirectApp<FSuccess>
where
    FSuccess: Fn(AuthenticationResult) + Clone,
{
    fn auth(&self) -> &msal::PublicClientApplication {
        &self.auth
    }
}

impl<FSuccess> PublicClientApplication for RedirectApp<FSuccess> where
    FSuccess: Fn(AuthenticationResult) + Clone // FErr: Fn(JsValue),
{
}

impl<FSuccess> RedirectApp<FSuccess>
where
    FSuccess: Fn(AuthenticationResult) + Clone,
    // FErr: Fn(JsValue),
{
    pub fn new(
        configuration: Configuration,
        on_redirect_success: FSuccess,
        // on_redirect_error: Option<FErr>,
    ) -> Self {
        let auth = msal::PublicClientApplication::new(configuration.into());
        Self {
            auth,
            on_redirect_success,
            // on_redirect_error,
        }
    }

    pub async fn login_redirect(&self) {
        let empty: [&str; 0] = [];
        self.login_redirect_with_scopes(&empty).await
    }

    pub async fn login_redirect_with_scopes<'a, T>(&self, scopes: &'a [T])
    where
        T: Into<String> + Clone,
    {
        match self.auth.handle_redirect_promise().await {
            Ok(auth_result) => {
                // AuthenticationResult will be undefined / null if not a redirect
                // Can't use the 'safe' methods since the type check fails even when valid as is an Object.
                let auth_res = auth_result.unchecked_into::<msal::AuthenticationResult>();
                if auth_res.is_undefined() || auth_res.is_null() {
                    self.auth.login_redirect(scopes.into())
                } else {
                    (self.on_redirect_success)(auth_res.into())
                }
            }
            // Will always be ok unless the msal library errors?
            Err(_) => {
                // if let Some(f) = &self.on_redirect_error {
                //     f(e)
                // }
            }
        }
    }

    pub async fn acquire_token_redirect<'a>(&self, request: &'a RedirectRequest<'a>) {
        self.auth.acquire_token_redirect(request.into())
    }

    pub async fn sso_silent<'a>(
        &self,
        request: &'a AuthorizationUrlRequest<'a>,
    ) -> Result<AuthenticationResult, JsValue> {
        sso_silent(&self.auth, request).await
    }

    pub async fn acquire_token_silent<'a>(
        &self,
        request: &'a SilentRequest<'a>,
    ) -> Result<AuthenticationResult, JsValue> {
        acquire_token_silent(&self.auth, request).await
    }
}

#[cfg(test)]
mod tests {
    wasm_bindgen_test_configure!(run_in_browser);

    use super::*;
    use crate::{tests::*, BrowserAuthOptions};
    use wasm_bindgen_test::*;

    #[allow(unused_must_use)]
    #[wasm_bindgen_test]
    fn login_redirect() {
        let b = BrowserAuthOptions::new(tests::CLIENT_ID)
            .set_authority(AUTHORITY)
            .set_redirect_uri(REDIRECT_URI);
        let config = Configuration::new(b);
        let client_app = RedirectApp::new(config, |_| ());
        client_app.login_redirect();
    }
}