1use url::Url;
2
3use crate::KontextAuthSession;
4use crate::KontextDevClient;
5use crate::KontextDevConfig;
6use crate::KontextDevError;
7use crate::TokenExchangeToken;
8
9#[derive(Clone, Debug)]
10pub struct KontextOAuthProviderConfig {
11 pub config: KontextDevConfig,
12}
13
14#[derive(Clone, Debug)]
15pub struct ParsedOAuthCallback {
16 pub code: Option<String>,
17 pub state: Option<String>,
18 pub error: Option<String>,
19 pub error_description: Option<String>,
20}
21
22#[derive(Clone, Debug)]
23pub struct TokenExchangeConfig {
24 pub config: KontextDevConfig,
25 pub subject_token: String,
26 pub resource: String,
27}
28
29#[derive(Clone, Debug)]
30pub struct KontextOAuthProvider {
31 client: KontextDevClient,
32}
33
34impl KontextOAuthProvider {
35 pub fn new(config: KontextOAuthProviderConfig) -> Self {
36 Self {
37 client: KontextDevClient::new(config.config),
38 }
39 }
40
41 pub fn client(&self) -> &KontextDevClient {
42 &self.client
43 }
44
45 pub async fn authenticate(&self) -> Result<KontextAuthSession, KontextDevError> {
46 self.client.authenticate_mcp().await
47 }
48
49 pub async fn create_connect_session(
50 &self,
51 gateway_access_token: &str,
52 ) -> Result<crate::ConnectSession, KontextDevError> {
53 self.client
54 .create_connect_session(gateway_access_token)
55 .await
56 }
57
58 pub async fn create_integration_connect_url(
59 &self,
60 gateway_access_token: &str,
61 ) -> Result<String, KontextDevError> {
62 self.client
63 .create_integration_connect_url(gateway_access_token)
64 .await
65 }
66}
67
68pub fn parse_oauth_callback(url: &str) -> Result<ParsedOAuthCallback, KontextDevError> {
69 let parsed = Url::parse(url).map_err(|source| KontextDevError::InvalidUrl {
70 url: url.to_string(),
71 source,
72 })?;
73
74 let mut callback = ParsedOAuthCallback {
75 code: None,
76 state: None,
77 error: None,
78 error_description: None,
79 };
80
81 for (key, value) in parsed.query_pairs() {
82 match key.as_ref() {
83 "code" => callback.code = Some(value.to_string()),
84 "state" => callback.state = Some(value.to_string()),
85 "error" => callback.error = Some(value.to_string()),
86 "error_description" => callback.error_description = Some(value.to_string()),
87 _ => {}
88 }
89 }
90
91 Ok(callback)
92}
93
94pub async fn exchange_token(
95 config: TokenExchangeConfig,
96) -> Result<TokenExchangeToken, KontextDevError> {
97 let client = KontextDevClient::new(config.config);
98 client
99 .exchange_for_resource(&config.subject_token, &config.resource, None)
100 .await
101}