Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

.NET SDK Bug: CredentialsMethod.ApiToken throw duplicate authorization header error #471

Open
6 of 10 tasks
alovera opened this issue Jan 20, 2025 · 1 comment
Open
6 of 10 tasks
Labels
bug Something isn't working

Comments

@alovera
Copy link

alovera commented Jan 20, 2025

Checklist

  • I have looked into the README and have not found a suitable solution or answer.
  • I have looked into the documentation and have not found a suitable solution or answer.
  • I have searched the issues and have not found a suitable solution or answer.
  • I have searched the Slack Community and have not found a suitable solution or answer.
  • I agree to the terms within the OpenFGA Code of Conduct.

Description

There is a bug in the .NET OpenFga.Sdk in the ApiClient class when you configure the client to use ClientCredentialsMethod.ApiToken. The setup in our application is the following:

appSettings.json

{
  "OpenFGAConfiguration": {
    "ApiUrl": "https://localhost:8102",
    "StoreId": "01JJ2AKYJX4KXKF59JAVXMTAZ2",
    "ApiToken": "f54daf80-fcfd-47c4-886f-98804fcc9279"
  }
}

Program.cs

var openFgaConfig = builder.Configuration
    .GetSection("OpenFGAConfiguration")
    .Get<ClientConfiguration>()!;

var apiToken = builder.Configuration
    .GetSection("OpenFGAConfiguration")
    .GetValue<string>("ApiToken")

// No way to currently set this directly via .NET config because of ICredentialsConfig interface
openFgaConfig.Credentials = new Credentials
{
    Method = CredentialsMethod.ApiToken,
    Config = new CredentialsConfig { ApiToken = apiToken }
};

services.AddSingleton<ClientConfiguration>(openFgaConfig);
services.AddHttpClient<OpenFgaClient>();

Using the setup above, we receive the following error any time we try to use the OpenFgaClient in code:

System.FormatException
  HResult=0x80131537
  Message=Cannot add value because header 'Authorization' does not support multiple values.
  Source=System.Net.Http
  StackTrace:
   at System.Net.Http.Headers.HttpHeaders.ParseAndAddValue(HeaderDescriptor descriptor, HeaderStoreItemInfo info, String value)

After debugging the source code, we found the cause of the error. When the OpenFgaClient is instantiated, it creates an internal OpenFgaApi object (see here) passing in the ClientConfiguration from the DI container and the HttpClient created by the HttpClientFactory. The OpenFgaApi then creates an internal ApiClient, again passing down the configuration and http client (see here). This ApiClient is where the problematic code begins.

The ApiClient starts by initializing a BaseClient using the configuration and client (see here) and then it initializes the BaseClient again when it detects that the credential method is ApiToken (see here). Both of these calls use the same configuration and client.

These calls work fine the first time the HttpClient is initialized. The client default headers are added as expected in the BaseClient class (see here). However, it fails the second time the class is initialized because the HttpClientFactory reuses the same HttpClient which already has the default headers configured. When this occurs, you get the exception above when a second "Authorization" header is added to the DefaultRequestHeaders collection.

Expectation

The code above should work as configured, specifically when using the HttpClientFactory registration method. The BaseClient constructor should check to see if the default header(s) already exist before adding additional headers.

Reproduction

Given you configure the OpenFgaClient with the services.AddHttpClient<OpenFgaClient>() method
When you configure the OpenFgaClient to use ApiToken authentication
Then you receive the error "Cannot add value because header 'Authorization' does not support multiple values."

SDK Checklist

  • JS SDK
  • Go SDK
  • .NET SDK
  • Python SDK
  • Java SDK

OpenFGA SDK version

0.5.1

OpenFGA version

latest

SDK Configuration

Via appSettings.json configuration

Logs

No response

References

No response

@alovera alovera added the bug Something isn't working label Jan 20, 2025
@alovera
Copy link
Author

alovera commented Jan 21, 2025

I have found a work-around solution that still utilizes the IHttpFactory to manage the underlying HttpClient. Since the problem is with adding multiple "Authorization" headers to the HttpClient.DefaultRequestHeaders collection, you have to remove the headers when the HttpClient is injected and also remove the added DefaultHeader from configuration. Here's the code:

appSettings.json

{
  "OpenFGAConfiguration": {
    "ApiUrl": "https://localhost:8102",
    "StoreId": "01JJ2AKYJX4KXKF59JAVXMTAZ2",
    "ApiToken": "f54daf80-fcfd-47c4-886f-98804fcc9279"
  }
}

Program.cs

var openFgaConfig = builder.Configuration
    .GetSection("OpenFGAConfiguration")
    .Get<ClientConfiguration>()!;

var apiToken = builder.Configuration
    .GetSection("OpenFGAConfiguration")
    .GetValue<string>("ApiToken")

// No way to currently set this directly via .NET config because of ICredentialsConfig interface
openFgaConfig.Credentials = new Credentials
{
    Method = CredentialsMethod.ApiToken,
    Config = new CredentialsConfig { ApiToken = apiToken }
};

services.AddSingleton<ClientConfiguration>(openFgaConfig);

const string openFgaHttpClient = "OpenFgaHttpClient";
services.AddHttpClient(openFgaHttpClient);

// Register the OpenFgaClient as Transient because that's what the
// services.AddHttpClient<OpenFgaClient>() does behind the scenes.
// <see cref="https://tinyurl.com/necb7sfn"/>
services.AddTransient<OpenFgaClient>((sp) =>
{
    // Get the configuration and the client from the factory
    var clientConfiguration = sp.GetRequiredService<ClientConfiguration>();
    var clientFactory = sp.GetRequiredService<IHttpClientFactory>();
    var httpClient = clientFactory.CreateClient(openFgaHttpClient);

    // We have to clear the default request headers because the OpenFga.Sdk
    // library re-adds the authorization headers every time it gets instantiated
    // causing an exception.
    httpClient.DefaultRequestHeaders.Clear();
    if (clientConfiguration.DefaultHeaders.ContainsKey("Authorization"))
    {
        // Remove the default header here because it gets added to configuration
        // from within the OpenFga.Sdk library if you are using ApiToken
        // authentication
        clientConfiguration.DefaultHeaders.Remove("Authorization");
    }

    // Return the new OpenFGA client
    return new OpenFgaClient(clientConfiguration, httpClient);
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
Status: Backlog
Development

No branches or pull requests

1 participant