Skip to content

Commit 682ca96

Browse files
davidortinauCopilot
andcommitted
Remove Entra ID remnants, simplify to Identity-only auth (#87)
- Remove Microsoft.Identity.Web NuGet packages from API, Web, and WebApp - Remove Entra ID code paths from all Program.cs files (API, Web, WebApp) - Remove MsalAuthService.cs and AuthenticatedApiDelegatingHandler.cs - Remove AzureAd parameter definitions from AppHost - Remove AzureAd/Auth config sections from all appsettings files - Simplify LoginDisplay.razor to Identity-only auth UI - Simplify AppLib ServiceCollectionExtentions to always use IdentityAuthService - Clean AuthenticatedHttpMessageHandler of AzureAd:Scopes dependency - Fix test factories for new JWT Bearer scheme naming - Clean CI workflow of Auth:UseEntraId env var - All 16 tests passing Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 13990cb commit 682ca96

File tree

19 files changed

+77
-415
lines changed

19 files changed

+77
-415
lines changed

.github/workflows/ci.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ env:
1515
DOTNET_NOLOGO: true
1616
DOTNET_CLI_TELEMETRY_OPTOUT: true
1717
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
18-
Auth__UseEntraId: "false"
1918

2019
jobs:
2120
build:

src/SentenceStudio.Api/Program.cs

Lines changed: 16 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
using Microsoft.AspNetCore.Identity;
1212
using Microsoft.AspNetCore.Mvc;
1313
using Microsoft.Extensions.AI;
14-
using Microsoft.Identity.Web;
1514
using Microsoft.IdentityModel.Tokens;
1615
using OpenAI;
1716
using SentenceStudio.Api.Auth;
@@ -32,36 +31,6 @@
3231
// Add services to the container.
3332
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
3433
builder.Services.AddOpenApi();
35-
var useEntraId = builder.Configuration.GetValue<bool>("Auth:UseEntraId");
36-
37-
if (useEntraId)
38-
{
39-
builder.Services.AddAuthentication(Microsoft.Identity.Web.Constants.Bearer)
40-
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));
41-
42-
builder.Services.AddAuthorization(options =>
43-
{
44-
options.AddPolicy("RequireUserRead", policy =>
45-
policy.RequireScope("user.read"));
46-
options.AddPolicy("RequireUserWrite", policy =>
47-
policy.RequireScope("user.write"));
48-
options.AddPolicy("RequireAiAccess", policy =>
49-
policy.RequireScope("ai.access"));
50-
options.AddPolicy("RequireSyncReadWrite", policy =>
51-
policy.RequireScope("sync.readwrite"));
52-
});
53-
}
54-
else if (builder.Environment.IsDevelopment())
55-
{
56-
builder.Services.AddAuthentication(DevAuthHandler.SchemeName)
57-
.AddScheme<AuthenticationSchemeOptions, DevAuthHandler>(DevAuthHandler.SchemeName, _ => { });
58-
builder.Services.AddAuthorization();
59-
}
60-
else
61-
{
62-
throw new InvalidOperationException(
63-
"Entra ID authentication must be enabled in non-development environments. Set Auth:UseEntraId=true.");
64-
}
6534

6635
// ASP.NET Core Identity for local account management
6736
builder.Services.AddIdentityCore<ApplicationUser>(options =>
@@ -74,12 +43,12 @@
7443
.AddEntityFrameworkStores<ApplicationDbContext>()
7544
.AddDefaultTokenProviders();
7645

77-
// JWT Bearer validation for Identity-issued tokens
46+
// JWT Bearer authentication for Identity-issued tokens
7847
var jwtSigningKey = builder.Configuration["Jwt:SigningKey"];
7948
if (!string.IsNullOrWhiteSpace(jwtSigningKey))
8049
{
81-
builder.Services.AddAuthentication()
82-
.AddJwtBearer("IdentityJwt", options =>
50+
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
51+
.AddJwtBearer(options =>
8352
{
8453
options.TokenValidationParameters = new TokenValidationParameters
8554
{
@@ -95,6 +64,19 @@
9564
};
9665
});
9766
}
67+
else if (builder.Environment.IsDevelopment())
68+
{
69+
// Fallback to dev auth handler when no JWT key is configured
70+
builder.Services.AddAuthentication(DevAuthHandler.SchemeName)
71+
.AddScheme<AuthenticationSchemeOptions, DevAuthHandler>(DevAuthHandler.SchemeName, _ => { });
72+
}
73+
else
74+
{
75+
throw new InvalidOperationException(
76+
"Jwt:SigningKey must be configured in non-development environments.");
77+
}
78+
79+
builder.Services.AddAuthorization();
9880

9981
builder.Services.AddScoped<JwtTokenService>();
10082

src/SentenceStudio.Api/SentenceStudio.Api.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
<PrivateAssets>all</PrivateAssets>
1717
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1818
</PackageReference>
19-
<PackageReference Include="Microsoft.Identity.Web" Version="3.8.2" />
2019
</ItemGroup>
2120

2221
<ItemGroup>

src/SentenceStudio.Api/appsettings.Development.json

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,5 @@
44
"Default": "Information",
55
"Microsoft.AspNetCore": "Warning"
66
}
7-
},
8-
"Auth": {
9-
"UseEntraId": false
10-
},
11-
"AzureAd": {
12-
"Instance": "https://login.microsoftonline.com/",
13-
"TenantId": "49c0cd14-bc68-4c6d-b87b-9d65a56fa6df",
14-
"ClientId": "8c051bcf-bd3a-4051-9cd3-0556ba5df2d8",
15-
"Audience": "api://8c051bcf-bd3a-4051-9cd3-0556ba5df2d8"
167
}
178
}

src/SentenceStudio.AppHost/AppHost.cs

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,6 @@
66
var syncfusionkey = builder.AddParameter("syncfusionkey", secret: true);
77
var elevenlabskey = builder.AddParameter("elevenlabskey", secret: true);
88

9-
var azureAdTenantId = builder.AddParameter("AzureAdTenantId", secret: false);
10-
var azureAdClientId = builder.AddParameter("AzureAdClientId", secret: false);
11-
var azureAdAudience = builder.AddParameter("AzureAdAudience", secret: false);
12-
139
var postgres = builder.AddPostgres("db")
1410
.AddDatabase("sentencestudio");
1511

@@ -21,11 +17,7 @@
2117

2218
var api = builder.AddProject<SentenceStudio_Api>("api")
2319
.WithEnvironment("AI__OpenAI__ApiKey", openaikey)
24-
.WithEnvironment("ElevenLabsKey", elevenlabskey)
25-
.WithEnvironment("AzureAd__Instance", "https://login.microsoftonline.com/")
26-
.WithEnvironment("AzureAd__TenantId", azureAdTenantId)
27-
.WithEnvironment("AzureAd__ClientId", azureAdClientId)
28-
.WithEnvironment("AzureAd__Audience", azureAdAudience);
20+
.WithEnvironment("ElevenLabsKey", elevenlabskey);
2921
// Email (production only -- dev mode uses ConsoleEmailSender automatically):
3022
// .WithEnvironment("Email__SmtpHost", "<smtp-host>")
3123
// .WithEnvironment("Email__SmtpPort", "587")

src/SentenceStudio.AppLib/ServiceCollectionExtentions.cs

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -63,16 +63,7 @@ public static void AddSyncServices(this IServiceCollection services, string data
6363

6464
public static IServiceCollection AddAuthServices(this IServiceCollection services, IConfiguration configuration)
6565
{
66-
var useEntraId = configuration.GetValue<bool>("Auth:UseEntraId");
67-
68-
if (useEntraId)
69-
{
70-
services.AddSingleton<IAuthService, MsalAuthService>();
71-
}
72-
else
73-
{
74-
services.AddSingleton<IAuthService, IdentityAuthService>();
75-
}
66+
services.AddSingleton<IAuthService, IdentityAuthService>();
7667

7768
// Register a named HttpClient for auth endpoints (login, register, refresh).
7869
// Uses the same API base URL as other clients but without the auth handler

src/SentenceStudio.AppLib/Services/AuthenticatedHttpMessageHandler.cs

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,43 +2,35 @@
22
using System.Net.Http.Headers;
33
using System.Threading;
44
using System.Threading.Tasks;
5-
using Microsoft.Extensions.Configuration;
65
using Microsoft.Extensions.Logging;
76

87
namespace SentenceStudio.Services;
98

109
/// <summary>
1110
/// Delegating handler that attaches a Bearer token to outgoing requests.
12-
/// For Entra ID, uses configured scopes. For Identity auth (no scopes configured),
13-
/// requests the token with an empty scope array — IdentityAuthService returns the
14-
/// stored JWT regardless of scopes.
11+
/// Gets the token from IAuthService (IdentityAuthService).
1512
/// If token acquisition returns null, the request proceeds unauthenticated so the
1613
/// server's DevAuthHandler can handle it during development.
1714
/// </summary>
1815
public class AuthenticatedHttpMessageHandler : DelegatingHandler
1916
{
20-
private readonly string[] _defaultScopes;
2117
private readonly IAuthService _authService;
2218
private readonly ILogger<AuthenticatedHttpMessageHandler> _logger;
2319

2420
public AuthenticatedHttpMessageHandler(
2521
IAuthService authService,
26-
IConfiguration configuration,
2722
ILogger<AuthenticatedHttpMessageHandler> logger)
2823
{
2924
_authService = authService;
3025
_logger = logger;
31-
32-
_defaultScopes = configuration.GetSection("AzureAd:Scopes").Get<string[]>()
33-
?? Array.Empty<string>();
3426
}
3527

3628
protected override async Task<HttpResponseMessage> SendAsync(
3729
HttpRequestMessage request, CancellationToken cancellationToken)
3830
{
3931
try
4032
{
41-
var token = await _authService.GetAccessTokenAsync(_defaultScopes);
33+
var token = await _authService.GetAccessTokenAsync(Array.Empty<string>());
4234
if (!string.IsNullOrEmpty(token))
4335
{
4436
request.Headers.Authorization =

src/SentenceStudio.AppLib/Services/MsalAuthService.cs

Lines changed: 0 additions & 133 deletions
This file was deleted.

0 commit comments

Comments
 (0)