Newer
Older
using FitConnect.Services;
using FitConnect.Services.Interfaces;
using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using Route = FitConnect.Services.Models.v1.Routes.Route;
private readonly FitConnectEnvironment _environment;
private readonly ILogger? _logger;
private readonly IRouteService _routeService;
private readonly ISelfServicePortalService _selfServicePortalService;
public Router(FitConnectEnvironment environment, ILogger? logger = null) {
_environment = environment;
_logger = logger;
_routeService = new RouteService(environment.RoutingUrl, "v1", logger);
_selfServicePortalService = new SelfServicePortalService(environment.SspUrl, "v1", logger);
public async Task<List<Route>> FindDestinationsAsync(string leiaKey, string? ags = null,
var routes = await _routeService.GetDestinationIdAsync(leiaKey, ags, ars, areaId);
_logger?.LogInformation("Testing destination {DestinationId}", route.DestinationId);
var verifyJwt = await VerifyDestinationSignature(route);
if (!verifyJwt) throw new Exception("Invalid destination signature");
verifyJwt &= await VerifyDestinationParametersSignature(route);
if (!verifyJwt) throw new Exception("Invalid destination parameter signature");
verifyJwt &= VerifySubmissionHost(route);
"SubmissionHost does not match DestinationParameters SubmissionUrl");
/// <summary>
/// Finding Areas
/// </summary>
/// <param name="filter"></param>
/// <param name="totalCount"></param>
/// <param name="offset"></param>
/// <param name="limit"></param>
/// <returns></returns>
public async Task<IEnumerable<Area>> GetAreas(List<string> filter, int offset = 0,
int limit = 100) {
var dto = await _routeService.GetAreas(filter, offset, limit);
// totalCount = dto?.TotalCount ?? 0;
return dto?.Areas ?? new List<Area>();
}
public async Task<IEnumerable<Area>> GetAreas(string filter, int offset = 0,
int limit = 100) => await GetAreas(new List<string> { filter }, offset, limit);
private bool VerifySubmissionHost(Route route) {
var signature = new JsonWebToken(route.DestinationSignature);
var payload =
JsonConvert.DeserializeObject<dynamic>(
Base64UrlEncoder.Decode(signature.EncodedPayload));
string? submissionHost = payload?.submissionHost;
if (submissionHost == null) return false;
var destinationUri = new Uri(route.DestinationParameters.SubmissionUrl);
return destinationUri.Host == submissionHost;
private async Task<bool> VerifyDestinationParametersSignature(Route route) {
// Get Key from SubmissionAPI
var submissionKey =
await GetSubmissionServiceValidationJwk(route.DestinationParameters.SubmissionUrl);
var parameterJson = JsonConvert.SerializeObject(route.DestinationParameters,
new JsonSerializerSettings {
NullValueHandling = NullValueHandling.Ignore,
Formatting = Formatting.None,
ContractResolver = new OrderedContractResolver()
});
var encodedParameter = Base64Url.Encode(Encoding.UTF8.GetBytes(parameterJson));
var signature =
route.DestinationParametersSignature.Replace("..", $".{encodedParameter}.");
// KID to check is in signature header kid
var header = JsonConvert.DeserializeObject<dynamic>(
Base64UrlEncoder.Decode(route.DestinationParametersSignature.Split('.')[0])
);
_logger?.LogInformation("Testing with kid: {Kid}", kid);
new JsonWebKeySet(submissionKey).Keys.First(k => k.Kid == kid), _logger);
}
private async Task<bool> VerifyDestinationSignature(Route route) {
var validation = await _selfServicePortalService.GetValidationJwk();
var token = new JsonWebToken(route.DestinationSignature);
var key = validation.Keys.First(k => k.Kid == token.Kid);
var verifyJwt =
FitEncryption.VerifyJwt(route.DestinationSignature, key, _logger);
return verifyJwt;
}
/// <summary>
/// </summary>
/// <returns></returns>
public static async Task<string> GetSubmissionServiceValidationJwk(string baseUrl) {
var client = new HttpClient {
BaseAddress = new Uri(baseUrl)
};
var result = await client.GetAsync("/.well-known/jwks.json");
public class OrderedContractResolver : DefaultContractResolver {
protected override IList<JsonProperty> CreateProperties(
Type type, MemberSerialization memberSerialization) {
NamingStrategy = new CamelCaseNamingStrategy();
return base.CreateProperties(type, memberSerialization).OrderBy(p => p.PropertyName)
.ToList();