Skip to content

Commit 3ce11cb

Browse files
committed
refactor: improve form content creation for OAuth requests using FormUrlEncoded helper
1 parent e039ae1 commit 3ce11cb

4 files changed

Lines changed: 68 additions & 100 deletions

File tree

MCPify/Core/Auth/DeviceCode/DeviceCodeAuthentication.cs

Lines changed: 17 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -76,19 +76,11 @@ public async Task ApplyAsync(HttpRequestMessage request, CancellationToken cance
7676

7777
private async Task<TokenData> PerformDeviceLoginAsync(CancellationToken cancellationToken)
7878
{
79-
var codeRequestForm = new Dictionary<string, string>
80-
{
81-
{ "client_id", _clientId },
82-
{ "scope", _scope }
83-
};
84-
85-
// RFC 8707: Add resource parameter if configured
86-
if (!string.IsNullOrEmpty(_resourceUrl))
87-
{
88-
codeRequestForm["resource"] = _resourceUrl;
89-
}
90-
91-
var codeRequest = new FormUrlEncodedContent(codeRequestForm);
79+
var codeRequest = FormUrlEncoded.Create()
80+
.Add("client_id", _clientId)
81+
.Add("scope", _scope)
82+
.AddIfNotEmpty("resource", _resourceUrl) // RFC 8707
83+
.ToContent();
9284

9385
var codeResponse = await _httpClient.PostAsync(_deviceCodeEndpoint, codeRequest, cancellationToken);
9486
codeResponse.EnsureSuccessStatusCode();
@@ -105,20 +97,12 @@ private async Task<TokenData> PerformDeviceLoginAsync(CancellationToken cancella
10597
{
10698
await Task.Delay(interval * 1000, cancellationToken);
10799

108-
var tokenRequestForm = new Dictionary<string, string>
109-
{
110-
{ "grant_type", "urn:ietf:params:oauth:grant-type:device_code" },
111-
{ "client_id", _clientId },
112-
{ "device_code", codeData.device_code }
113-
};
114-
115-
// RFC 8707: Add resource parameter if configured
116-
if (!string.IsNullOrEmpty(_resourceUrl))
117-
{
118-
tokenRequestForm["resource"] = _resourceUrl;
119-
}
120-
121-
var tokenRequest = new FormUrlEncodedContent(tokenRequestForm);
100+
var tokenRequest = FormUrlEncoded.Create()
101+
.Add("grant_type", "urn:ietf:params:oauth:grant-type:device_code")
102+
.Add("client_id", _clientId)
103+
.Add("device_code", codeData.device_code)
104+
.AddIfNotEmpty("resource", _resourceUrl) // RFC 8707
105+
.ToContent();
122106

123107
var tokenResponse = await _httpClient.PostAsync(_tokenEndpoint, tokenRequest, cancellationToken);
124108

@@ -144,20 +128,12 @@ private async Task<TokenData> PerformDeviceLoginAsync(CancellationToken cancella
144128

145129
private async Task<TokenData> RefreshTokenAsync(string refreshToken, CancellationToken cancellationToken)
146130
{
147-
var form = new Dictionary<string, string>
148-
{
149-
{ "grant_type", "refresh_token" },
150-
{ "client_id", _clientId },
151-
{ "refresh_token", refreshToken }
152-
};
153-
154-
// RFC 8707: Add resource parameter if configured
155-
if (!string.IsNullOrEmpty(_resourceUrl))
156-
{
157-
form["resource"] = _resourceUrl;
158-
}
159-
160-
var content = new FormUrlEncodedContent(form);
131+
var content = FormUrlEncoded.Create()
132+
.Add("grant_type", "refresh_token")
133+
.Add("client_id", _clientId)
134+
.Add("refresh_token", refreshToken)
135+
.AddIfNotEmpty("resource", _resourceUrl) // RFC 8707
136+
.ToContent();
161137

162138
var response = await _httpClient.PostAsync(_tokenEndpoint, content, cancellationToken);
163139
response.EnsureSuccessStatusCode();

MCPify/Core/Auth/FormUrlEncoded.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
namespace MCPify.Core.Auth;
2+
3+
/// <summary>
4+
/// Fluent helper for creating application/x-www-form-urlencoded POST content.
5+
/// </summary>
6+
internal class FormUrlEncoded
7+
{
8+
private readonly List<KeyValuePair<string, string>> _params = new();
9+
10+
public static FormUrlEncoded Create() => new();
11+
12+
public FormUrlEncoded Add(string key, string value)
13+
{
14+
_params.Add(new(key, value));
15+
return this;
16+
}
17+
18+
public FormUrlEncoded AddIfNotEmpty(string key, string? value)
19+
{
20+
if (!string.IsNullOrEmpty(value))
21+
{
22+
_params.Add(new(key, value));
23+
}
24+
return this;
25+
}
26+
27+
public FormUrlEncodedContent ToContent() => new(_params);
28+
}

MCPify/Core/Auth/OAuth/ClientCredentialsAuthentication.cs

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -57,21 +57,13 @@ public async Task ApplyAsync(HttpRequestMessage request, CancellationToken cance
5757

5858
private async Task<TokenData> RequestTokenAsync(CancellationToken cancellationToken)
5959
{
60-
var form = new Dictionary<string, string>
61-
{
62-
{ "grant_type", "client_credentials" },
63-
{ "client_id", _clientId },
64-
{ "client_secret", _clientSecret },
65-
{ "scope", _scope }
66-
};
67-
68-
// RFC 8707: Add resource parameter if configured
69-
if (!string.IsNullOrEmpty(_resourceUrl))
70-
{
71-
form["resource"] = _resourceUrl;
72-
}
73-
74-
var content = new FormUrlEncodedContent(form);
60+
var content = FormUrlEncoded.Create()
61+
.Add("grant_type", "client_credentials")
62+
.Add("client_id", _clientId)
63+
.Add("client_secret", _clientSecret)
64+
.Add("scope", _scope)
65+
.AddIfNotEmpty("resource", _resourceUrl) // RFC 8707
66+
.ToContent();
7567
var response = await _httpClient.PostAsync(_tokenEndpoint, content, cancellationToken);
7668
response.EnsureSuccessStatusCode();
7769

MCPify/Core/Auth/OAuth/OAuthAuthorizationCodeAuthentication.cs

Lines changed: 16 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -229,31 +229,15 @@ public virtual async Task<TokenData> HandleAuthorizationCallbackAsync(string cod
229229

230230
private async Task<TokenData> ExchangeCodeForTokenAsync(string code, string redirectUri, string? codeVerifier, CancellationToken cancellationToken)
231231
{
232-
var form = new Dictionary<string, string>
233-
{
234-
{ "grant_type", "authorization_code" },
235-
{ "client_id", _clientId },
236-
{ "code", code },
237-
{ "redirect_uri", redirectUri }
238-
};
239-
240-
if (!string.IsNullOrEmpty(codeVerifier))
241-
{
242-
form["code_verifier"] = codeVerifier;
243-
}
244-
245-
if (!string.IsNullOrEmpty(_clientSecret))
246-
{
247-
form["client_secret"] = _clientSecret;
248-
}
249-
250-
// RFC 8707: Add resource parameter if configured
251-
if (!string.IsNullOrEmpty(_resourceUrl))
252-
{
253-
form["resource"] = _resourceUrl;
254-
}
255-
256-
var content = new FormUrlEncodedContent(form);
232+
var content = FormUrlEncoded.Create()
233+
.Add("grant_type", "authorization_code")
234+
.Add("client_id", _clientId)
235+
.Add("code", code)
236+
.Add("redirect_uri", redirectUri)
237+
.AddIfNotEmpty("code_verifier", codeVerifier)
238+
.AddIfNotEmpty("client_secret", _clientSecret)
239+
.AddIfNotEmpty("resource", _resourceUrl) // RFC 8707
240+
.ToContent();
257241

258242
var response = await _httpClient.PostAsync(_tokenEndpoint, content, cancellationToken);
259243
response.EnsureSuccessStatusCode();
@@ -277,25 +261,13 @@ private async Task<TokenData> ExchangeCodeForTokenAsync(string code, string redi
277261

278262
private async Task<TokenData> RefreshTokenAsync(string refreshToken, string sessionId, CancellationToken cancellationToken)
279263
{
280-
var form = new Dictionary<string, string>
281-
{
282-
{ "grant_type", "refresh_token" },
283-
{ "client_id", _clientId },
284-
{ "refresh_token", refreshToken }
285-
};
286-
287-
if (!string.IsNullOrEmpty(_clientSecret))
288-
{
289-
form["client_secret"] = _clientSecret;
290-
}
291-
292-
// RFC 8707: Add resource parameter if configured
293-
if (!string.IsNullOrEmpty(_resourceUrl))
294-
{
295-
form["resource"] = _resourceUrl;
296-
}
297-
298-
var content = new FormUrlEncodedContent(form);
264+
var content = FormUrlEncoded.Create()
265+
.Add("grant_type", "refresh_token")
266+
.Add("client_id", _clientId)
267+
.Add("refresh_token", refreshToken)
268+
.AddIfNotEmpty("client_secret", _clientSecret)
269+
.AddIfNotEmpty("resource", _resourceUrl) // RFC 8707
270+
.ToContent();
299271

300272
var response = await _httpClient.PostAsync(_tokenEndpoint, content, cancellationToken);
301273
response.EnsureSuccessStatusCode();

0 commit comments

Comments
 (0)