-
-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Added ElevenLabs-DotNet-Proxy project - Added support for proxy api gateway - Updated unit tests
- Loading branch information
1 parent
60b1686
commit ca3accf
Showing
33 changed files
with
974 additions
and
182 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
<PropertyGroup> | ||
<TargetFramework>net6.0</TargetFramework> | ||
<ImplicitUsings>disable</ImplicitUsings> | ||
<Nullable>disable</Nullable> | ||
<TreatWarningsAsErrors>True</TreatWarningsAsErrors> | ||
<GeneratePackageOnBuild>false</GeneratePackageOnBuild> | ||
<SignAssembly>false</SignAssembly> | ||
<Authors>Stephen Hodgson</Authors> | ||
<Product>ElevenLabs-DotNet-Proxy</Product> | ||
<Description>A simple Proxy API gateway for ElevenLabs-DotNet to make authenticated requests from a front end application without exposing your API keys.</Description> | ||
<Copyright>2023</Copyright> | ||
<PackageProjectUrl>https://github.com/RageAgainstThePixel/ElevenLabs-DotNet</PackageProjectUrl> | ||
<RepositoryUrl>https://github.com/RageAgainstThePixel/ElevenLabs-DotNet</RepositoryUrl> | ||
<PackageTags>ElevenLabs, AI, ML, API, api-proxy, proxy, gateway</PackageTags> | ||
<Title>ElevenLabs API Proxy</Title> | ||
<PackageId>ElevenLabs-DotNet-Proxy</PackageId> | ||
<Version>1.0.0</Version> | ||
<RootNamespace>ElevenLabs.Proxy</RootNamespace> | ||
<PackageReleaseNotes>Initial Release!</PackageReleaseNotes> | ||
<TreatWarningsAsErrors>True</TreatWarningsAsErrors> | ||
<GeneratePackageOnBuild>false</GeneratePackageOnBuild> | ||
<PackageReadmeFile>Readme.md</PackageReadmeFile> | ||
<IncludeSymbols>True</IncludeSymbols> | ||
<PackageIcon>ElevenLabsIcon.png</PackageIcon> | ||
<PackageLicenseFile>LICENSE</PackageLicenseFile> | ||
</PropertyGroup> | ||
<ItemGroup> | ||
<ProjectReference Include="..\ElevenLabs-DotNet\ElevenLabs-DotNet.csproj" /> | ||
<FrameworkReference Include="Microsoft.AspNetCore.App" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<None Include="..\ElevenLabs-DotNet\Assets\ElevenLabsIcon.png"> | ||
<Pack>True</Pack> | ||
<PackagePath>\</PackagePath> | ||
</None> | ||
<None Include="..\LICENSE"> | ||
<Pack>True</Pack> | ||
<PackagePath>\</PackagePath> | ||
</None> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<None Update="Readme.md"> | ||
<Pack>True</Pack> | ||
<PackagePath>\</PackagePath> | ||
</None> | ||
</ItemGroup> | ||
</Project> |
13 changes: 13 additions & 0 deletions
13
ElevenLabs-DotNet-Proxy/Proxy/AbstractAuthenticationFilter.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
// Licensed under the MIT License. See LICENSE in the project root for license information. | ||
|
||
using Microsoft.AspNetCore.Http; | ||
|
||
namespace ElevenLabs.Proxy | ||
{ | ||
/// <inheritdoc /> | ||
public abstract class AbstractAuthenticationFilter : IAuthenticationFilter | ||
{ | ||
/// <inheritdoc /> | ||
public abstract void ValidateAuthentication(IHeaderDictionary request); | ||
} | ||
} |
179 changes: 179 additions & 0 deletions
179
ElevenLabs-DotNet-Proxy/Proxy/ElevenLabsProxyStartup.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,179 @@ | ||
// Licensed under the MIT License. See LICENSE in the project root for license information. | ||
|
||
using ElevenLabs.Proxy; | ||
using Microsoft.AspNetCore.Builder; | ||
using Microsoft.AspNetCore.Hosting; | ||
using Microsoft.AspNetCore.Http; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using Microsoft.Extensions.Hosting; | ||
using Microsoft.Net.Http.Headers; | ||
using System; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Net.Http; | ||
using System.Security.Authentication; | ||
using System.Threading.Tasks; | ||
using MediaTypeHeaderValue = System.Net.Http.Headers.MediaTypeHeaderValue; | ||
|
||
namespace ElevenLabs.Proxy | ||
{ | ||
/// <summary> | ||
/// Used in ASP.NET Core WebApps to start your own ElevenLabs web api proxy. | ||
/// </summary> | ||
public class ElevenLabsProxyStartup | ||
{ | ||
private ElevenLabsClient elevenLabsClient; | ||
private IAuthenticationFilter authenticationFilter; | ||
|
||
// Copied from https://github.com/microsoft/reverse-proxy/blob/51d797986b1fea03500a1ad173d13a1176fb5552/src/ReverseProxy/Forwarder/RequestUtilities.cs#L61-L83 | ||
private static readonly HashSet<string> ExcludedHeaders = new HashSet<string>() | ||
{ | ||
HeaderNames.Connection, | ||
HeaderNames.TransferEncoding, | ||
HeaderNames.KeepAlive, | ||
HeaderNames.Upgrade, | ||
"Proxy-Connection", | ||
"Proxy-Authenticate", | ||
"Proxy-Authentication-Info", | ||
"Proxy-Authorization", | ||
"Proxy-Features", | ||
"Proxy-Instruction", | ||
"Security-Scheme", | ||
"ALPN", | ||
"Close", | ||
HeaderNames.TE, | ||
#if NET | ||
HeaderNames.AltSvc, | ||
#else | ||
"Alt-Svc", | ||
#endif | ||
}; | ||
|
||
public void ConfigureServices(IServiceCollection services) { } | ||
|
||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) | ||
{ | ||
if (env.IsDevelopment()) | ||
{ | ||
app.UseDeveloperExceptionPage(); | ||
} | ||
|
||
elevenLabsClient = app.ApplicationServices.GetRequiredService<ElevenLabsClient>(); | ||
authenticationFilter = app.ApplicationServices.GetRequiredService<IAuthenticationFilter>(); | ||
|
||
app.UseHttpsRedirection(); | ||
app.UseRouting(); | ||
app.UseEndpoints(endpoints => | ||
{ | ||
endpoints.MapGet("/health", HealthEndpoint); | ||
endpoints.Map($"{elevenLabsClient.ElevenLabsClientSettings.BaseRequest}{{**endpoint}}", HandleRequest); | ||
}); | ||
} | ||
|
||
/// <summary> | ||
/// Creates a new <see cref="IHost"/> that acts as a proxy web api for ElevenLabs. | ||
/// </summary> | ||
/// <typeparam name="T"><see cref="IAuthenticationFilter"/> type to use to validate your custom issued tokens.</typeparam> | ||
/// <param name="args">Startup args.</param> | ||
/// <param name="elevenLabsClient"><see cref="ElevenLabsClient"/> with configured <see cref="ElevenLabsAuthentication"/> and <see cref="ElevenLabsClientSettings"/>.</param> | ||
public static IHost CreateDefaultHost<T>(string[] args, ElevenLabsClient elevenLabsClient) where T : class, IAuthenticationFilter | ||
{ | ||
return Host.CreateDefaultBuilder(args) | ||
.ConfigureWebHostDefaults(webBuilder => | ||
{ | ||
webBuilder.UseStartup<ElevenLabsProxyStartup>(); | ||
webBuilder.ConfigureKestrel(options => | ||
{ | ||
options.AllowSynchronousIO = false; | ||
options.Limits.MinRequestBodyDataRate = null; | ||
options.Limits.MinResponseDataRate = null; | ||
options.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(10); | ||
options.Limits.RequestHeadersTimeout = TimeSpan.FromMinutes(2); | ||
}); | ||
}) | ||
.ConfigureServices(services => | ||
{ | ||
services.AddSingleton(elevenLabsClient); | ||
services.AddSingleton<IAuthenticationFilter, T>(); | ||
}).Build(); | ||
} | ||
|
||
private static async Task HealthEndpoint(HttpContext context) | ||
{ | ||
// Respond with a 200 OK status code and a plain text message | ||
context.Response.StatusCode = StatusCodes.Status200OK; | ||
const string contentType = "text/plain"; | ||
context.Response.ContentType = contentType; | ||
const string content = "OK"; | ||
await context.Response.WriteAsync(content); | ||
} | ||
|
||
/// <summary> | ||
/// Handles incoming requests, validates authentication, and forwards the request to ElevenLabs API | ||
/// </summary> | ||
private async Task HandleRequest(HttpContext httpContext, string endpoint) | ||
{ | ||
try | ||
{ | ||
authenticationFilter.ValidateAuthentication(httpContext.Request.Headers); | ||
|
||
var method = new HttpMethod(httpContext.Request.Method); | ||
var uri = new Uri(string.Format(elevenLabsClient.ElevenLabsClientSettings.BaseRequestUrlFormat, $"{endpoint}{httpContext.Request.QueryString}")); | ||
var elevenLabsRequest = new HttpRequestMessage(method, uri); | ||
|
||
elevenLabsRequest.Content = new StreamContent(httpContext.Request.Body); | ||
|
||
if (httpContext.Request.ContentType != null) | ||
{ | ||
elevenLabsRequest.Content.Headers.ContentType = MediaTypeHeaderValue.Parse(httpContext.Request.ContentType); | ||
} | ||
|
||
var proxyResponse = await elevenLabsClient.Client.SendAsync(elevenLabsRequest, HttpCompletionOption.ResponseHeadersRead); | ||
httpContext.Response.StatusCode = (int)proxyResponse.StatusCode; | ||
|
||
foreach (var (key, value) in proxyResponse.Headers) | ||
{ | ||
if (ExcludedHeaders.Contains(key)) { continue; } | ||
httpContext.Response.Headers[key] = value.ToArray(); | ||
} | ||
|
||
foreach (var (key, value) in proxyResponse.Content.Headers) | ||
{ | ||
if (ExcludedHeaders.Contains(key)) { continue; } | ||
httpContext.Response.Headers[key] = value.ToArray(); | ||
} | ||
|
||
httpContext.Response.ContentType = proxyResponse.Content.Headers.ContentType?.ToString() ?? string.Empty; | ||
const string streamingContent = "text/event-stream"; | ||
|
||
if (httpContext.Response.ContentType.Equals(streamingContent)) | ||
{ | ||
var stream = await proxyResponse.Content.ReadAsStreamAsync(); | ||
await WriteServerStreamEventsAsync(httpContext, stream); | ||
} | ||
else | ||
{ | ||
await proxyResponse.Content.CopyToAsync(httpContext.Response.Body); | ||
} | ||
} | ||
catch (AuthenticationException authenticationException) | ||
{ | ||
httpContext.Response.StatusCode = StatusCodes.Status401Unauthorized; | ||
await httpContext.Response.WriteAsync(authenticationException.Message); | ||
} | ||
catch (Exception e) | ||
{ | ||
httpContext.Response.StatusCode = StatusCodes.Status500InternalServerError; | ||
await httpContext.Response.WriteAsync(e.Message); | ||
} | ||
} | ||
|
||
private static async Task WriteServerStreamEventsAsync(HttpContext httpContext, Stream contentStream) | ||
{ | ||
var responseStream = httpContext.Response.Body; | ||
await contentStream.CopyToAsync(responseStream, httpContext.RequestAborted); | ||
await responseStream.FlushAsync(httpContext.RequestAborted); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
// Licensed under the MIT License. See LICENSE in the project root for license information. | ||
|
||
using System.Security.Authentication; | ||
using Microsoft.AspNetCore.Http; | ||
|
||
namespace ElevenLabs.Proxy | ||
{ | ||
/// <summary> | ||
/// Filters headers to ensure your users have the correct access. | ||
/// </summary> | ||
public interface IAuthenticationFilter | ||
{ | ||
/// <summary> | ||
/// Checks the headers for your user issued token. | ||
/// If it's not valid, then throw <see cref="AuthenticationException"/>. | ||
/// </summary> | ||
/// <param name="request"></param> | ||
/// <exception cref="AuthenticationException"></exception> | ||
void ValidateAuthentication(IHeaderDictionary request); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
|
||
# ElevenLabs-DotNet-Proxy | ||
|
||
[![NuGet version (ElevenLabs-DotNet-Proxy)](https://img.shields.io/nuget/v/ElevenLabs-DotNet-Proxy.svg?label=ElevenLabs-DotNet-Proxy&logo=nuget)](https://www.nuget.org/packages/ElevenLabs-DotNet-Proxy/) | ||
|
||
A simple Proxy API gateway for [ElevenLabs-DotNet](https://github.com/RageAgainstThePixel/ElevenLabs-DotNet) to make authenticated requests from a front end application without exposing your API keys. | ||
|
||
## Getting started | ||
|
||
### Install from NuGet | ||
|
||
Install package [`ElevenLabs-DotNet-Proxy` from Nuget](https://www.nuget.org/packages/ElevenLabs-DotNet-Proxy/). Here's how via command line: | ||
|
||
```powershell | ||
Install-Package ElevenLabs-DotNet-Proxy | ||
``` | ||
|
||
## Documentation | ||
|
||
Using either the [ElevenLabs-DotNet](https://github.com/RageAgainstThePixel/ElevenLabs-DotNet) or [com.rest.elevenlabs](https://github.com/RageAgainstThePixel/com.rest.elevenlabs) packages directly in your front-end app may expose your API keys and other sensitive information. To mitigate this risk, it is recommended to set up an intermediate API that makes requests to ElevenLabs on behalf of your front-end app. This library can be utilized for both front-end and intermediary host configurations, ensuring secure communication with the ElevenLabs API. | ||
|
||
### Front End Example | ||
|
||
In the front end example, you will need to securely authenticate your users using your preferred OAuth provider. Once the user is authenticated, exchange your custom auth token with your API key on the backend. | ||
|
||
Follow these steps: | ||
|
||
1. Setup a new project using either the [ElevenLabs-DotNet](https://github.com/RageAgainstThePixel/ElevenLabs-DotNet) or [com.rest.elevenlabs](https://github.com/RageAgainstThePixel/com.rest.elevenlabs) packages. | ||
2. Authenticate users with your OAuth provider. | ||
3. After successful authentication, create a new `ElevenLabsAuthentication` object and pass in the custom token. | ||
4. Create a new `ElevenLabsClientSettings` object and specify the domain where your intermediate API is located. | ||
5. Pass your new `auth` and `settings` objects to the `ElevenLabsClient` constructor when you create the client instance. | ||
|
||
Here's an example of how to set up the front end: | ||
|
||
```csharp | ||
var authToken = await LoginAsync(); | ||
var auth = new ElevenLabsAuthentication(authToken); | ||
var settings = new ElevenLabsClientSettings(domain: "api.your-custom-domain.com"); | ||
var api = new ElevenLabsClient(auth, settings); | ||
``` | ||
|
||
This setup allows your front end application to securely communicate with your backend that will be using the ElevenLabs-DotNet-Proxy, which then forwards requests to the ElevenLabs API. This ensures that your ElevenLabs API keys and other sensitive information remain secure throughout the process. | ||
|
||
### Back End Example | ||
|
||
In this example, we demonstrate how to set up and use `ElevenLabsProxyStartup` in a new ASP.NET Core web app. The proxy server will handle authentication and forward requests to the ElevenLabs API, ensuring that your API keys and other sensitive information remain secure. | ||
|
||
1. Create a new [ASP.NET Core minimal web API](https://learn.microsoft.com/en-us/aspnet/core/tutorials/min-web-api?view=aspnetcore-6.0) project. | ||
2. Add the ElevenLabs-DotNet nuget package to your project. | ||
- Powershell install: `Install-Package ElevenLabs-DotNet-Proxy` | ||
- Manually editing .csproj: `<PackageReference Include="ElevenLabs-DotNet-Proxy" />` | ||
3. Create a new class that inherits from `AbstractAuthenticationFilter` and override the `ValidateAuthentication` method. This will implement the `IAuthenticationFilter` that you will use to check user session token against your internal server. | ||
4. In `Program.cs`, create a new proxy web application by calling `ElevenLabsProxyStartup.CreateDefaultHost` method, passing your custom `AuthenticationFilter` as a type argument. | ||
5. Create `ElevenLabsAuthentication` and `ElevenLabsClientSettings` as you would normally with your API keys, org id, or Azure settings. | ||
|
||
```csharp | ||
public partial class Program | ||
{ | ||
private class AuthenticationFilter : AbstractAuthenticationFilter | ||
{ | ||
public override void ValidateAuthentication(IHeaderDictionary request) | ||
{ | ||
// You will need to implement your own class to properly test | ||
// custom issued tokens you've setup for your end users. | ||
if (!request["xi-api-key"].ToString().Contains(userToken)) | ||
{ | ||
throw new AuthenticationException("User is not authorized"); | ||
} | ||
} | ||
} | ||
|
||
public static void Main(string[] args) | ||
{ | ||
var client = new ElevenLabsClient(); | ||
var proxy = ElevenLabsProxyStartup.CreateDefaultHost<AuthenticationFilter>(args, client); | ||
proxy.Run(); | ||
} | ||
} | ||
``` | ||
|
||
Once you have set up your proxy server, your end users can now make authenticated requests to your proxy api instead of directly to the ElevenLabs API. The proxy server will handle authentication and forward requests to the ElevenLabs API, ensuring that your API keys and other sensitive information remain secure. |
11 changes: 11 additions & 0 deletions
11
ElevenLabs-DotNet-Tests-Proxy/ElevenLabs-DotNet-Tests-Proxy.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<Project Sdk="Microsoft.NET.Sdk.Web"> | ||
<PropertyGroup> | ||
<TargetFramework>net6.0</TargetFramework> | ||
<Nullable>disable</Nullable> | ||
<ImplicitUsings>disable</ImplicitUsings> | ||
<RootNamespace>ElevenLabs.Tests.Proxy</RootNamespace> | ||
</PropertyGroup> | ||
<ItemGroup> | ||
<ProjectReference Include="..\ElevenLabs-DotNet-Proxy\ElevenLabs-DotNet-Proxy.csproj" /> | ||
</ItemGroup> | ||
</Project> |
Oops, something went wrong.