added auth with a simple UI

This commit is contained in:
2026-03-09 23:29:04 +01:00
parent c8347ac96b
commit 82ac241129
123 changed files with 3419 additions and 93 deletions

View File

@@ -0,0 +1,36 @@
using System.Net.Http.Headers;
using OED.Api.Core.Interfaces.Services;
namespace OED.Api.Infrastructure.Esi;
public class EsiAuthHandler(
ITokenStore tokenStore,
IEsiTokenRefreshService refreshService,
IHttpContextAccessor httpContextAccessor
) : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
var characterId = GetCharacterIdFromSession();
if (characterId is null)
return await base.SendAsync(request, cancellationToken);
var accessToken = await tokenStore.GetAccessTokenAsync(characterId.Value);
if (accessToken is null)
accessToken = await refreshService.RefreshAsync(characterId.Value);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
return await base.SendAsync(request, cancellationToken);
}
private long? GetCharacterIdFromSession()
{
var context = httpContextAccessor.HttpContext;
// CharacterId is set on the HttpContext by session middleware
return context?.Items["CharacterId"] as long?;
}
}

View File

@@ -0,0 +1,61 @@
using OED.Api.Core.Interfaces.Services;
namespace OED.Api.Infrastructure.Esi;
public interface IEsiTokenRefreshService
{
Task<string> RefreshAsync(long characterId);
}
public class EsiTokenRefreshService(
IHttpClientFactory httpClientFactory,
ITokenStore tokenStore,
IConfiguration config
) : IEsiTokenRefreshService
{
public async Task<string> RefreshAsync(long characterId)
{
var refreshToken = await tokenStore.GetRefreshTokenAsync(characterId)
?? throw new InvalidOperationException($"No refresh token found for character {characterId}");
var clientId = config["Eve:ClientId"]!;
var secretKey = config["Eve:SecretKey"]!;
var credentials = Convert.ToBase64String(
System.Text.Encoding.UTF8.GetBytes($"{clientId}:{secretKey}")
);
var client = httpClientFactory.CreateClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://login.eveonline.com/v2/oauth/token");
request.Headers.Add("Authorization", $"Basic {credentials}");
request.Content = new FormUrlEncodedContent(new Dictionary<string, string>
{
["grant_type"] = "refresh_token",
["refresh_token"] = refreshToken
});
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadFromJsonAsync<EsiTokenResponse>()
?? throw new InvalidOperationException("Empty token response from EVE SSO");
// Always overwrite — EVE may rotate the refresh token
await tokenStore.SetRefreshTokenAsync(characterId, result.RefreshToken);
await tokenStore.SetAccessTokenAsync(characterId, result.AccessToken, TimeSpan.FromSeconds(result.ExpiresIn - 30));
return result.AccessToken;
}
}
public record EsiTokenResponse(
string access_token,
int expires_in,
string token_type,
string refresh_token
)
{
public string AccessToken => access_token;
public int ExpiresIn => expires_in;
public string RefreshToken => refresh_token;
}