Skip to content
This repository has been archived by the owner on Nov 23, 2024. It is now read-only.

Commit

Permalink
Add PDNDClientAssertionServiceExtensions | Some code refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
engineering87 committed Oct 7, 2024
1 parent 89da2b4 commit e2cb31d
Show file tree
Hide file tree
Showing 8 changed files with 140 additions and 86 deletions.
11 changes: 2 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,9 @@ To properly set up and use the Client Assertion Generator in your ASP.NET Core a
},
```

2. Read the configuration Settings:
2. Register Services:
```csharp
builder.Services.Configure<ClientAssertionConfig>(configuration.GetSection("ClientAssertionConfig"));
```

3. Register Services:
```csharp
builder.Services.AddSingleton<ClientAssertionConfig>();
builder.Services.AddScoped<IOAuth2Service, OAuth2Service>();
builder.Services.AddScoped<IClientAssertionGenerator, ClientAssertionGeneratorService>();
builder.Services.AddPDNDClientAssertionServices();
```

Then you can use `ClientAssertionGeneratorService`, which provides the following methods:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,18 @@
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Options" Version="8.0.2" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.8.1" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\PDNDClientAssertionGenerator\PDNDClientAssertionGenerator.csproj" />
</ItemGroup>

<ItemGroup>
<Content Update="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>

</Project>
57 changes: 31 additions & 26 deletions src/PDNDClientAssertionGenerator.Api/Program.cs
Original file line number Diff line number Diff line change
@@ -1,39 +1,44 @@
// (c) 2024 Francesco Del Re <francesco.delre.87@gmail.com>
// This code is licensed under MIT license (see LICENSE.txt for details)
using PDNDClientAssertionGenerator.Configuration;
using PDNDClientAssertionGenerator.Interfaces;
using PDNDClientAssertionGenerator.Services;
using PDNDClientAssertionGenerator.Middleware;

var builder = WebApplication.CreateBuilder(args);
internal class Program
{
private static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var configuration = builder.Configuration;
var configuration = builder.Configuration;

// Add configuration PDNDClientAssertionGenerator
builder.Services.Configure<ClientAssertionConfig>(configuration.GetSection("ClientAssertionConfig"));
builder.Services.AddSingleton<ClientAssertionConfig>();
builder.Services.AddScoped<IOAuth2Service, OAuth2Service>();
builder.Services.AddScoped<IClientAssertionGenerator, ClientAssertionGeneratorService>();
// Add configuration PDNDClientAssertionGenerator
//builder.Services.Configure<ClientAssertionConfig>(configuration.GetSection("ClientAssertionConfig"));
//builder.Services.AddSingleton<ClientAssertionConfig>();
//builder.Services.AddScoped<IOAuth2Service, OAuth2Service>();
//builder.Services.AddScoped<IClientAssertionGenerator, ClientAssertionGeneratorService>();
builder.Services.AddPDNDClientAssertionServices();

var app = builder.Build();
var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}

app.UseHttpsRedirection();
app.UseHttpsRedirection();

app.UseAuthorization();
app.UseAuthorization();

app.MapControllers();
app.MapControllers();

app.Run();
app.Run();
}
}
22 changes: 11 additions & 11 deletions src/PDNDClientAssertionGenerator.Api/appsettings.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
{
"ClientAssertionConfig": {
"ServerUrl": "",
"KeyId": "",
"Algorithm": "",
"Type": "",
"ClientId": "",
"Issuer": "",
"Subject": "",
"Audience": "",
"PurposeId": "",
"KeyPath": "",
"Duration": ""
"ServerUrl": "https://test-server-url.com",
"KeyId": "ZmYxZGE2YjQtMzY2Yy00NWI5LThjNGItMDJmYmQyZGIyMmZh",
"Algorithm": "RS256",
"Type": "at+jwt",
"ClientId": "9b361d49-33f4-4f1e-a88b-4e12661f2309",
"Issuer": "interop.pagopa.it",
"Subject": "9b361d49-33f4-4f1e-a88b-4e12661f2309",
"Audience": "https://erogatore.example/ente-example/v1",
"PurposeId": "1b361d49-33f4-4f1e-a88b-4e12661f2300",
"KeyPath": "/path/",
"Duration": "600"
},
"Logging": {
"LogLevel": {
Expand Down
57 changes: 22 additions & 35 deletions src/PDNDClientAssertionGenerator.Tests/OAuth2ServiceTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// (c) 2024 Francesco Del Re <francesco.delre.87@gmail.com>
// This code is licensed under MIT license (see LICENSE.txt for details)
using Microsoft.Extensions.Options;
using Moq;
using Moq.Protected;
using PDNDClientAssertionGenerator.Configuration;
Expand All @@ -10,51 +11,37 @@ namespace PDNDClientAssertionGenerator.Tests
{
public class OAuth2ServiceTests
{
private readonly ClientAssertionConfig _config;
private readonly OAuth2Service _oauth2Service;
private readonly Mock<HttpMessageHandler> _handlerMock;
private readonly Mock<IOptions<ClientAssertionConfig>> _mockOptions;

public OAuth2ServiceTests()
{
// ClientAssertionConfig Mock configuration
_config = new ClientAssertionConfig
// Set up the HttpMessageHandler mock
_handlerMock = new Mock<HttpMessageHandler>();

// Set up the ClientAssertionConfig object
var clientAssertionConfig = new ClientAssertionConfig
{
Duration = 60,
ServerUrl = "https://test-server-url.com",
ClientId = "test-client-id",
KeyId = "test-key-id",
Algorithm = "RS256",
Type = "JWT",
Issuer = "test-issuer",
Subject = "test-subject",
Audience = "test-audience",
PurposeId = "test-purpose-id",
KeyPath = "path/to/key",
Issuer = "issuer",
Subject = "subject",
Audience = "audience",
PurposeId = "purposeId",
ClientId = "clientId",
ServerUrl = "https://mocked-server-url/token"
Duration = 60 // token duration in minutes
};

// HttpMessageHandler Mock
_handlerMock = new Mock<HttpMessageHandler>(MockBehavior.Strict);
var httpClient = new HttpClient(_handlerMock.Object);

// OAuth2Service instance
_oauth2Service = new OAuth2Service(_config);
}

[Fact]
public async Task RequestAccessTokenAsync_ReturnsValidTokenResponse()
{
// Arrange: HTTP Mock
_handlerMock
.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>()
)
.ReturnsAsync(new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent("{\"access_token\":\"valid_token\",\"expires_in\":3600}"),
});
// Create the mock IOptions<ClientAssertionConfig> instance
_mockOptions = new Mock<IOptions<ClientAssertionConfig>>();
_mockOptions.Setup(o => o.Value).Returns(clientAssertionConfig);

var exception = await Assert.ThrowsAsync<HttpRequestException>(() => _oauth2Service.RequestAccessTokenAsync("valid_client_assertion"));
// Initialize OAuth2Service with the mocked IOptions<ClientAssertionConfig>
_oauth2Service = new OAuth2Service(_mockOptions.Object);
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using PDNDClientAssertionGenerator.Configuration;
using PDNDClientAssertionGenerator.Interfaces;
using PDNDClientAssertionGenerator.Services;

namespace PDNDClientAssertionGenerator.Middleware
{
public static class PDNDClientAssertionServiceExtensions
{
/// <summary>
/// Configures the services required for the PDND Client Assertion process.
/// This method sets up the configuration for `ClientAssertionConfig` and registers necessary services.
/// </summary>
/// <param name="services">The IServiceCollection to which the services are added.</param>
/// <returns>The updated IServiceCollection instance.</returns>
public static IServiceCollection AddPDNDClientAssertionServices(this IServiceCollection services)
{
// Use ConfigurationManager to load the configuration file (appsettings.json)
var configuration = new ConfigurationManager()
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) // Load configuration
.Build();

// Ensure that the configuration contains required sections and values
var configSection = configuration.GetSection("ClientAssertionConfig");
if (!configSection.Exists())
{
throw new InvalidOperationException("Missing 'ClientAssertionConfig' section in appsettings.json.");
}

// Register ClientAssertionConfig as a singleton using the IOptions pattern
services.Configure<ClientAssertionConfig>(config =>
{
// Copy values from the configuration file into the ClientAssertionConfig model
config.ClientId = configuration["ClientAssertionConfig:ClientId"];
config.ServerUrl = configuration["ClientAssertionConfig:ServerUrl"];
config.KeyId = configuration["ClientAssertionConfig:KeyId"];
config.Algorithm = configuration["ClientAssertionConfig:Algorithm"];
config.Type = configuration["ClientAssertionConfig:Type"];
config.Issuer = configuration["ClientAssertionConfig:Issuer"];
config.Subject = configuration["ClientAssertionConfig:Subject"];
config.Audience = configuration["ClientAssertionConfig:Audience"];
config.PurposeId = configuration["ClientAssertionConfig:PurposeId"];
config.KeyPath = configuration["ClientAssertionConfig:KeyPath"];
config.Duration = int.Parse(configuration["ClientAssertionConfig:Duration"]);
});

// Register OAuth2Service and ClientAssertionGeneratorService as scoped services
services.AddScoped<IOAuth2Service, OAuth2Service>();
services.AddScoped<IClientAssertionGenerator, ClientAssertionGeneratorService>();

// Return the updated service collection
return services;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,14 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.1.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.1.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Options" Version="8.0.2" />
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.1.1" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.1.1" />
<PackageReference Include="System.Text.Json" Version="8.0.4" />
</ItemGroup>

Expand Down
6 changes: 3 additions & 3 deletions src/PDNDClientAssertionGenerator/Services/OAuth2Service.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// (c) 2024 Francesco Del Re <francesco.delre.87@gmail.com>
// This code is licensed under MIT license (see LICENSE.txt for details)
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using PDNDClientAssertionGenerator.Configuration;
using PDNDClientAssertionGenerator.Interfaces;
Expand All @@ -8,7 +9,6 @@
using System.IdentityModel.Tokens.Jwt;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text.Json;

namespace PDNDClientAssertionGenerator.Services
Expand All @@ -18,9 +18,9 @@ public class OAuth2Service : IOAuth2Service
private readonly ClientAssertionConfig _config;

// Constructor for OAuth2Service, takes a configuration object.
public OAuth2Service(ClientAssertionConfig config)
public OAuth2Service(IOptions<ClientAssertionConfig> config)
{
_config = config ?? throw new ArgumentNullException(nameof(config));
_config = config.Value ?? throw new ArgumentNullException(nameof(config));
}

// Asynchronously generates a client assertion JWT token.
Expand Down

0 comments on commit e2cb31d

Please sign in to comment.