Skip to content

Commit 4ac99c3

Browse files
committed
2 parents 842e4a5 + 44f9975 commit 4ac99c3

File tree

5 files changed

+56
-18
lines changed

5 files changed

+56
-18
lines changed

README.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,9 @@ Details of services available need to be applied to the Umbraco web application'
9898
"ApiKey": "",
9999
"ApiKeyProvision": {
100100
"Method": "HttpHeader|QueryString",
101-
"Key": ""
101+
"Key": "",
102+
"AdditionalParameters": {
103+
}
102104
},
103105
"ClientId": "",
104106
"ClientSecret": "",
@@ -169,7 +171,7 @@ Used, along with `IdentityHost` to construct a URL that the user is redirected t
169171

170172
###### CanManuallyProvideToken
171173

172-
Specifies whether the service supports generating of tokens via the provider's developer portal such that an administrator can manually add one via the backoffice.
174+
Specifies whether the service supports generating of tokens via the provider's developer portal such that an administrator can manually add one via the backoffice.
173175

174176
###### CanManuallyProvideApiKey
175177

@@ -181,7 +183,7 @@ Specifies whether the access token can be exchanged with a long lived one.
181183

182184
###### ExchangeTokenProvision
183185

184-
Provides a strongly typed configuration for a setup that allows exchanging an access token.
186+
Provides a strongly typed configuration for a setup that allows exchanging an access token.
185187

186188
This setting is only utilized when `CanExchangeToken` is set to `true`.
187189

@@ -192,7 +194,7 @@ The configuration of exchange tokens includes:
192194
- `TokenGrantType`
193195
- `RequestRefreshTokenPath`
194196
- `RefreshTokenGrantType`
195-
- `ExchangeTokenWhenExpiresWithin`
197+
- `ExchangeTokenWhenExpiresWithin`
196198

197199
###### AuthorizationUrlRequiresRedirectUrl
198200

@@ -231,6 +233,8 @@ Specifies the key a service with `AuthenticationMethod` set to `ApiKey` will use
231233

232234
For `ApiKey` authentication methods, options for passing the API key need to be set, by specifying a method: `HttpHeader` or `QueryString` and the name for the key holding the value.
233235

236+
You can also provide additional parameters that will be included in the querystring or headers via the `AdditionalParameters` dictionary.
237+
234238
###### ClientId *
235239

236240
This value will be retrieved from the registered service app.

src/Umbraco.AuthorizedServices/Configuration/AuthorizedServiceSettings.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ public class ApiKeyProvision
8080

8181
public string Key { get; set; } = string.Empty;
8282

83+
public IDictionary<string, string> AdditionalParameters { get; set; } = new Dictionary<string, string>();
84+
8385
public override string ToString() => $"{Method} / {Key}";
8486
}
8587

src/Umbraco.AuthorizedServices/Services/Implement/AuthorizedRequestBuilder.cs

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,7 @@ public HttpRequestMessage CreateRequestMessageWithApiKey<TRequest>(
5252
throw new InvalidOperationException("Cannot create an HTTP request message for an API key request as no API key provision detail has been provided in configuration.");
5353
}
5454

55-
var requestUri = new Uri(serviceDetail.ApiHost + path);
56-
if (serviceDetail.ApiKeyProvision.Method == ApiKeyProvisionMethod.QueryString)
57-
{
58-
NameValueCollection queryStringParams = HttpUtility.ParseQueryString(requestUri.Query);
59-
requestUri = new Uri($"{requestUri}{(queryStringParams.Count > 0 ? "&" : "?")}{serviceDetail.ApiKeyProvision.Key}={apiKey}");
60-
}
55+
Uri requestUri = BuildRequestUriWithApiKey(serviceDetail.ApiHost, serviceDetail.ApiKeyProvision, path, apiKey);
6156

6257
HttpRequestMessage requestMessage = CreateRequestMessage(
6358
httpMethod,
@@ -66,13 +61,46 @@ public HttpRequestMessage CreateRequestMessageWithApiKey<TRequest>(
6661

6762
if (serviceDetail.ApiKeyProvision.Method == ApiKeyProvisionMethod.HttpHeader)
6863
{
69-
requestMessage.Headers.Add(serviceDetail.ApiKeyProvision.Key, apiKey);
64+
AddHeadersForRequestWithApiKeyProvisionedInHeader(requestMessage, serviceDetail.ApiKeyProvision, apiKey);
7065
}
7166

7267
AddCommonHeaders(requestMessage);
7368
return requestMessage;
7469
}
7570

71+
private static Uri BuildRequestUriWithApiKey(string apiHost, ApiKeyProvision apiKeyProvision, string path, string apiKey)
72+
{
73+
var requestUri = new Uri(apiHost + path);
74+
if (apiKeyProvision.Method != ApiKeyProvisionMethod.QueryString)
75+
{
76+
return requestUri;
77+
}
78+
79+
bool pathHasQuerystring = HttpUtility.ParseQueryString(requestUri.Query).Count > 0;
80+
var requestUrlWithQuerystring = new StringBuilder(requestUri.ToString());
81+
requestUrlWithQuerystring.Append(pathHasQuerystring ? "&" : "?");
82+
requestUrlWithQuerystring.AppendFormat("{0}={1}", apiKeyProvision.Key, apiKey);
83+
84+
foreach (KeyValuePair<string, string> additionalParameter in apiKeyProvision.AdditionalParameters)
85+
{
86+
requestUrlWithQuerystring.AppendFormat("&{0}={1}", additionalParameter.Key, additionalParameter.Value);
87+
}
88+
89+
requestUri = new Uri(requestUrlWithQuerystring.ToString());
90+
91+
return requestUri;
92+
}
93+
94+
private static void AddHeadersForRequestWithApiKeyProvisionedInHeader(HttpRequestMessage requestMessage, ApiKeyProvision apiKeyProvision, string apiKey)
95+
{
96+
requestMessage.Headers.Add(apiKeyProvision.Key, apiKey);
97+
98+
foreach (KeyValuePair<string, string> additionalParameter in apiKeyProvision.AdditionalParameters)
99+
{
100+
requestMessage.Headers.Add(additionalParameter.Key, additionalParameter.Value);
101+
}
102+
}
103+
76104
public HttpRequestMessage CreateRequestMessageWithOAuth1Token<TRequest>(ServiceDetail serviceDetail, string path, HttpMethod httpMethod, OAuth1Token oauth1Token, TRequest? requestContent) where TRequest : class
77105
{
78106
var url = serviceDetail.ApiHost + path;

tests/Umbraco.AuthorizedServices.Tests/Services/AuthorizedRequestBuilderTests.cs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public async Task CreateRequestMessageWithToken_ReturnsExpectedResult()
3737
}
3838

3939
[Test]
40-
public async Task CreateRequestMessageWithApiKey_WithKeyProvidedInQueryString_ReturnsExpectedResult()
40+
public async Task CreateRequestMessageWithApiKey_WithKeyAndAdditionalParametersProvidedInQueryString_ReturnsExpectedResult()
4141
{
4242
// Arrange
4343
var serviceDetail = new ServiceDetail
@@ -48,7 +48,8 @@ public async Task CreateRequestMessageWithApiKey_WithKeyProvidedInQueryString_Re
4848
ApiKeyProvision = new ApiKeyProvision
4949
{
5050
Key = "x-api-key",
51-
Method = ApiKeyProvisionMethod.QueryString
51+
Method = ApiKeyProvisionMethod.QueryString,
52+
AdditionalParameters = new Dictionary<string, string> { { "foo", "bar" }, { "baz", "buzz" } }
5253
}
5354
};
5455
const string Path = "/api/test";
@@ -59,15 +60,15 @@ public async Task CreateRequestMessageWithApiKey_WithKeyProvidedInQueryString_Re
5960
HttpRequestMessage result = sut.CreateRequestMessageWithApiKey(serviceDetail, Path, HttpMethod.Post, "abc", data);
6061

6162
// Assert
62-
var expectedUri = new Uri("https://service.url/api/test?x-api-key=abc");
63+
var expectedUri = new Uri("https://service.url/api/test?x-api-key=abc&foo=bar&baz=buzz");
6364
await AssertResult(result, expectedUri);
6465

6566
result.Headers.Count().Should().Be(2);
6667
AssertCommonHeaders(result);
6768
}
6869

6970
[Test]
70-
public async Task CreateRequestMessageWithApiKey_WithKeyProvidedInHttpHeader_ReturnsExpectedResult()
71+
public async Task CreateRequestMessageWithApiKey_WithKeyAndAdditionalParametersProvidedInHttpHeader_ReturnsExpectedResult()
7172
{
7273
// Arrange
7374
var serviceDetail = new ServiceDetail
@@ -78,7 +79,8 @@ public async Task CreateRequestMessageWithApiKey_WithKeyProvidedInHttpHeader_Ret
7879
ApiKeyProvision = new ApiKeyProvision
7980
{
8081
Key = "x-api-key",
81-
Method = ApiKeyProvisionMethod.HttpHeader
82+
Method = ApiKeyProvisionMethod.HttpHeader,
83+
AdditionalParameters = new Dictionary<string, string> { { "foo", "bar" }, { "baz", "buzz" } }
8284
}
8385
};
8486
const string Path = "/api/test";
@@ -92,8 +94,10 @@ public async Task CreateRequestMessageWithApiKey_WithKeyProvidedInHttpHeader_Ret
9294
var expectedUri = new Uri("https://service.url/api/test");
9395
await AssertResult(result, expectedUri);
9496

95-
result.Headers.Count().Should().Be(3);
97+
result.Headers.Count().Should().Be(5);
9698
result.Headers.Single(x => x.Key == "x-api-key").Value.First().Should().Be("abc");
99+
result.Headers.Single(x => x.Key == "foo").Value.First().Should().Be("bar");
100+
result.Headers.Single(x => x.Key == "baz").Value.First().Should().Be("buzz");
97101
AssertCommonHeaders(result);
98102
}
99103

version.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json",
3-
"version": "0.3.0",
3+
"version": "0.3.1",
44
"assemblyVersion": {
55
"precision": "build"
66
},

0 commit comments

Comments
 (0)