headless_lms_server/programs/seed/
seed_oauth_clients.rs

1use std::str::FromStr;
2
3use headless_lms_models::{
4    library::oauth::{Digest, GrantTypeName, pkce},
5    oauth_client,
6};
7use sqlx::{Pool, Postgres};
8use uuid::Uuid;
9
10pub struct SeedOAuthClientsResult {
11    pub client_db_id: Uuid,
12}
13
14pub async fn seed_oauth_clients(db_pool: Pool<Postgres>) -> anyhow::Result<SeedOAuthClientsResult> {
15    info!("Inserting OAuth Clients");
16    let secret =
17        Digest::from_str("396b544a35b29f7d613452a165dcaebf4d71b80e981e687e91ce6d9ba9679cb2")
18            .unwrap(); // "very-secret"
19    let mut conn = db_pool.acquire().await?;
20    // One redirect URI per Playwright worker (ports 8765..8784) so each worker has its own callback server.
21    // Must match system-tests getRedirectUri(): http://127.0.0.1:{port}/callback
22    let mut redirect_uris: Vec<String> = (8765..=8784)
23        .map(|p| format!("http://127.0.0.1:{p}/callback"))
24        .collect();
25    redirect_uris.push("https://localhost.emobix.co.uk:8443/test/a/testing/callback".to_string());
26
27    let scopes = vec![
28        "openid".to_string(),
29        "profile".to_string(),
30        "email".to_string(),
31        "offline_access".to_string(),
32    ];
33    let allowed_grant_types = vec![
34        GrantTypeName::AuthorizationCode,
35        GrantTypeName::RefreshToken,
36    ];
37    let pkce_methods_allowed = vec![pkce::PkceMethod::S256];
38    let allowed_origins = vec!["http://localhost".to_string()];
39
40    let new_client_parms = oauth_client::NewClientParams {
41        client_name: "Test Client",
42        application_type: oauth_client::ApplicationType::Web,
43        client_id: "test-client-id",
44        client_secret: Some(&secret), // "very-secret"
45        client_secret_expires_at: None,
46        redirect_uris: redirect_uris.as_slice(),
47        allowed_grant_types: &allowed_grant_types,
48        scopes: scopes.as_slice(),
49        allowed_origins: Some(allowed_origins.as_slice()),
50        bearer_allowed: true,
51        pkce_methods_allowed: &pkce_methods_allowed,
52        post_logout_redirect_uris: None,
53        require_pkce: true,
54        token_endpoint_auth_method: oauth_client::TokenEndpointAuthMethod::ClientSecretPost,
55    };
56
57    let client = if let Some(existing) =
58        oauth_client::OAuthClient::find_by_client_id_optional(&mut conn, "test-client-id").await?
59    {
60        existing
61    } else {
62        oauth_client::OAuthClient::insert(&mut conn, new_client_parms).await?
63    };
64
65    let new_client_parms_2 = oauth_client::NewClientParams {
66        client_name: "Test Client 2",
67        application_type: oauth_client::ApplicationType::Web,
68        client_id: "test-client-id-2",
69        client_secret: Some(&secret), // "very-secret"
70        client_secret_expires_at: None,
71        redirect_uris: redirect_uris.as_slice(),
72        allowed_grant_types: &allowed_grant_types,
73        scopes: scopes.as_slice(),
74        allowed_origins: Some(allowed_origins.as_slice()),
75        bearer_allowed: true,
76        pkce_methods_allowed: &pkce_methods_allowed,
77        post_logout_redirect_uris: None,
78        require_pkce: false,
79        token_endpoint_auth_method: oauth_client::TokenEndpointAuthMethod::ClientSecretPost,
80    };
81    if oauth_client::OAuthClient::find_by_client_id_optional(&mut conn, "test-client-id-2")
82        .await?
83        .is_none()
84    {
85        let _client_2 = oauth_client::OAuthClient::insert(&mut conn, new_client_parms_2).await?;
86    }
87
88    let new_client_parms_3 = oauth_client::NewClientParams {
89        client_name: "Test Client 3",
90        application_type: oauth_client::ApplicationType::Web,
91        client_id: "test-client-id-3",
92        client_secret: Some(&secret), // "very-secret"
93        client_secret_expires_at: None,
94        redirect_uris: redirect_uris.as_slice(),
95        allowed_grant_types: &allowed_grant_types,
96        scopes: scopes.as_slice(),
97        allowed_origins: Some(allowed_origins.as_slice()),
98        bearer_allowed: true,
99        pkce_methods_allowed: &pkce_methods_allowed,
100        post_logout_redirect_uris: None,
101        require_pkce: false,
102        token_endpoint_auth_method: oauth_client::TokenEndpointAuthMethod::ClientSecretPost,
103    };
104    if oauth_client::OAuthClient::find_by_client_id_optional(&mut conn, "test-client-id-3")
105        .await?
106        .is_none()
107    {
108        let _client_3 = oauth_client::OAuthClient::insert(&mut conn, new_client_parms_3).await?;
109    }
110
111    Ok(SeedOAuthClientsResult {
112        client_db_id: client.id,
113    })
114}