loco_oauth2/
config.rs

1use crate::error::OAuth2StoreError;
2use crate::grants::authorization_code::{CookieConfig, Credentials, UrlConfig};
3use serde::de::Error;
4use serde::{Deserialize, Serialize};
5use serde_json::Value;
6use std::str::FromStr;
7
8/// `OAuth2 Authentication configuration`
9/// # Fields
10/// * `secret_key` - Optional, key for Private Cookie Jar, must be more than 64 bytes. If not provided, a new key will be generated.
11/// * `authorization_code` - Authorization code grant type
12///
13/// Example (development):
14/// ```yaml
15/// # config/development.yaml
16/// oauth2:
17///  secret_key: {{get_env(name="OAUTH_PRIVATE_KEY", default="144, 76, 183, 1, 15, 184, 233, 174, 214, 251, 190, 186, 122, 61, 74, 84, 225, 110, 189, 115, 10, 251, 133, 128, 52, 46, 15, 66, 85, 1, 245, 73, 27, 113, 189, 15, 209, 205, 61, 100, 73, 31, 18, 58, 235, 105, 141, 36, 70, 92, 231, 151, 27, 32, 243, 117, 30, 244, 110, 89, 233, 196, 137, 130")}} # Optional, key for Private Cookie Jar, must be more than 64 bytes. If not provided, a new key will be generated.
18///  authorization_code: # Authorization code grant type
19///   - client_identifier: google # Identifier for the OAuth2 provider. Replace 'google' with your provider's name if different, must be unique within the oauth2 config.
20///     client_credentials:
21///       client_id: <your client id> # Replace with your OAuth2 client ID.
22///       client_secret: <your client secret> # Replace with your OAuth2 client secret.
23///     url_config:
24///      auth_url: https://accounts.google.com/o/oauth2/auth # authorization endpoint from the provider
25///      token_url: https://www.googleapis.com/oauth2/v3/token # token endpoint from the provider for exchanging the authorization code for an access token
26///      redirect_url: http://localhost:5150/api/auth/google_callback # server callback endpoint for the provider
27///      profile_url: https://openidconnect.googleapis.com/v1/userinfo # user profile endpoint from the provider for getting user data
28///      scopes:
29///       - https://www.googleapis.com/auth/userinfo.email # Scopes for requesting access to user data
30///     cookie_config:
31///       protected_url: http://localhost:5150/api/auth/google_callback # Optional - For redirecting to protect url in cookie to prevent XSS attack
32///     timeout_seconds: 600 # Optional, default 600 seconds
33/// ```
34#[derive(Debug, Clone, Deserialize, Serialize)]
35pub struct Config {
36    pub secret_key: Option<Vec<u8>>,
37    pub authorization_code: Vec<AuthorizationCode>,
38}
39
40#[derive(Debug, Clone, Deserialize, Serialize)]
41pub struct AuthorizationCode {
42    pub client_identifier: String,
43    pub client_credentials: Credentials,
44    pub url_config: UrlConfig,
45    pub cookie_config: CookieConfig,
46    pub timeout_seconds: Option<u64>,
47}
48
49impl TryFrom<Value> for Config {
50    type Error = OAuth2StoreError;
51    #[tracing::instrument(name = "Convert Value to OAuth2Config")]
52    fn try_from(value: Value) -> Result<Self, Self::Error> {
53        let secret_key: Option<Vec<u8>> =
54            value.get("secret_key").and_then(|v| v.as_str()).map(|s| {
55                s.split(", ")
56                    .filter_map(|byte| u8::from_str(byte.trim()).ok())
57                    .collect()
58            });
59
60        let authorization_code: Vec<AuthorizationCode> = value
61            .get("authorization_code")
62            .and_then(|v| v.as_array())
63            .ok_or_else(|| {
64                serde_json::Error::custom("authorization_code is not an array or is missing")
65            })
66            .and_then(|v| {
67                v.iter()
68                    .map(|item| serde_json::from_value(item.clone()))
69                    .collect()
70            })?;
71        Ok(Self {
72            secret_key,
73            authorization_code,
74        })
75    }
76}