diff --git a/core/src/lib.rs b/core/src/lib.rs index f14fff6eecb1..6dac293a0655 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -88,6 +88,7 @@ pub mod oauth { pub mod confluence; pub mod github; pub mod google_drive; + pub mod intercom; pub mod notion; pub mod slack; pub mod utils; diff --git a/core/src/oauth/connection.rs b/core/src/oauth/connection.rs index fbfc15d9d190..5c2823540ca9 100644 --- a/core/src/oauth/connection.rs +++ b/core/src/oauth/connection.rs @@ -1,8 +1,8 @@ use crate::oauth::{ providers::{ confluence::ConfluenceConnectionProvider, github::GithubConnectionProvider, - google_drive::GoogleDriveConnectionProvider, notion::NotionConnectionProvider, - slack::SlackConnectionProvider, + google_drive::GoogleDriveConnectionProvider, intercom::IntercomConnectionProvider, + notion::NotionConnectionProvider, slack::SlackConnectionProvider, }, store::OAuthStore, }; @@ -143,7 +143,7 @@ pub fn provider(t: ConnectionProvider) -> Box { ConnectionProvider::Confluence => Box::new(ConfluenceConnectionProvider::new()), ConnectionProvider::Github => Box::new(GithubConnectionProvider::new()), ConnectionProvider::GoogleDrive => Box::new(GoogleDriveConnectionProvider::new()), - ConnectionProvider::Intercom => unimplemented!(), + ConnectionProvider::Intercom => Box::new(IntercomConnectionProvider::new()), ConnectionProvider::Notion => Box::new(NotionConnectionProvider::new()), ConnectionProvider::Slack => Box::new(SlackConnectionProvider::new()), } diff --git a/core/src/oauth/providers/intercom.rs b/core/src/oauth/providers/intercom.rs new file mode 100644 index 000000000000..5fdf05c2a341 --- /dev/null +++ b/core/src/oauth/providers/intercom.rs @@ -0,0 +1,82 @@ +use crate::oauth::{ + connection::{Connection, ConnectionProvider, FinalizeResult, Provider, RefreshResult}, + providers::utils::execute_request, +}; +use anyhow::{anyhow, Result}; +use async_trait::async_trait; +use lazy_static::lazy_static; +use serde_json::json; +use std::env; + +lazy_static! { + static ref OAUTH_INTERCOM_CLIENT_ID: String = env::var("OAUTH_INTERCOM_CLIENT_ID").unwrap(); + static ref OAUTH_INTERCOM_CLIENT_SECRET: String = + env::var("OAUTH_INTERCOM_CLIENT_SECRET").unwrap(); +} + +pub struct IntercomConnectionProvider {} + +impl IntercomConnectionProvider { + pub fn new() -> Self { + IntercomConnectionProvider {} + } +} + +#[async_trait] +impl Provider for IntercomConnectionProvider { + fn id(&self) -> ConnectionProvider { + ConnectionProvider::Intercom + } + + async fn finalize( + &self, + _connection: &Connection, + code: &str, + redirect_uri: &str, + ) -> Result { + let body = json!({ + "grant_type": "authorization_code", + "client_id": *OAUTH_INTERCOM_CLIENT_ID, + "client_secret": *OAUTH_INTERCOM_CLIENT_SECRET, + "code": code, + "redirect_uri": redirect_uri, + }); + + let req = reqwest::Client::new() + .post("https://api.intercom.io/auth/eagle/token") + .header("Content-Type", "application/json") + .json(&body); + + let raw_json = execute_request(ConnectionProvider::Intercom, req).await?; + + let access_token = match raw_json["access_token"].as_str() { + Some(token) => token, + None => Err(anyhow!("Missing `access_token` in response from Intercom"))?, + }; + + Ok(FinalizeResult { + redirect_uri: redirect_uri.to_string(), + code: code.to_string(), + access_token: access_token.to_string(), + access_token_expiry: None, + refresh_token: None, + raw_json, + }) + } + + async fn refresh(&self, _connection: &Connection) -> Result { + Err(anyhow!("Intercom access tokens do not expire"))? + } + + fn scrubbed_raw_json(&self, raw_json: &serde_json::Value) -> Result { + let raw_json = match raw_json.clone() { + serde_json::Value::Object(mut map) => { + map.remove("access_token"); + map.remove("token"); + serde_json::Value::Object(map) + } + _ => Err(anyhow!("Invalid raw_json, not an object"))?, + }; + Ok(raw_json) + } +} diff --git a/front/lib/api/config.ts b/front/lib/api/config.ts index cbcfe8fac692..e68738ea5123 100644 --- a/front/lib/api/config.ts +++ b/front/lib/api/config.ts @@ -103,6 +103,9 @@ const config = { getOAuthSlackClientId: (): string => { return EnvironmentConfig.getEnvVariable("OAUTH_SLACK_CLIENT_ID"); }, + getOAuthIntercomClientId: (): string => { + return EnvironmentConfig.getEnvVariable("OAUTH_INTERCOM_CLIENT_ID"); + }, }; export default config; diff --git a/front/lib/api/oauth.ts b/front/lib/api/oauth.ts index 6b9a261f2140..96cb872b3020 100644 --- a/front/lib/api/oauth.ts +++ b/front/lib/api/oauth.ts @@ -177,11 +177,20 @@ const PROVIDER_STRATEGIES: Record< }, }, intercom: { - setupUri: () => { - throw new Error("Slack OAuth is not implemented"); + setupUri: (connection) => { + return ( + `https://app.intercom.com/oauth` + + `?client_id=${config.getOAuthIntercomClientId()}` + + `&state=${connection.connection_id}` + + `&redirect_uri=${encodeURIComponent(finalizeUriForProvider("intercom"))}` + ); + }, + codeFromQuery: (connection) => { + return getStringFromQuery(connection, "code"); + }, + connectionIdFromQuery: (connection) => { + return getStringFromQuery(connection, "state"); }, - codeFromQuery: () => null, - connectionIdFromQuery: () => null, }, microsoft: { setupUri: () => {