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/// ```no_run
15/// use atomic_lti::validate::validate_launch;
16/// use atomic_lti::id_token::IdToken;
17/// use atomic_lti::stores::oidc_state_store::OIDCStateStore;
18/// # use atomic_lti::errors::OIDCError;
19///
20/// # async fn example(
21/// #   state: &str,
22/// #   oidc_state_store: &dyn OIDCStateStore,
23/// #   id_token: &IdToken,
24/// # ) -> Result<(), OIDCError> {
25/// validate_launch(state, oidc_state_store, id_token).await?;
26/// # Ok(())
27/// # }
28/// ```
29pub async fn validate_launch(
30  state: &str,
31  oidc_state_store: &dyn OIDCStateStore,
32  id_token: &IdToken,
33) -> Result<(), OIDCError> {
34  // Check the state from parameters matches the state in the store
35  if state != oidc_state_store.get_state().await {
36    return Err(OIDCError::StateInvalid("Invalid state value".to_string()));
37  }
38
39  // Check the OIDC state entry and make sure the state is not older than 10 minutes
40  if !is_expired(oidc_state_store.get_created_at().await) {
41    return Err(OIDCError::NonceExpired);
42  }
43
44  // Check the id token nonce against the oidc state nonce
45  if id_token.nonce != oidc_state_store.get_nonce().await {
46    return Err(OIDCError::NonceInvalid);
47  }
48
49  Ok(())
50}
51
52fn is_expired(datetime: NaiveDateTime) -> bool {
53  let now = Utc::now().naive_utc();
54  let ten_minutes_ago = now - Duration::minutes(10);
55
56  datetime > ten_minutes_ago
57}