Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,37 +10,49 @@ namespace Microsoft.Identity.Abstractions
/// <summary>
/// Options passed-in to call downstream web APIs.
/// </summary>
/// <example>
/// <format type="text/markdown">
/// <![CDATA[
/// Here is an example of a configuration of a downstream API that would retrieve
/// the user profile (it's illustrated with Microsoft Graph as this is a well-known API, but of course
/// to effectively call Microsoft graph, rather use Microsoft.Identity.Web.MicrosoftGraph)
///
/// ```json
/// "DownstreamApis": [
/// "MyProfile": {
/// "BaseUrl": "https://graph.microsoft.com/v1.0",
/// "RelativePath": "/me/profile",
/// "Scopes": [ "user.read"]
/// }
/// ]
/// ```
///
/// The following describes a downstream web API called on behalf of the application itself (application token)
/// and using the Pop protocol:
/// ```json
/// "DownstreamApis": [
/// "AllBooks": {
/// "BaseUrl": "https://mylibrary.com",
/// "RelativePath": "/books/all",
/// "RequestAppToken": true,
/// "ProtocolScheme": "Pop",
/// "Scopes": ["https://mylibrary.com/.default"]
/// }
/// ]
/// ```
/// ]]></format>
/// <example>
/// <format type="text/markdown">
/// <![CDATA[
/// Here is an example of a configuration of a downstream API that would retrieve
/// the user profile (it's illustrated with Microsoft Graph as this is a well-known API, but of course
/// to effectively call Microsoft graph, rather use Microsoft.Identity.Web.MicrosoftGraph)
///
/// ```json
/// "DownstreamApis": [
/// "MyProfile": {
/// "BaseUrl": "https://graph.microsoft.com/v1.0",
/// "RelativePath": "/me/profile",
/// "Scopes": [ "user.read"],
/// "ExtraHeaderParameters": {
/// "OData-Version": "4.0"
/// },
/// "ExtraQueryParameters": {
/// "filter": "displayName eq 'John'"
/// }
/// }
/// ]
/// ```
///
/// The following describes a downstream web API called on behalf of the application itself (application token)
/// and using the Pop protocol:
/// ```json
/// "DownstreamApis": [
/// "AllBooks": {
/// "BaseUrl": "https://mylibrary.com",
/// "RelativePath": "/books/all",
/// "RequestAppToken": true,
/// "ProtocolScheme": "Pop",
/// "Scopes": ["https://mylibrary.com/.default"],
/// "ExtraHeaderParameters": {
/// "X-API-Version": "v2"
/// },
/// "ExtraQueryParameters": {
/// "format": "json"
/// }
/// }
/// ]
/// ```
/// ]]></format>
/// </example>
public class DownstreamApiOptions : AuthorizationHeaderProviderOptions
{
Expand All @@ -51,17 +63,19 @@ public DownstreamApiOptions()
{
}

/// <summary>
/// Copy constructor.
/// </summary>
/// <param name="other"></param>
public DownstreamApiOptions(DownstreamApiOptions other) : base(other)
{
Scopes = other.Scopes;
Serializer = other.Serializer;
Deserializer = other.Deserializer;
AcceptHeader = other.AcceptHeader;
ContentType = other.ContentType;
/// <summary>
/// Copy constructor.
/// </summary>
/// <param name="other"></param>
public DownstreamApiOptions(DownstreamApiOptions other) : base(other)
{
Scopes = other.Scopes;
Serializer = other.Serializer;
Deserializer = other.Deserializer;
AcceptHeader = other.AcceptHeader;
ContentType = other.ContentType;
ExtraHeaderParameters = other.ExtraHeaderParameters;
ExtraQueryParameters = other.ExtraQueryParameters;
}

/// <summary>
Expand Down Expand Up @@ -120,10 +134,21 @@ protected override AuthorizationHeaderProviderOptions CloneInternal()
/// <default>application/json</default>
public string AcceptHeader { get; set; } = "application/json";

/// <summary>
/// Content type of the request body.
/// </summary>
/// <default>application/json</default>
public string ContentType { get; set; } = "application/json";
}
/// <summary>
/// Content type of the request body.
/// </summary>
/// <default>application/json</default>
public string ContentType { get; set; } = "application/json";

/// <summary>
/// Sets extra headers in the HTTP request to the downstream web API.
/// </summary>
public IDictionary<string, string>? ExtraHeaderParameters { get; set; }

/// <summary>
/// Sets query parameters for the query string in the HTTP request to the
/// downstream web API.
/// </summary>
public IDictionary<string, string>? ExtraQueryParameters { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
#nullable enable
Microsoft.Identity.Abstractions.CredentialDescription.Algorithm.get -> string?
Microsoft.Identity.Abstractions.CredentialDescription.Algorithm.set -> void
Microsoft.Identity.Abstractions.DownstreamApiOptions.ExtraHeaderParameters.get -> System.Collections.Generic.IDictionary<string!, string!>?
Microsoft.Identity.Abstractions.DownstreamApiOptions.ExtraHeaderParameters.set -> void
Microsoft.Identity.Abstractions.DownstreamApiOptions.ExtraQueryParameters.get -> System.Collections.Generic.IDictionary<string!, string!>?
Microsoft.Identity.Abstractions.DownstreamApiOptions.ExtraQueryParameters.set -> void
Microsoft.Identity.Abstractions.IAuthorizationHeaderProvider<TResult>
Microsoft.Identity.Abstractions.IAuthorizationHeaderProvider<TResult>.CreateAuthorizationHeaderAsync(Microsoft.Identity.Abstractions.DownstreamApiOptions! downstreamApiOptions, System.Security.Claims.ClaimsPrincipal? claimsPrincipal = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<TResult>!
Microsoft.Identity.Abstractions.MicrosoftEntraApplicationOptions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ Microsoft.Identity.Abstractions.CredentialDescription.Algorithm.get -> string?
Microsoft.Identity.Abstractions.CredentialDescription.Algorithm.set -> void
Microsoft.Identity.Abstractions.CredentialDescriptionJsonConverter
Microsoft.Identity.Abstractions.CredentialDescriptionJsonConverter.CredentialDescriptionJsonConverter() -> void
Microsoft.Identity.Abstractions.DownstreamApiOptions.ExtraHeaderParameters.get -> System.Collections.Generic.IDictionary<string!, string!>?
Microsoft.Identity.Abstractions.DownstreamApiOptions.ExtraHeaderParameters.set -> void
Microsoft.Identity.Abstractions.DownstreamApiOptions.ExtraQueryParameters.get -> System.Collections.Generic.IDictionary<string!, string!>?
Microsoft.Identity.Abstractions.DownstreamApiOptions.ExtraQueryParameters.set -> void
Microsoft.Identity.Abstractions.IAuthorizationHeaderProvider<TResult>
Microsoft.Identity.Abstractions.IAuthorizationHeaderProvider<TResult>.CreateAuthorizationHeaderAsync(Microsoft.Identity.Abstractions.DownstreamApiOptions! downstreamApiOptions, System.Security.Claims.ClaimsPrincipal? claimsPrincipal = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<TResult>!
Microsoft.Identity.Abstractions.MicrosoftEntraApplicationOptions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ Microsoft.Identity.Abstractions.CredentialDescription.Algorithm.get -> string?
Microsoft.Identity.Abstractions.CredentialDescription.Algorithm.set -> void
Microsoft.Identity.Abstractions.CredentialDescriptionJsonConverter
Microsoft.Identity.Abstractions.CredentialDescriptionJsonConverter.CredentialDescriptionJsonConverter() -> void
Microsoft.Identity.Abstractions.DownstreamApiOptions.ExtraHeaderParameters.get -> System.Collections.Generic.IDictionary<string!, string!>?
Microsoft.Identity.Abstractions.DownstreamApiOptions.ExtraHeaderParameters.set -> void
Microsoft.Identity.Abstractions.DownstreamApiOptions.ExtraQueryParameters.get -> System.Collections.Generic.IDictionary<string!, string!>?
Microsoft.Identity.Abstractions.DownstreamApiOptions.ExtraQueryParameters.set -> void
Microsoft.Identity.Abstractions.IAuthorizationHeaderProvider<TResult>
Microsoft.Identity.Abstractions.IAuthorizationHeaderProvider<TResult>.CreateAuthorizationHeaderAsync(Microsoft.Identity.Abstractions.DownstreamApiOptions! downstreamApiOptions, System.Security.Claims.ClaimsPrincipal? claimsPrincipal = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<TResult>!
Microsoft.Identity.Abstractions.MicrosoftEntraApplicationOptions
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
#nullable enable
Microsoft.Identity.Abstractions.CredentialDescription.Algorithm.get -> string?
Microsoft.Identity.Abstractions.CredentialDescription.Algorithm.set -> void
Microsoft.Identity.Abstractions.DownstreamApiOptions.ExtraHeaderParameters.get -> System.Collections.Generic.IDictionary<string!, string!>?
Microsoft.Identity.Abstractions.DownstreamApiOptions.ExtraHeaderParameters.set -> void
Microsoft.Identity.Abstractions.DownstreamApiOptions.ExtraQueryParameters.get -> System.Collections.Generic.IDictionary<string!, string!>?
Microsoft.Identity.Abstractions.DownstreamApiOptions.ExtraQueryParameters.set -> void
Microsoft.Identity.Abstractions.IAuthorizationHeaderProvider<TResult>
Microsoft.Identity.Abstractions.IAuthorizationHeaderProvider<TResult>.CreateAuthorizationHeaderAsync(Microsoft.Identity.Abstractions.DownstreamApiOptions! downstreamApiOptions, System.Security.Claims.ClaimsPrincipal? claimsPrincipal = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<TResult>!
Microsoft.Identity.Abstractions.MicrosoftEntraApplicationOptions
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
#nullable enable
Microsoft.Identity.Abstractions.CredentialDescription.Algorithm.get -> string?
Microsoft.Identity.Abstractions.CredentialDescription.Algorithm.set -> void
Microsoft.Identity.Abstractions.DownstreamApiOptions.ExtraHeaderParameters.get -> System.Collections.Generic.IDictionary<string!, string!>?
Microsoft.Identity.Abstractions.DownstreamApiOptions.ExtraHeaderParameters.set -> void
Microsoft.Identity.Abstractions.DownstreamApiOptions.ExtraQueryParameters.get -> System.Collections.Generic.IDictionary<string!, string!>?
Microsoft.Identity.Abstractions.DownstreamApiOptions.ExtraQueryParameters.set -> void
Microsoft.Identity.Abstractions.IAuthorizationHeaderProvider<TResult>
Microsoft.Identity.Abstractions.IAuthorizationHeaderProvider<TResult>.CreateAuthorizationHeaderAsync(Microsoft.Identity.Abstractions.DownstreamApiOptions! downstreamApiOptions, System.Security.Claims.ClaimsPrincipal? claimsPrincipal = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<TResult>!
Microsoft.Identity.Abstractions.MicrosoftEntraApplicationOptions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@
HttpMethod = HttpMethod.Trace.ToString(),
ProtocolScheme = "bearer",
RelativePath = "/api/values",
RequestAppToken = true
RequestAppToken = true,
ExtraHeaderParameters = new Dictionary<string, string> { { "OData-Version", "4.0" } },
ExtraQueryParameters = new Dictionary<string, string> { { "filter", "name eq 'test'" } }
};

Assert.Equal("https://apitocall.domain.com/api/values", downstreamApiOptions.GetApiUrl());
Expand Down Expand Up @@ -94,9 +96,11 @@
Assert.Equal(downstreamApiOptions.AcquireTokenOptions.UserFlow, downstreamApiClone.AcquireTokenOptions.UserFlow);
Assert.Equal("application/json", downstreamApiClone.AcceptHeader);
Assert.Equal("application/json", downstreamApiClone.ContentType);
Assert.Equal(downstreamApiOptions.ExtraHeaderParameters, downstreamApiClone.ExtraHeaderParameters);
Assert.Equal(downstreamApiOptions.ExtraQueryParameters, downstreamApiClone.ExtraQueryParameters);

// If this fails, think of also adding a line to test the new property
Assert.Equal(12, typeof(DownstreamApiOptions).GetProperties().Length);
Assert.Equal(14, typeof(DownstreamApiOptions).GetProperties().Length);
Assert.Equal(15, typeof(AcquireTokenOptions).GetProperties().Length);

DownstreamApiOptionsReadOnlyHttpMethod options = new DownstreamApiOptionsReadOnlyHttpMethod(downstreamApiOptions, HttpMethod.Delete.ToString());
Expand Down Expand Up @@ -126,15 +130,56 @@
}

[Fact]
public void ExerciseApi()
public void ExtraParametersWorkCorrectly()
{
var downstreamApiOptions = new DownstreamApiOptions
{
ExtraHeaderParameters = new Dictionary<string, string>
{
{ "OData-Version", "4.0" },
{ "Accept-Language", "en-US" }
},
ExtraQueryParameters = new Dictionary<string, string>
{
{ "filter", "name eq 'test'" },
{ "top", "10" }
}
};

// Test that properties are set correctly
Assert.NotNull(downstreamApiOptions.ExtraHeaderParameters);
Assert.Equal(2, downstreamApiOptions.ExtraHeaderParameters.Count);
Assert.Equal("4.0", downstreamApiOptions.ExtraHeaderParameters["OData-Version"]);
Assert.Equal("en-US", downstreamApiOptions.ExtraHeaderParameters["Accept-Language"]);

Assert.NotNull(downstreamApiOptions.ExtraQueryParameters);
Assert.Equal(2, downstreamApiOptions.ExtraQueryParameters.Count);
Assert.Equal("name eq 'test'", downstreamApiOptions.ExtraQueryParameters["filter"]);
Assert.Equal("10", downstreamApiOptions.ExtraQueryParameters["top"]);

// Test cloning preserves the properties
var clonedOptions = downstreamApiOptions.Clone();
Assert.NotNull(clonedOptions.ExtraHeaderParameters);
Assert.Equal(downstreamApiOptions.ExtraHeaderParameters, clonedOptions.ExtraHeaderParameters);
Assert.NotNull(clonedOptions.ExtraQueryParameters);
Assert.Equal(downstreamApiOptions.ExtraQueryParameters, clonedOptions.ExtraQueryParameters);

// Test null values work
var emptyOptions = new DownstreamApiOptions();
Assert.Null(emptyOptions.ExtraHeaderParameters);
Assert.Null(emptyOptions.ExtraQueryParameters);

var clonedEmptyOptions = emptyOptions.Clone();
Assert.Null(clonedEmptyOptions.ExtraHeaderParameters);
Assert.Null(clonedEmptyOptions.ExtraQueryParameters);
}
IDownstreamApi downstreamApi = new CustomDownstreamApi();

// Call a service based on the configuration only. The name "service" maps to a
downstreamApi.CallApiAsync("service");

Check failure on line 179 in test/Microsoft.Identity.Abstractions.Tests/DownstreamApiTests.cs

View workflow job for this annotation

GitHub Actions / Abstractions GitHub Action Test

Invalid token '"service"' in a member declaration

Check failure on line 179 in test/Microsoft.Identity.Abstractions.Tests/DownstreamApiTests.cs

View workflow job for this annotation

GitHub Actions / Abstractions GitHub Action Test

) expected

Check failure on line 179 in test/Microsoft.Identity.Abstractions.Tests/DownstreamApiTests.cs

View workflow job for this annotation

GitHub Actions / Abstractions GitHub Action Test

Tuple must contain at least two elements.

Check failure on line 179 in test/Microsoft.Identity.Abstractions.Tests/DownstreamApiTests.cs

View workflow job for this annotation

GitHub Actions / Abstractions GitHub Action Test

Type expected

Check failure on line 179 in test/Microsoft.Identity.Abstractions.Tests/DownstreamApiTests.cs

View workflow job for this annotation

GitHub Actions / Abstractions GitHub Action Test

Invalid token '(' in a member declaration

// Calls a service based on the programmatic description only.
downstreamApi.CallApiAsync(null,

Check failure on line 182 in test/Microsoft.Identity.Abstractions.Tests/DownstreamApiTests.cs

View workflow job for this annotation

GitHub Actions / Abstractions GitHub Action Test

Invalid token 'null' in a member declaration

Check failure on line 182 in test/Microsoft.Identity.Abstractions.Tests/DownstreamApiTests.cs

View workflow job for this annotation

GitHub Actions / Abstractions GitHub Action Test

) expected

Check failure on line 182 in test/Microsoft.Identity.Abstractions.Tests/DownstreamApiTests.cs

View workflow job for this annotation

GitHub Actions / Abstractions GitHub Action Test

Tuple must contain at least two elements.

Check failure on line 182 in test/Microsoft.Identity.Abstractions.Tests/DownstreamApiTests.cs

View workflow job for this annotation

GitHub Actions / Abstractions GitHub Action Test

Type expected

Check failure on line 182 in test/Microsoft.Identity.Abstractions.Tests/DownstreamApiTests.cs

View workflow job for this annotation

GitHub Actions / Abstractions GitHub Action Test

Invalid token '(' in a member declaration
options =>
{
options.HttpMethod = HttpMethod.Get.ToString();
Expand Down
Loading