Skip to content

Commit

Permalink
- adds an API key authentication provider
Browse files Browse the repository at this point in the history
  • Loading branch information
baywet committed Oct 18, 2022
1 parent 9b53254 commit 74b202f
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 2 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

## [1.0.0-preview.15] - 2022-10-18

### Added

- Adds an API key authentication provider.

## [1.0.0-preview.14] - 2022-10-17

### Changed
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using System;
using System.Threading.Tasks;
using Microsoft.Kiota.Abstractions.Authentication;
using Xunit;

namespace Microsoft.Kiota.Abstractions.Tests;

public class ApiKeyAuthenticationProviderTests {
[Fact]
public void DefensiveProgramming() {
Assert.Throws<ArgumentNullException>(() => new ApiKeyAuthenticationProvider(null, "param", ApiKeyAuthenticationProvider.KeyLocation.Header));
Assert.Throws<ArgumentNullException>(() => new ApiKeyAuthenticationProvider("key", null, ApiKeyAuthenticationProvider.KeyLocation.Header));

var value = new ApiKeyAuthenticationProvider("key", "param", ApiKeyAuthenticationProvider.KeyLocation.Header);
Assert.Throws<ArgumentNullException>(() => value.AuthenticateRequestAsync(null).GetAwaiter().GetResult());
}
[Fact]
public async Task AddsInHeader() {
var value = new ApiKeyAuthenticationProvider("key", "param", ApiKeyAuthenticationProvider.KeyLocation.Header);
var request = new RequestInformation {
UrlTemplate = "https://localhost{?param1}",
};
await value.AuthenticateRequestAsync(request);
Assert.False(request.URI.ToString().EndsWith("param=key", StringComparison.OrdinalIgnoreCase));
Assert.Contains("param", request.Headers.Keys);
}
[Fact]
public async Task AddsInQueryParameters() {
var value = new ApiKeyAuthenticationProvider("key", "param", ApiKeyAuthenticationProvider.KeyLocation.QueryParameter);
var request = new RequestInformation {
UrlTemplate = "https://localhost{?param1}",
};
await value.AuthenticateRequestAsync(request);
Assert.EndsWith("?param=key", request.URI.ToString());
Assert.DoesNotContain("param", request.Headers.Keys);
}
[Fact]
public async Task AddsInQueryParametersWithOtherParameters() {
var value = new ApiKeyAuthenticationProvider("key", "param", ApiKeyAuthenticationProvider.KeyLocation.QueryParameter);
var request = new RequestInformation {
UrlTemplate = "https://localhost{?param1}",
};
request.QueryParameters.Add("param1", "value1");
await value.AuthenticateRequestAsync(request);
Assert.EndsWith("&param=key", request.URI.ToString());
Assert.DoesNotContain("param", request.Headers.Keys);
}
}
4 changes: 2 additions & 2 deletions src/Microsoft.Kiota.Abstractions.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<Deterministic>true</Deterministic>
<VersionPrefix>1.0.0</VersionPrefix>
<VersionSuffix>preview.14</VersionSuffix>
<VersionSuffix>preview.15</VersionSuffix>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<SignAssembly>false</SignAssembly>
<DelaySign>false</DelaySign>
Expand All @@ -23,7 +23,7 @@
<!-- Enable this line once we go live to prevent breaking changes -->
<!-- <PackageValidationBaselineVersion>1.0.0</PackageValidationBaselineVersion> -->
<PackageReleaseNotes>
- Changes the ResponeHandler parameter in IRequestAdapter to be a RequestOption
- Adds an API key authentication provider
</PackageReleaseNotes>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
Expand Down
88 changes: 88 additions & 0 deletions src/authentication/ApiKeyAuthenticationProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// ------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information.
// ------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace Microsoft.Kiota.Abstractions.Authentication;

/// <summary>
/// This authentication provider authenticates requests using an API key.
/// </summary>
public class ApiKeyAuthenticationProvider : IAuthenticationProvider
{
private readonly string ApiKey;
private readonly string ParameterName;
private readonly KeyLocation KeyLoc;
private readonly AllowedHostsValidator AllowedHostsValidator;

/// <summary>
/// Instantiates a new <see cref="ApiKeyAuthenticationProvider"/> using the provided parameters.
/// </summary>
/// <param name="apiKey">The API key to use for authentication.</param>
/// <param name="parameterName">The name of the query parameter or header to use for authentication.</param>
/// <param name="keyLocation">The location of the API key.</param>
/// <param name="allowedHosts">The hosts that are allowed to use the provided API key.</param>
public ApiKeyAuthenticationProvider(string apiKey, string parameterName, KeyLocation keyLocation, params string[] allowedHosts)
{
if (string.IsNullOrEmpty(apiKey))
throw new ArgumentNullException(nameof(apiKey));
if (string.IsNullOrEmpty(parameterName))
throw new ArgumentNullException(nameof(parameterName));
if (allowedHosts == null)
throw new ArgumentNullException(nameof(allowedHosts));
ApiKey = apiKey;
ParameterName = parameterName;
KeyLoc = keyLocation;
AllowedHostsValidator = new AllowedHostsValidator(allowedHosts);
}
private static ActivitySource _activitySource = new(typeof(RequestInformation).Namespace);
/// <inheritdoc />
public Task AuthenticateRequestAsync(RequestInformation request, Dictionary<string, object> additionalAuthenticationContext = null, CancellationToken cancellationToken = default) {
if (request == null)
throw new ArgumentNullException(nameof(request));
using var span = _activitySource?.StartActivity(nameof(AuthenticateRequestAsync));
if (!AllowedHostsValidator.IsUrlHostValid(request.URI)) {
span?.SetTag("com.microsoft.kiota.authentication.is_url_valid", false);
return Task.CompletedTask;
}

var uri = request.URI;
if(!uri.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase)) {
span?.SetTag("com.microsoft.kiota.authentication.is_url_valid", false);
throw new ArgumentException("Only https is supported");
}

switch(KeyLoc) {
case KeyLocation.QueryParameter:
var uriString = uri.OriginalString + (uri.Query != string.Empty ? "&" : "?") + $"{ParameterName}={ApiKey}";
request.URI = new Uri(uriString);
break;
case KeyLocation.Header:
request.Headers.Add(ParameterName, ApiKey);
break;
default:
throw new ArgumentOutOfRangeException(nameof(KeyLoc));
}
span?.SetTag("com.microsoft.kiota.authentication.is_url_valid", true);
return Task.CompletedTask;
}
/// <summary>
/// The location of the API key parameter.
/// </summary>
public enum KeyLocation {
/// <summary>
/// The API key is passed as a query parameter.
/// </summary>
QueryParameter,
/// <summary>
/// The API key is passed as a header.
/// </summary>
Header
}
}

0 comments on commit 74b202f

Please sign in to comment.