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}