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

Fix for error creating API tokens with a short encryption seed string #680

Merged
merged 1 commit into from
Dec 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion BLAZAM/BLAZAM.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<ImplicitUsings>enable</ImplicitUsings>
<ServerGarbageCollection>false</ServerGarbageCollection>
<AssemblyVersion>1.2.1</AssemblyVersion>
<Version>2024.11.30.2133</Version>
<Version>2024.12.01.1549</Version>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
<RootNamespace>BLAZAM</RootNamespace>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
Expand Down
196 changes: 106 additions & 90 deletions BLAZAM/ProgramHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -176,11 +176,12 @@ public static WebApplicationBuilder InjectServices(this WebApplicationBuilder bu
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true, // Important: Validate the signing key
IssuerSigningKey = ApplicationInfo.tokenKey,
IssuerSigningKey = new SymmetricSecurityKey (Encryption.Instance.Key),
ValidateIssuer = false,
ValidateAudience = false,
ValidateActor = false,
ValidateLifetime = true
ValidateLifetime = true,

};
options.Events = new JwtAuthenticationEventsHandler(
builder.Services.BuildServiceProvider().GetRequiredService<IHttpContextAccessor>(),
Expand Down Expand Up @@ -234,12 +235,12 @@ public static WebApplicationBuilder InjectServices(this WebApplicationBuilder bu
.SetHandlerLifetime(TimeSpan.FromMinutes(5)) //Set lifetime to five minutes
.AddPolicyHandler(GetWebhookRetryPolicy());

builder.Services.AddHttpClient(HttpClientNames.WebHookHttpClientNoSSLCheckName)
.SetHandlerLifetime(TimeSpan.FromMinutes(5)) //Set lifetime to five minutes
.AddPolicyHandler(GetWebhookRetryPolicy()).ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler
{
ServerCertificateCustomValidationCallback = (m, c, ch, e) => true
});
builder.Services.AddHttpClient(HttpClientNames.WebHookHttpClientNoSSLCheckName)
.SetHandlerLifetime(TimeSpan.FromMinutes(5)) //Set lifetime to five minutes
.AddPolicyHandler(GetWebhookRetryPolicy()).ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler
{
ServerCertificateCustomValidationCallback = (m, c, ch, e) => true
});

//Also keeping this here for a possible future API, though this would be for internal use
//builder.Services.AddTransient<ApiService>();
Expand Down Expand Up @@ -351,117 +352,132 @@ public static WebApplicationBuilder InjectServices(this WebApplicationBuilder bu

builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Blazam API",
Version = "v1" ,
Description="The official Blazam API documentation. Authorization is required for API access.",
License=new OpenApiLicense() { Name="MIT License", Url= new Uri("https://github.com/Blazam-App/BLAZAM/blob/v1-Dev/LICENSE") },
Contact=new() { Email = "support@blazam.org",
Name="Blazam Support",
Url=new("https://blazam.org/support") },
TermsOfService=new Uri("https://blazam.org/tos")
});

// Add descriptions using XML comments
var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename));

// Configure Swagger to use JWT Bearer authorization
//c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
//{
// Description = "JWT Authorization header using the Bearer scheme.Example: \"Authorization: Bearer {token}\"",
// Name = "Authorization",
// In = ParameterLocation.Header,
// Type = SecuritySchemeType.Http,
// Scheme = "bearer",
// BearerFormat = "JWT"
//});
c.SwaggerDoc("v1", new OpenApiInfo
{
Title = "Blazam API",
Version = "v1",
Description = "The official Blazam API documentation. Authorization is required for API access.",
License = new OpenApiLicense() { Name = "MIT License", Url = new Uri("https://github.com/Blazam-App/BLAZAM/blob/v1-Dev/LICENSE") },
Contact = new()
{
Email = "support@blazam.org",
Name = "Blazam Support",
Url = new("https://blazam.org/support")
},
TermsOfService = new Uri("https://blazam.org/tos")
});

// Add descriptions using XML comments
var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename));

// Configure Swagger to use JWT Bearer authorization
var jwtSecurityScheme = new OpenApiSecurityScheme
{
Description = "JWT Authorization header using the Bearer scheme.Example: \"Authorization: Bearer {token}\"",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.Http,
Scheme = JwtBearerDefaults.AuthenticationScheme,
BearerFormat = "JWT",
Reference = new OpenApiReference
{
Id = JwtBearerDefaults.AuthenticationScheme,
Type = ReferenceType.SecurityScheme
}
};

c.AddSecurityDefinition(jwtSecurityScheme.Reference.Id, jwtSecurityScheme);
c.AddSecurityRequirement(new OpenApiSecurityRequirement() {
{ jwtSecurityScheme,Array.Empty<string>() }
});
});

builder.Host.UseWindowsService();



return builder;
}

public static void PreRun(this WebApplication application)
public static void PreRun(this WebApplication application)
{
//Setup Seq logging if allowed by admin
try
{
//Setup Seq logging if allowed by admin
try
var context = Program.AppInstance.Services.GetRequiredService<IAppDatabaseFactory>().CreateDbContext();
if (context != null && context.AppSettings.FirstOrDefault()?.SendLogsToDeveloper != null)
{
var context = Program.AppInstance.Services.GetRequiredService<IAppDatabaseFactory>().CreateDbContext();
if (context != null && context.AppSettings.FirstOrDefault()?.SendLogsToDeveloper != null)
{
Loggers.SendToSeqServer = context.AppSettings.FirstOrDefault().SendLogsToDeveloper;

}
Loggers.SendToSeqServer = context.AppSettings.FirstOrDefault().SendLogsToDeveloper;

}
catch (Exception ex)
{
Loggers.SystemLogger.Error(ex.Message + " {@Error}", ex);
}
PreloadServices();

}
static IAsyncPolicy<HttpResponseMessage> GetWebhookRetryPolicy()
catch (Exception ex)
{
var delay = Backoff.DecorrelatedJitterBackoffV2(medianFirstRetryDelay: TimeSpan.FromSeconds(1), retryCount: 5);
Loggers.SystemLogger.Error(ex.Message + " {@Error}", ex);
}
PreloadServices();

}
static IAsyncPolicy<HttpResponseMessage> GetWebhookRetryPolicy()
{
var delay = Backoff.DecorrelatedJitterBackoffV2(medianFirstRetryDelay: TimeSpan.FromSeconds(1), retryCount: 5);

return HttpPolicyExtensions
.HandleTransientHttpError()
.OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound)
.WaitAndRetryAsync(delay);
return HttpPolicyExtensions
.HandleTransientHttpError()
.OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound)
.WaitAndRetryAsync(delay);
}
private static void PreloadServices()
{
try
{
var context = Program.AppInstance.Services.GetRequiredService<NotificationGenerationService>();
}
private static void PreloadServices()
catch (Exception ex)
{
try
{
var context = Program.AppInstance.Services.GetRequiredService<NotificationGenerationService>();
}
catch (Exception ex)
Loggers.SystemLogger.Error(ex.Message + " {@Error}", ex);
}
try
{
if (ApplicationInfo.installationCompleted)
{
Loggers.SystemLogger.Error(ex.Message + " {@Error}", ex);
var context = Program.AppInstance.Services.GetRequiredService<UserSeederService>();
}
try
{
if (ApplicationInfo.installationCompleted)
{
var context = Program.AppInstance.Services.GetRequiredService<UserSeederService>();
}

}
catch (Exception ex)
}
catch (Exception ex)
{
Loggers.SystemLogger.Error(ex.Message + " {@Error}", ex);
}
try
{
if (ApplicationInfo.installationCompleted)
{
Loggers.SystemLogger.Error(ex.Message + " {@Error}", ex);
var context = Program.AppInstance.Services.GetRequiredService<UpdateService>();
context.Initialize();
}
try
{
if (ApplicationInfo.installationCompleted)
{
var context = Program.AppInstance.Services.GetRequiredService<UpdateService>();
context.Initialize();
}

}
catch (Exception ex)
{
Loggers.SystemLogger.Error(ex.Message + " {@Error}", ex);
}
try
}
catch (Exception ex)
{
Loggers.SystemLogger.Error(ex.Message + " {@Error}", ex);
}
try
{
if (ApplicationInfo.installationCompleted)
{
if (ApplicationInfo.installationCompleted)
{
var context = Program.AppInstance.Services.GetRequiredService<WebHookPublisher>();

}
var context = Program.AppInstance.Services.GetRequiredService<WebHookPublisher>();

}
catch (Exception ex)
{
Loggers.SystemLogger.Error(ex.Message + " {@Error}", ex);
}

}
catch (Exception ex)
{
Loggers.SystemLogger.Error(ex.Message + " {@Error}", ex);
}

}
}
}
20 changes: 2 additions & 18 deletions BLAZAMCommon/Data/ApplicationInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,19 +78,7 @@ public class ApplicationInfo
/// </summary>
public static IServiceProvider services;

/// <summary>
/// A symmetric key version of the encryption key
/// for use with API signing
/// </summary>
public static SymmetricSecurityKey tokenKey { get {

var keyString = configuration.GetValue<string>("EncryptionKey");
var keyBytes = Encoding.ASCII.GetBytes(keyString);
return new SymmetricSecurityKey(keyBytes);
} }




/// <summary>
/// The running Blazam version
/// </summary>
Expand Down Expand Up @@ -173,11 +161,7 @@ public static bool installationCompleted
/// Unique ID for this machine
/// </summary>
public Guid InstallationId { get => installationId; set => installationId = value; }
/// <summary>
/// A symmetric key version of the encryption key
/// for use with API signing
/// </summary>
public SymmetricSecurityKey TokenKey { get => tokenKey; }


/// <summary>
/// Use only for UnitTests
Expand Down
8 changes: 7 additions & 1 deletion BLAZAMGui/Layouts/AppUserApiModalContent.razor
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
@inherits AppModalContent
<MudLink Href="/swagger" Target="_blank">
<MudAlert Severity="Severity.Info" Icon="@Icons.Material.Filled.BookOnline">@AppLocalization["Click to view API documentation"].</MudAlert>
<MudAlert Severity="Severity.Info"
Variant="Variant.Filled"
Class="mb-5"

Icon="@Icons.Material.Filled.BookOnline">
@AppLocalization["Click to view API documentation"].
</MudAlert>
</MudLink>
<UserApiTokenDataGrid @ref=apiDataGrid/>
@code{
Expand Down
6 changes: 5 additions & 1 deletion BLAZAMGui/UI/Outputs/UserApiTokenDataGrid.razor
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@
<MudSelectItem Value="TimeSpan.FromDays(1000*365)">@AppLocalization["Forever"]</MudSelectItem>

</MudSelect>
<MudButton Variant="Variant.Filled" Color="Color.Success" OnClick="@GenerateToken">@AppLocalization["Generate New API Token"]</MudButton>
<MudButton Variant="Variant.Filled"
Color="Color.Success"
OnClick="@GenerateToken">
@AppLocalization["Generate New API Token"]
</MudButton>
</SettingsField>
@code {
List<ApiToken> _tokens = new();
Expand Down
8 changes: 5 additions & 3 deletions BLAZAMServices/JwtTokenService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@ namespace BLAZAM.Services
{
public class JwtTokenService
{
private readonly IEncryptionService _encryptionService;
private readonly ICurrentUserStateService _currentUserStateService;
private readonly ApplicationInfo _applicationInfo;

public JwtTokenService(ApplicationInfo applicationInfo, ICurrentUserStateService currentUserStateService)
public JwtTokenService(IEncryptionService encryptionService, ApplicationInfo applicationInfo, ICurrentUserStateService currentUserStateService)

{
_encryptionService = encryptionService;
_currentUserStateService = currentUserStateService;
_applicationInfo = applicationInfo;
}
Expand Down Expand Up @@ -51,7 +53,7 @@ public string GenerateJwtToken(TimeSpan? lifetime=null)
IssuedAt = DateTime.UtcNow,
Issuer = DatabaseCache.ApplicationSettings.AppName,
Expires = (DateTime.UtcNow+lifetime.Value), // Set expiration
SigningCredentials = new SigningCredentials(_applicationInfo.TokenKey, SecurityAlgorithms.HmacSha256Signature)
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(Encryption.Instance.Key), SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
var jwtToken = tokenHandler.WriteToken(token);
Expand All @@ -72,7 +74,7 @@ public ClaimsPrincipal DecodeJwtToken(string
tokenHandler.ValidateToken(token, new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = _applicationInfo.TokenKey,
IssuerSigningKey = new SymmetricSecurityKey(Encryption.Instance.Key),
ValidateIssuer = false,
ValidateAudience = false,
ClockSkew = TimeSpan.Zero // Set clock skew to zero
Expand Down