Skip to content

Commit

Permalink
Merge pull request #680 from Blazam-App/v1-2-1
Browse files Browse the repository at this point in the history
Fix for error creating API tokens with a short encryption seed string
  • Loading branch information
jacobsen9026 authored Dec 1, 2024
2 parents 05be8f7 + ad92ba1 commit 1b4c6ef
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 114 deletions.
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

0 comments on commit 1b4c6ef

Please sign in to comment.