diff --git a/backend/oauth_connect.json b/backend/oauth_connect.json index 888b9e0bca..2b1edfdf6f 100644 --- a/backend/oauth_connect.json +++ b/backend/oauth_connect.json @@ -75,6 +75,9 @@ "linkedin": { "auth_url": "https://www.linkedin.com/oauth/v2/authorization", "token_url": "https://www.linkedin.com/oauth/v2/accessToken", - "scopes": ["w_member_social"] + "scopes": ["w_member_social"], + "extra_params_callback": { + "redirect_uri": "https://app.windmill.dev/oauth/callback/linkedin" + } } } diff --git a/backend/windmill-api/src/oauth2.rs b/backend/windmill-api/src/oauth2.rs index 963478d83c..3eda008b59 100644 --- a/backend/windmill-api/src/oauth2.rs +++ b/backend/windmill-api/src/oauth2.rs @@ -76,6 +76,7 @@ pub struct ClientWithScopes { client: OClient, scopes: Vec, extra_params: Option>, + extra_params_callback: Option>, allowed_domains: Option>, userinfo_url: Option, } @@ -89,6 +90,7 @@ pub struct OAuthConfig { userinfo_url: Option, scopes: Option>, extra_params: Option>, + extra_params_callback: Option>, } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -158,6 +160,7 @@ pub async fn build_oauth_clients(base_url: &str) -> anyhow::Result { client: named_client.1, scopes: config.scopes.unwrap_or(vec![]), extra_params: config.extra_params, + extra_params_callback: config.extra_params_callback, allowed_domains: client_params.allowed_domains.clone(), userinfo_url: config.userinfo_url, }, @@ -188,6 +191,7 @@ pub async fn build_oauth_clients(base_url: &str) -> anyhow::Result { client: named_client.1, scopes: config.scopes.unwrap_or(vec![]), extra_params: config.extra_params, + extra_params_callback: config.extra_params_callback, allowed_domains: None, userinfo_url: None, }, @@ -204,6 +208,7 @@ pub async fn build_oauth_clients(base_url: &str) -> anyhow::Result { userinfo_url: None, scopes: None, extra_params: None, + extra_params_callback: None, }, v.clone(), false, @@ -599,15 +604,16 @@ async fn connect_callback( Extension(clients): Extension>, Extension(http_client): Extension, ) -> error::JsonResult { - let client = (&clients + let client_w_scopes = &clients .connects .get(&client_name) - .ok_or_else(|| error::Error::BadRequest("invalid client".to_string()))? - .client) - .to_owned(); + .ok_or_else(|| error::Error::BadRequest("invalid client".to_string()))?; + let client = client_w_scopes.client.to_owned(); + let extra_params = client_w_scopes.extra_params_callback.clone(); let token_response = - exchange_code::(callback, &cookies, client, &http_client).await?; + exchange_code::(callback, &cookies, client, &http_client, extra_params) + .await?; Ok(Json(token_response)) } @@ -627,7 +633,7 @@ async fn connect_slack_callback( .ok_or_else(|| error::Error::BadRequest("slack client not setup".to_string()))? .to_owned(); let token = - exchange_code::(callback, &cookies, client, &http_client).await?; + exchange_code::(callback, &cookies, client, &http_client, None).await?; let mut tx = user_db.begin(&authed).await?; @@ -832,7 +838,8 @@ async fn login_callback( .get(&client_name) .ok_or_else(|| error::Error::BadRequest("invalid client".to_string()))?; let client = client_w_config.client.to_owned(); - let token_res = exchange_code::(callback, &cookies, client, &http_client).await; + let token_res = + exchange_code::(callback, &cookies, client, &http_client, None).await; if let Ok(token) = token_res { let token = &token.access_token.to_string(); @@ -963,6 +970,7 @@ async fn exchange_code( cookies: &Cookies, client: OClient, http_client: &Client, + extra_params: Option>, ) -> error::Result { let csrf_state = cookies .get("csrf") @@ -972,8 +980,14 @@ async fn exchange_code( return Err(error::Error::BadRequest("csrf did not match".to_string())); } - client - .exchange_code(callback.code) + let mut token_url = client.exchange_code(callback.code); + + if let Some(extra_params) = extra_params { + for (key, value) in extra_params { + token_url = token_url.param(key, value) + } + } + token_url .with_client(http_client) .execute::() .await