greentic_ext_runtime/
oauth.rs1#![allow(dead_code)]
8
9use serde::{Deserialize, Serialize};
10use url::Url;
11
12#[derive(Clone, Debug, Default)]
14pub struct OAuthBrokerConfig {
15 pub http_base_url: String,
16 pub env: String,
17 pub tenant: String,
18 pub team: Option<String>,
19 pub shared_secret: Option<String>,
20}
21
22#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
23pub struct ResourceTokenRequest {
24 pub http_base_url: String,
25 pub env: String,
26 pub tenant: String,
27 pub team: Option<String>,
28 pub resource_id: String,
29 pub scopes: Vec<String>,
30}
31
32#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
33pub struct ResourceTokenResponse {
34 pub access_token: String,
35 pub expires_at: u64,
36}
37
38pub fn request_resource_token_blocking(
43 client: &reqwest::blocking::Client,
44 request: &ResourceTokenRequest,
45 shared_secret: Option<&str>,
46) -> anyhow::Result<ResourceTokenResponse> {
47 let base = Url::parse(&request.http_base_url)?;
48 let is_local = base.host_str() == Some("127.0.0.1") || base.host_str() == Some("localhost");
49 anyhow::ensure!(
50 base.scheme() == "https" || is_local,
51 "oauth broker http_base_url must be https"
52 );
53 anyhow::ensure!(
54 base.query().is_none(),
55 "oauth broker http_base_url must not carry a query"
56 );
57 let url = base.join("resource-token")?;
58 let mut rb = client.post(url).json(request);
59 if let Some(secret) = shared_secret {
60 rb = rb.bearer_auth(secret);
61 }
62 let response = rb.send()?.error_for_status()?;
63 Ok(response.json()?)
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69
70 #[test]
71 fn resource_token_request_serializes_expected_shape() {
72 let req = ResourceTokenRequest {
73 http_base_url: "https://oauth.example/".into(),
74 env: "dev".into(),
75 tenant: "acme".into(),
76 team: None,
77 resource_id: "hubspot".into(),
78 scopes: vec!["crm.objects.contacts.read".into()],
79 };
80 let v: serde_json::Value = serde_json::to_value(&req).unwrap();
81 assert_eq!(v["resource_id"], "hubspot");
82 assert_eq!(v["tenant"], "acme");
83 assert_eq!(v["scopes"][0], "crm.objects.contacts.read");
84 }
85
86 #[test]
87 fn resource_token_response_deserializes() {
88 let r: ResourceTokenResponse =
89 serde_json::from_str(r#"{"access_token":"tok","expires_at":123}"#).unwrap();
90 assert_eq!(r.access_token, "tok");
91 assert_eq!(r.expires_at, 123);
92 }
93}