MSAL ==== This library is a stub, which depends on the libhimmelblau library. Please migrate to the [new library](https://crates.io/crates/libhimmelblau). The purpose of this project is to implement MSAL for Rust, based on the specifications found in the Microsoft API Reference for [ClientApplication Class](https://learn.microsoft.com/en-us/python/api/msal/msal.application.clientapplication?view=msal-py-latest) and [PublicClientApplication Class](https://learn.microsoft.com/en-us/python/api/msal/msal.application.publicclientapplication?view=msal-py-latest). These are Python references which will be mimicked in Rust here. > **_NOTE:_** Implementing the [ConfidentialClientApplication Class](https://learn.microsoft.com/en-us/python/api/msal/msal.application.confidentialclientapplication?view=msal-py-latest) is not currently a target for this project. If you are interested in volunteering to implement the ConfidentialClientApplication Class, please contact the maintainer. The project also implements the [MS-DRS] protocol, which is undocumented by microsoft. A [protocol specification](https://github.com/himmelblau-idm/aad-join-spec/releases/latest) is in progress as part of the himmelblau project. In addition to the ClientApplication Class and [MS-DRS] implementations, this project implements [MS-OAPXBC] sections [3.1.5.1.2 Request for Primary Refresh Token](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-oapxbc/d32d5cd0-05d4-4ec2-8bcc-ac29ce711c23) and [3.1.5.1.3 Exchange Primary Refresh Token for Access Token](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-oapxbc/06e2bf0d-8cea-4b11-ad78-d212330ebda9). These are not implemented in Microsoft's MSAL libraries, but are possible when authenticating from an enrolled device. How do I use this library? -------------------------- Import the module into your project, then include the PublicClientApplication: ```Rust use msal::PublicClientApplication; ``` Create an instance of the PublicClientApplication, then authenticate: ```Rust let authority = format!("https://login.microsoftonline.com/{}", tenant_id); let app = PublicClientApplication::new(client_id, Some(&authority)).expect("Failed creating app"); let scope = vec![]; let token = app.acquire_token_by_username_password(username, password, scope).await?; ``` You can obtain your `client_id` and `tenant_id` from the Azure portal. You can perform a silent auth using a previously obtained refresh token: ```Rust let token = app.acquire_token_silent(scope, &token.refresh_token).await?; ``` Or finally, you can perform a Device Authorization Grant: ```Rust let flow = app.initiate_device_flow(scope).await?; // Prompt the user with the message found in flow.message let token = app.acquire_token_by_device_flow(flow).await?; ``` If msal is built with the `broker` feature, you can enroll the device, then request an authentication token: ```Rust use kanidm_hsm_crypto::soft::SoftTpm; use kanidm_hsm_crypto::{BoxedDynTpm, Tpm, AuthValue}; // First create the TPM object and a machine_key let mut tpm = BoxedDynTpm::new(SoftTpm::new()); let auth_str = AuthValue::generate().expect("Failed to create hex pin"); let auth_value = AuthValue::from_str(&auth_str).expect("Unable to create auth value"); let loadable_machine_key = tpm .machine_key_create(&auth_value) .expect("Unable to create new machine key"); let machine_key = tpm .machine_key_load(&auth_value, &loadable_machine_key) .expect("Unable to load machine key"); let app = BrokerClientApplication::new(Some(&authority), None, None).expect("Failed creating app"); // Obtain a token for authentication. If authenticating here without MFA, the PRT and // user token will not have the mfa claim. Use initiate_device_flow_for_device_enrollment() // and acquire_token_by_device_flow() to authenticate with the // mfa claim. let token = app.acquire_token_by_username_password_for_device_enrollment(username, password).await?; // Specify the attributes which will be used for enrollment let attrs = match EnrollAttrs::new( domain.to_string(), Some("test_machine".to_string()), // Device name Some("Linux".to_string()), // Device type Some(0), // Join type Some("openSUSE Leap 15.5".to_string()), // OS version ) { Ok(attrs) => attrs, Err(e) => { println!("{:?}", e); return (); } }; // Use the tpm for enrollment. let (transport_key, cert_key, device_id) = app.enroll_device(&token.refresh_token, attrs, &mut tpm, &machine_key).await?; // Request an authentication token let token = app.acquire_token_by_username_password(username, password, scope, &mut tpm, &machine_key).await?; ``` In order to initialize a BrokerClientApplication that was previously enrolled, ensure you've cached your `auth_value`, `loadable_machine_key`, `transport_key`, and `cert_key`. The `auth_value` MUST be stored in a secure manor only accessible to your application. Preferably your application should execute as a unique user, and only that user will have read access to the `auth_value`. Re-initialize as follows: ```Rust let mut tpm = BoxedDynTpm::new(SoftTpm::new()); let loadable_machine_key = tpm .machine_key_create(&auth_value) .expect("Unable to create new machine key"); let machine_key = tpm .machine_key_load(&auth_value, &loadable_machine_key) .expect("Unable to load machine key"); let app = BrokerClientApplication::new(Some(&authority), Some(&transport_key), Some(&cert_key)).expect("Failed creating app"); ```