Skip to content

Commit

Permalink
Add Gong Oauth provider core (#6413)
Browse files Browse the repository at this point in the history
* Add Gong Oauth provider core

* Fix refresh_token

* Pass client id

* Remove return carriage

* ✨

---------

Co-authored-by: Flavien David <[email protected]>
  • Loading branch information
albandum and flvndvd authored Jul 22, 2024
1 parent 47f0f43 commit 9bc9e99
Show file tree
Hide file tree
Showing 3 changed files with 168 additions and 3 deletions.
1 change: 1 addition & 0 deletions core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ pub mod oauth {
pub mod providers {
pub mod confluence;
pub mod github;
pub mod gong;
pub mod google_drive;
pub mod intercom;
pub mod microsoft;
Expand Down
9 changes: 6 additions & 3 deletions core/src/oauth/connection.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use crate::oauth::{
providers::{
confluence::ConfluenceConnectionProvider, github::GithubConnectionProvider,
google_drive::GoogleDriveConnectionProvider, intercom::IntercomConnectionProvider,
microsoft::MicrosoftConnectionProvider, notion::NotionConnectionProvider,
slack::SlackConnectionProvider, utils::ProviderHttpRequestError,
gong::GongConnectionProvider, google_drive::GoogleDriveConnectionProvider,
intercom::IntercomConnectionProvider, microsoft::MicrosoftConnectionProvider,
notion::NotionConnectionProvider, slack::SlackConnectionProvider,
utils::ProviderHttpRequestError,
},
store::OAuthStore,
};
Expand Down Expand Up @@ -96,6 +97,7 @@ pub enum ConnectionProvider {
Notion,
Slack,
Microsoft,
Gong,
}

impl fmt::Display for ConnectionProvider {
Expand Down Expand Up @@ -181,6 +183,7 @@ pub fn provider(t: ConnectionProvider) -> Box<dyn Provider + Sync + Send> {
ConnectionProvider::Notion => Box::new(NotionConnectionProvider::new()),
ConnectionProvider::Slack => Box::new(SlackConnectionProvider::new()),
ConnectionProvider::Microsoft => Box::new(MicrosoftConnectionProvider::new()),
ConnectionProvider::Gong => Box::new(GongConnectionProvider::new()),
}
}

Expand Down
161 changes: 161 additions & 0 deletions core/src/oauth/providers/gong.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
use crate::{
oauth::{
connection::{
Connection, ConnectionProvider, FinalizeResult, Provider, ProviderError, RefreshResult,
PROVIDER_TIMEOUT_SECONDS,
},
providers::utils::execute_request,
},
utils,
};
use anyhow::{anyhow, Result};
use async_trait::async_trait;
use base64::{engine::general_purpose, Engine as _};
use lazy_static::lazy_static;
use std::env;

lazy_static! {
static ref OAUTH_GONG_CLIENT_ID: String = env::var("OAUTH_GONG_CLIENT_ID").unwrap();
static ref OAUTH_GONG_CLIENT_SECRET: String = env::var("OAUTH_GONG_CLIENT_SECRET").unwrap();
}

pub fn create_gong_basic_auth_token() -> String {
let credentials = format!("{}:{}", *OAUTH_GONG_CLIENT_ID, *OAUTH_GONG_CLIENT_SECRET);
format!("Basic {}", general_purpose::STANDARD.encode(credentials))
}

pub struct GongConnectionProvider {}

impl GongConnectionProvider {
pub fn new() -> Self {
GongConnectionProvider {}
}
}

#[async_trait]
impl Provider for GongConnectionProvider {
fn id(&self) -> ConnectionProvider {
ConnectionProvider::Gong
}

async fn finalize(
&self,
_connection: &Connection,
code: &str,
redirect_uri: &str,
) -> Result<FinalizeResult, ProviderError> {
let authorization = create_gong_basic_auth_token();

let params = [
("grant_type", "authorization_code"),
("code", &code),
("client_id", &*OAUTH_GONG_CLIENT_ID),
("redirect_uri", &redirect_uri),
];

let req = reqwest::Client::new()
.post("https://app.gong.io/oauth2/generate-customer-token")
.header("Content-Type", "application/json")
.header("Authorization", authorization)
.query(&params);

let raw_json = execute_request(ConnectionProvider::Gong, req)
.await
.map_err(|e| self.handle_provider_request_error(e))?;

let access_token = match raw_json["access_token"].as_str() {
Some(token) => token,
None => Err(anyhow!("Missing `access_token` in response from Gong"))?,
};

let expires_in = match raw_json.get("expires_in") {
Some(serde_json::Value::Number(n)) => match n.as_u64() {
Some(n) => n,
None => Err(anyhow!("Invalid `expires_in` in response from Gong"))?,
},
_ => Err(anyhow!("Missing `expires_in` in response from Gong"))?,
};

let refresh_token = match raw_json["refresh_token"].as_str() {
Some(token) => token,
None => Err(anyhow!("Missing `refresh_token` in response from Gong"))?,
};

Ok(FinalizeResult {
redirect_uri: redirect_uri.to_string(),
code: code.to_string(),
access_token: access_token.to_string(),
access_token_expiry: Some(
utils::now() + (expires_in - PROVIDER_TIMEOUT_SECONDS) * 1000,
),
refresh_token: Some(refresh_token.to_string()),
raw_json,
})
}

async fn refresh(&self, connection: &Connection) -> Result<RefreshResult, ProviderError> {
let refresh_token = match connection.unseal_refresh_token() {
Ok(Some(token)) => token,
Ok(None) => Err(anyhow!("Missing `refresh_token` in Gong connection"))?,
Err(e) => Err(e)?,
};

let authorization = create_gong_basic_auth_token();

let params = [
("grant_type", "authorization_code"),
("refresh_token", &refresh_token),
];

let req = reqwest::Client::new()
.post("https://app.gong.io/oauth2/generate-customer-token")
.header("Content-Type", "application/json")
.header("Authorization", authorization)
.query(&params);

let raw_json = execute_request(ConnectionProvider::Gong, req)
.await
.map_err(|e| self.handle_provider_request_error(e))?;

let access_token = match raw_json["access_token"].as_str() {
Some(token) => token,
None => Err(anyhow!("Missing `access_token` in response from Gong"))?,
};

let expires_in = match raw_json.get("expires_in") {
Some(serde_json::Value::Number(n)) => match n.as_u64() {
Some(n) => n,
None => Err(anyhow!("Invalid `expires_in` in response from Gong"))?,
},
_ => Err(anyhow!("Missing `expires_in` in response from Gong"))?,
};

let refresh_token = match raw_json["refresh_token"].as_str() {
Some(token) => token,
None => Err(anyhow!("Missing `refresh_token` in response from Gong"))?,
};

Ok(RefreshResult {
access_token: access_token.to_string(),
access_token_expiry: Some(
utils::now() + (expires_in - PROVIDER_TIMEOUT_SECONDS) * 1000,
),
refresh_token: Some(refresh_token.to_string()),
raw_json,
})
}

fn scrubbed_raw_json(&self, raw_json: &serde_json::Value) -> Result<serde_json::Value> {
let raw_json = match raw_json.clone() {
serde_json::Value::Object(mut map) => {
map.remove("access_token");
map.remove("refresh_token");
map.remove("expires_in");
serde_json::Value::Object(map)
}
_ => Err(anyhow!("Invalid raw_json, not an object"))?,
};

Ok(raw_json)
}
}

0 comments on commit 9bc9e99

Please sign in to comment.