atomic_lti/validate.rs
1use crate::id_token::IdToken;
2use crate::errors::OIDCError;
3use crate::stores::oidc_state_store::OIDCStateStore;
4use chrono::{Duration, NaiveDateTime, Utc};
5
6/// Validate the launch request
7/// #arguements
8/// * `state` - The state parameter from the launch request parameters
9/// * `oidc_state_store` - The OIDC state store that implements the OIDCStateStore trait
10/// * `id_token` - The id token from the launch request
11/// #returns
12/// * `Result<(), OIDCError>` - Returns an error if the launch is invalid
13/// #example
14/// ```
15/// use atomic_lti::validate::validate_launch;
16/// use atomic_lti::id_token::IdToken;
17/// use atomic_lti::params::{LaunchParams, LaunchSettings};
18/// use atomic_lti::jwks::Jwks;
19/// use atomic_lti::platforms::{get_jwk_set, PlatformStore};
20/// use atomic_lti::stores::oidc_state_store::OIDCStateStore;
21/// ```
22/// pub async fn launch(
23/// req: HttpRequest,
24/// params: &LaunchParams,
25/// platform_store: &dyn PlatformStore,
26/// oidc_state_store: &dyn OIDCStateStore,
27/// hashed_script_name: &str,
28/// ) -> Result<HttpResponse, AtomicToolError> {
29/// let jwk_server_url = platform_store.get_jwk_server_url().await?;
30/// let jwk_set = get_jwk_set(jwk_server_url).await?;
31/// let id_token = decode(¶ms.id_token, &jwk_set)?;
32/// let requested_target_link_uri = req.uri().to_string();
33/// validate_launch(¶ms.state, oidc_state_store, &id_token).await?;
34///
35/// // ... additional code
36/// }
37/// ```
38pub async fn validate_launch(
39 state: &str,
40 oidc_state_store: &dyn OIDCStateStore,
41 id_token: &IdToken,
42) -> Result<(), OIDCError> {
43 // Check the state from parameters matches the state in the store
44 if state != oidc_state_store.get_state().await {
45 return Err(OIDCError::StateInvalid("Invalid state value".to_string()));
46 }
47
48 // Check the OIDC state entry and make sure the state is not older than 10 minutes
49 if !is_expired(oidc_state_store.get_created_at().await) {
50 return Err(OIDCError::NonceExpired);
51 }
52
53 // Check the id token nonce against the oidc state nonce
54 if id_token.nonce != oidc_state_store.get_nonce().await {
55 return Err(OIDCError::NonceInvalid);
56 }
57
58 Ok(())
59}
60
61fn is_expired(datetime: NaiveDateTime) -> bool {
62 let now = Utc::now().naive_utc();
63 let ten_minutes_ago = now - Duration::minutes(10);
64
65 datetime > ten_minutes_ago
66}