Skip to content

Commit 09cf1ce

Browse files
committed
feat: support client secret
1 parent 16949a0 commit 09cf1ce

File tree

8 files changed

+79
-1
lines changed

8 files changed

+79
-1
lines changed

src/commands/add.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ pub struct AddCommand {
66
pub nickname: Option<String>,
77
pub auth_url: String,
88
pub client_id: String,
9+
pub secret: Option<String>,
910
}
1011

1112
impl CommandHandler for AddCommand {
@@ -19,6 +20,7 @@ impl CommandHandler for AddCommand {
1920
auth_url: self.auth_url.clone(),
2021
client_id: self.client_id.clone(),
2122
refresh_token: None,
23+
secret: self.secret.clone(),
2224
};
2325

2426
context
@@ -66,6 +68,7 @@ mod tests {
6668
nickname: Some("test_client".to_string()),
6769
auth_url: "https://example.com".to_string(),
6870
client_id: "client123".to_string(),
71+
secret: None,
6972
};
7073

7174
let mock_credentials_provider = MockCredentialsProvider;
@@ -95,6 +98,7 @@ mod tests {
9598
nickname: None,
9699
auth_url: "https://example.com".to_string(),
97100
client_id: "client123".to_string(),
101+
secret: Some("secret".to_string()),
98102
};
99103

100104
let mock_credentials_provider = MockCredentialsProvider;
@@ -108,5 +112,12 @@ mod tests {
108112
let result = add_command.execute(context).await;
109113
assert!(result.is_ok());
110114
assert!(config.clients.contains_key("client123"));
115+
assert_eq!(
116+
config
117+
.clients
118+
.get("client123")
119+
.and_then(|client| client.secret.clone()),
120+
Some("secret".to_string())
121+
);
111122
}
112123
}

src/commands/delete.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ mod tests {
5555
auth_url: "https://auth1.com".to_string(),
5656
client_id: "client_id_1".to_string(),
5757
refresh_token: Some("token1".to_string()),
58+
secret: None,
5859
},
5960
),
6061
(
@@ -63,6 +64,7 @@ mod tests {
6364
auth_url: "https://auth2.com".to_string(),
6465
client_id: "client_id_2".to_string(),
6566
refresh_token: None,
67+
secret: None,
6668
},
6769
),
6870
]
@@ -80,6 +82,7 @@ mod tests {
8082
auth_url: "https://auth2.com".to_string(),
8183
client_id: "client_id_2".to_string(),
8284
refresh_token: None,
85+
secret: None,
8386
},
8487
)]
8588
.into_iter()
@@ -102,6 +105,7 @@ mod tests {
102105
auth_url: "https://example.com".to_string(),
103106
client_id: "client123".to_string(),
104107
refresh_token: Some("refresh123".to_string()),
108+
secret: None,
105109
},
106110
);
107111
clients

src/commands/get.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ mod tests {
130130
auth_url: server.url(""),
131131
client_id: "test-client".into(),
132132
refresh_token: Some("existing_refresh_token".into()),
133+
secret: None,
133134
},
134135
);
135136

@@ -179,6 +180,7 @@ mod tests {
179180
auth_url: server.url(""),
180181
client_id: "test-client".into(),
181182
refresh_token: Some("existing_refresh_token".into()),
183+
secret: None,
182184
},
183185
);
184186

@@ -221,6 +223,7 @@ mod tests {
221223
auth_url: server.url(""),
222224
client_id: "test-client".into(),
223225
refresh_token: Some("invalid_refresh_token".into()),
226+
secret: None,
224227
},
225228
);
226229

@@ -272,6 +275,7 @@ mod tests {
272275
auth_url: server.url(""),
273276
client_id: "test-client".into(),
274277
refresh_token: Some("existing_refresh_token".into()),
278+
secret: None,
275279
},
276280
);
277281

@@ -321,6 +325,7 @@ mod tests {
321325
auth_url: server.url(""),
322326
client_id: "test-client".into(),
323327
refresh_token: Some("existing_refresh_token".into()),
328+
secret: None,
324329
},
325330
);
326331

src/commands/list.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ mod tests {
4949
auth_url: "https://auth1.com".to_string(),
5050
client_id: "client_id_1".to_string(),
5151
refresh_token: Some("token1".to_string()),
52+
secret: None,
5253
},
5354
),
5455
(
@@ -57,6 +58,7 @@ mod tests {
5758
auth_url: "https://auth2.com".to_string(),
5859
client_id: "client_id_2".to_string(),
5960
refresh_token: None,
61+
secret: None,
6062
},
6163
),
6264
]
@@ -121,6 +123,7 @@ mod tests {
121123
auth_url: "https://auth1.com".to_string(),
122124
client_id: "id1".to_string(),
123125
refresh_token: Some("token1".to_string()),
126+
secret: None,
124127
},
125128
);
126129
clients.insert(
@@ -129,6 +132,7 @@ mod tests {
129132
auth_url: "https://auth2.com".to_string(),
130133
client_id: "id2".to_string(),
131134
refresh_token: None,
135+
secret: None,
132136
},
133137
);
134138
clients

src/commands/logout.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ mod tests {
5656
auth_url: "https://auth1.com".to_string(),
5757
client_id: "client_id_1".to_string(),
5858
refresh_token: Some("token1".to_string()),
59+
secret: None,
5960
},
6061
),
6162
(
@@ -64,6 +65,7 @@ mod tests {
6465
auth_url: "https://auth2.com".to_string(),
6566
client_id: "client_id_2".to_string(),
6667
refresh_token: None,
68+
secret: None,
6769
},
6870
),
6971
]
@@ -84,6 +86,7 @@ mod tests {
8486
auth_url: "https://auth1.com".to_string(),
8587
client_id: "client_id_1".to_string(),
8688
refresh_token: None,
89+
secret: None,
8790
},
8891
),
8992
(
@@ -92,6 +95,7 @@ mod tests {
9295
auth_url: "https://auth2.com".to_string(),
9396
client_id: "client_id_2".to_string(),
9497
refresh_token: None,
98+
secret: None,
9599
},
96100
),
97101
]
@@ -115,6 +119,7 @@ mod tests {
115119
auth_url: "https://example.com".to_string(),
116120
client_id: "client123".to_string(),
117121
refresh_token: Some("refresh123".to_string()),
122+
secret: None,
118123
},
119124
);
120125
clients

src/main.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ enum Command {
4545
auth_url: String,
4646
#[arg(short, long)]
4747
client_id: String,
48+
#[arg(short, long)]
49+
secret: Option<String>,
4850
},
4951
/// Remove a saved client.
5052
Delete { nickname: String },
@@ -93,11 +95,13 @@ async fn run_command(
9395
nickname,
9496
auth_url,
9597
client_id,
98+
secret,
9699
} => {
97100
let command = AddCommand {
98101
nickname,
99102
auth_url,
100103
client_id,
104+
secret,
101105
};
102106
command.execute(context).await
103107
}
@@ -150,6 +154,7 @@ mod tests {
150154
client_id: "test_id".to_string(),
151155
auth_url: "https://example.com/auth".to_string(),
152156
refresh_token: None,
157+
secret: None,
153158
},
154159
);
155160
config
@@ -167,6 +172,7 @@ mod tests {
167172
nickname: Some("test".to_string()),
168173
auth_url: "https://example.com/auth".to_string(),
169174
client_id: "test_client".to_string(),
175+
secret: None,
170176
},
171177
};
172178

src/oauth.rs

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,13 +105,17 @@ impl TokenManager {
105105
fn add_optional_fields<'a>(
106106
&self,
107107
form: &mut Vec<(&str, &'a str)>,
108-
_auth: &'a AuthConfig,
108+
auth: &'a AuthConfig,
109109
scopes: &[String],
110110
) {
111111
if !scopes.is_empty() {
112112
let scopes_str: &'a str = Box::leak(scopes.join(" ").into_boxed_str());
113113
form.push(("scope", scopes_str));
114114
}
115+
116+
if let Some(secret) = &auth.secret {
117+
form.push(("client_secret", secret));
118+
}
115119
}
116120
}
117121

@@ -147,6 +151,41 @@ mod tests {
147151
auth_url: format!("{}/realms/master", server.url()),
148152
client_id: "test".to_string(),
149153
refresh_token: None,
154+
secret: None,
155+
};
156+
157+
let token_manager = TokenManager::new();
158+
let credentials_provider = MockCredentialsProvider;
159+
let scopes = vec!["openid".to_string(), "profile".to_string()];
160+
161+
let token = token_manager
162+
.get_or_refresh_token(&mut auth, false, &scopes, &credentials_provider)
163+
.await
164+
.unwrap();
165+
166+
assert_eq!(token, "token");
167+
mock.assert_async().await;
168+
}
169+
170+
#[tokio::test]
171+
async fn ensure_secret() {
172+
let mock_response = r#"{"access_token": "token", "refresh_token": "refresh"}"#;
173+
let mut server = Server::new_async().await;
174+
175+
let mock = server
176+
.mock("POST", "/realms/master/protocol/openid-connect/token")
177+
.with_status(200)
178+
.match_body(Regex("client_secret=secret".into()))
179+
.with_header("content-type", "application/json")
180+
.with_body(mock_response)
181+
.create_async()
182+
.await;
183+
184+
let mut auth = AuthConfig {
185+
auth_url: format!("{}/realms/master", server.url()),
186+
client_id: "test".to_string(),
187+
refresh_token: None,
188+
secret: Some("secret".to_string()),
150189
};
151190

152191
let token_manager = TokenManager::new();

src/types.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ pub struct AuthConfig {
66
pub auth_url: String,
77
pub client_id: String,
88
pub refresh_token: Option<String>,
9+
pub secret: Option<String>,
910
}
1011

1112
#[derive(Debug, Serialize, Deserialize, Default, PartialEq)]
@@ -57,6 +58,7 @@ mod tests {
5758
auth_url: "https://example.com".to_string(),
5859
client_id: "client123".to_string(),
5960
refresh_token: Some("refresh123".to_string()),
61+
secret: None,
6062
};
6163

6264
let serialized = serde_json::to_string(&auth_config).unwrap();
@@ -71,6 +73,7 @@ mod tests {
7173
auth_url: "https://example.com".to_string(),
7274
client_id: "client123".to_string(),
7375
refresh_token: None,
76+
secret: None,
7477
};
7578

7679
let serialized = serde_json::to_string(&auth_config).unwrap();
@@ -94,6 +97,7 @@ mod tests {
9497
auth_url: "https://example.com".to_string(),
9598
client_id: "client123".to_string(),
9699
refresh_token: Some("refresh123".to_string()),
100+
secret: None,
97101
},
98102
);
99103

0 commit comments

Comments
 (0)