Skip to content

Commit

Permalink
[VATIS-106] Add support for IDS validation token (#66)
Browse files Browse the repository at this point in the history
  • Loading branch information
justinshannon authored Jan 15, 2025
1 parent 5c2eef1 commit 093ee81
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 4 deletions.
7 changes: 6 additions & 1 deletion Vatsim.Network/ClientAuth.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,9 @@ public string GenerateAuthChallenge()
}

public ushort ClientId => throw new NotImplementedException();
}

public string IdsValidationKey()
{
throw new NotImplementedException();
}
}
3 changes: 2 additions & 1 deletion Vatsim.Network/IClientAuth.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ namespace Vatsim.Network;

public interface IClientAuth
{
string? IdsValidationKey();
string? GenerateHubToken();
string GenerateAuthResponse(string challenge, string key = "");
string GenerateAuthChallenge();
ushort ClientId { get; }
}
}
16 changes: 14 additions & 2 deletions vATIS.Desktop/Atis/AtisBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@
using System.Threading;
using System.Threading.Tasks;
using Serilog;
using Vatsim.Network;
using Vatsim.Vatis.Atis.Extensions;
using Vatsim.Vatis.Atis.Nodes;
using Vatsim.Vatis.Io;
using Vatsim.Vatis.NavData;
using Vatsim.Vatis.Profiles.Models;
using Vatsim.Vatis.TextToSpeech;
using Vatsim.Vatis.Utils;
using Vatsim.Vatis.Weather;
using Vatsim.Vatis.Weather.Decoder.Entity;

Expand All @@ -35,6 +37,7 @@ public class AtisBuilder : IAtisBuilder
private readonly IMetarRepository? _metarRepository;
private readonly INavDataRepository? _navDataRepository;
private readonly ITextToSpeechService? _textToSpeechService;
private readonly IClientAuth _clientAuth;

/// <summary>
/// Initializes a new instance of the <see cref="AtisBuilder"/> class.
Expand All @@ -43,13 +46,15 @@ public class AtisBuilder : IAtisBuilder
/// <param name="navDataRepository">The navigation data repository.</param>
/// <param name="textToSpeechService">The text to speech service.</param>
/// <param name="metarRepository">The METAR repository.</param>
/// <param name="clientAuth">The client auth service.</param>
public AtisBuilder(IDownloader downloader, INavDataRepository navDataRepository,
ITextToSpeechService textToSpeechService, IMetarRepository metarRepository)
ITextToSpeechService textToSpeechService, IMetarRepository metarRepository, IClientAuth clientAuth)
{
_downloader = downloader;
_metarRepository = metarRepository;
_navDataRepository = navDataRepository;
_textToSpeechService = textToSpeechService;
_clientAuth = clientAuth;
}

/// <inheritdoc/>
Expand Down Expand Up @@ -117,8 +122,15 @@ public async Task UpdateIds(AtisStation station, AtisPreset preset, char current
{
ArgumentNullException.ThrowIfNull(_downloader);

string? jwt = null;
if (!string.IsNullOrEmpty(_clientAuth.IdsValidationKey()))
{
// Generate a signed JWT token for optional validation by the IDS server.
jwt = JwtHelper.GenerateJwt(_clientAuth.IdsValidationKey(), "ids-validation");
}

var jsonSerialized = JsonSerializer.Serialize(request, SourceGenerationContext.NewDefault.IdsUpdateRequest);
await _downloader.PostJson(station.IdsEndpoint, jsonSerialized);
await _downloader.PostJson(station.IdsEndpoint, jsonSerialized, jwt);
}
catch (OperationCanceledException)
{
Expand Down
46 changes: 46 additions & 0 deletions vATIS.Desktop/Utils/JwtHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// <copyright file="JwtHelper.cs" company="Justin Shannon">
// Copyright (c) Justin Shannon. All rights reserved.
// Licensed under the GPLv3 license. See LICENSE file in the project root for full license information.
// </copyright>

using System;
using System.Security.Cryptography;
using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;

namespace Vatsim.Vatis.Utils;

/// <summary>
/// Provides utility methods for creating and handling JSON Web Tokens (JWTs).
/// </summary>
public static class JwtHelper
{
/// <summary>
/// Generates a JSON Web Token (JWT) using the specified private key, issuer, audience, and expiration time.
/// </summary>
/// <param name="privateKey">The private RSA key used to sign the token.</param>
/// <param name="keyId">The name of the security key.</param>
/// <returns>A signed JWT as a string.</returns>
/// <exception cref="ArgumentNullException">Thrown if any of the required parameters are null.</exception>
/// <exception cref="CryptographicException">Thrown if the provided private key is invalid or cannot be imported.</exception>
public static string GenerateJwt(string? privateKey, string keyId)
{
if (string.IsNullOrEmpty(privateKey))
throw new ArgumentNullException(nameof(privateKey));

var privateKeyBytes = Convert.FromBase64String(privateKey);
using var rsa = RSA.Create();
rsa.ImportPkcs8PrivateKey(privateKeyBytes, out _);
var securityKey = new RsaSecurityKey(rsa) { KeyId = keyId };

var handler = new JsonWebTokenHandler();
return handler.CreateToken(new SecurityTokenDescriptor
{
Issuer = "vatis.app",
Audience = "vatis.app",
NotBefore = DateTime.UtcNow,
Expires = DateTime.UtcNow.Add(TimeSpan.FromMinutes(5)),
SigningCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.RsaSha256)
});
}
}
1 change: 1 addition & 0 deletions vATIS.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=fsver/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=helo/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=icao/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=jwks/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=metars/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=mscfs/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=msfs/@EntryIndexedValue">True</s:Boolean>
Expand Down

0 comments on commit 093ee81

Please sign in to comment.