Skip to content

Commit

Permalink
feat: add auth option
Browse files Browse the repository at this point in the history
  • Loading branch information
EnisMulic committed Nov 17, 2023
1 parent a56fd48 commit 9d57c3d
Show file tree
Hide file tree
Showing 14 changed files with 172 additions and 65 deletions.
4 changes: 4 additions & 0 deletions .template.config/dotnetcli.host.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
"Database": {
"longName": "database",
"shortName": "db"
},
"Auth": {
"longName": "auth",
"shortName": "oa"
}
}
}
12 changes: 11 additions & 1 deletion .template.config/ide.host.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"text": "Git Repository Host"
},
"description": {
"text": "Select the git respository host"
"text": "Select the git repository host"
},
"isVisible": true
},
Expand All @@ -22,6 +22,16 @@
"text": "Select the database"
},
"isVisible": true
},
{
"id": "Auth",
"name": {
"text": "Auth"
},
"description": {
"text": "Select the auth provider"
},
"isVisible": true
}
]
}
24 changes: 24 additions & 0 deletions .template.config/template.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,26 @@
"UseDatabase": {
"type": "computed",
"value": "(Database != \"None\")"
},
"Auth": {
"type": "parameter",
"datatype": "choice",
"choices": [
{
"choice": "Entra",
"description": "Use Microsoft Entra as the auth provider"
},
{
"choice": "None",
"description": ""
}
],
"defaultValue": "None",
"description": "The type of auth provider to use"
},
"UseEntra": {
"type": "computed",
"value": "(Auth == \"Entra\")"
}
},
"sources": [
Expand Down Expand Up @@ -130,6 +150,10 @@
"appsettings.PostgreSql.json": "appsettings.Development.json",
"docker-compose.postgresql.yml": "docker-compose.yml"
}
},
{
"condition": "(!UseEntra)",
"exclude": ["**MicrosoftEntra**", "**Scopes**"]
}
]
}
Expand Down
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ dotnet new vsma --name ProjectName
dotnet new vsma --name [ProjectName]
[-gh|--git-host [Github|AzureDevOps|None]]
[-db|--database [MsSql|PostgreSql|None]]
[-oa|--auth [Entra|None]]
```

### Options
Expand All @@ -39,6 +40,9 @@ dotnet new vsma --name [ProjectName]
Choose the platform you will host your projects git repository, this will give you a base CI workflow, pull request template, and anything specific to the platform that might be of use. The default value is `None`.
- `-db|--database [MsSql|PostgreSql|None]`
Choose what database to use for your project. The default is `None`
- `-oa|--auth [Entra|None]`
Choose a auth provider to use for your project.
The default is `None` which will configure a JwtBearer Auth that you can use with `dotnet user-jwts`.

## Configuration

Expand All @@ -57,7 +61,7 @@ To test/develop the template with specific options add a `<DefineConstants>` blo
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>

<AnalysisLevel>7-recommended</AnalysisLevel>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>

Expand All @@ -81,9 +85,9 @@ When you run the application the database will be created (if it doesn't exist)

To run the migrations you will need to add the following flags to your ef commands.

* `-p | --project src/Application`
* `-s | --startup-project src/Api`
* `-o | --output-dir Infrastructure/Persistance/Migrations`
- `-p | --project src/Application`
- `-s | --startup-project src/Api`
- `-o | --output-dir Infrastructure/Persistance/Migrations`

For example, to add a new migration:

Expand Down
3 changes: 3 additions & 0 deletions src/Api/Api.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@

<PackageReference Include="Carter" Version="7.2.0" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.13" />
<!-- #if (UseEntra) -->
<PackageReference Include="Microsoft.Identity.Web" Version="2.15.3" />
<!-- #endif -->
</ItemGroup>
<ItemGroup>
<Folder Include="Services\" />
Expand Down
26 changes: 26 additions & 0 deletions src/Api/ConfigureMicrosoftEntraAuth.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using Api.Options;

using Microsoft.Extensions.Options;
using Microsoft.Identity.Web;

using Swashbuckle.AspNetCore.SwaggerGen;

namespace Api;

public static class ConfigureMicrosoftEntraAuth
{
public static IServiceCollection AddMicrosoftEntraAuth(this IServiceCollection services, IConfiguration configuration)
{
services.Configure<MicrosoftEntraOptions>(options =>
configuration.Bind(MicrosoftEntraOptions.SectionName, options));

services.AddTransient<IConfigureOptions<SwaggerGenOptions>, MicrosoftEntraSwaggerConfigurationOptions>();

services.AddAuthentication()
.AddMicrosoftIdentityWebApi(configuration, MicrosoftEntraOptions.SectionName)
.EnableTokenAcquisitionToCallDownstreamApi()
.AddInMemoryTokenCaches();

return services;
}
}
12 changes: 12 additions & 0 deletions src/Api/Options/MicrosoftEntraOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Microsoft.Identity.Web;

namespace Api.Options;

public class MicrosoftEntraOptions : MicrosoftIdentityOptions
{
public const string SectionName = "Entra";

public string BaseUrl => $"{Instance}/{TenantId}/oauth2/v2.0";
public string AuthorizationUrl => $"{BaseUrl}/authorize";
public string TokenUrl => $"{BaseUrl}/token";
}
55 changes: 55 additions & 0 deletions src/Api/Options/MicrosoftEntraSwaggerConfigurationOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using Application.Authorization;

using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Models;

using Swashbuckle.AspNetCore.SwaggerGen;

namespace Api.Options;

public class MicrosoftEntraSwaggerConfigurationOptions : IConfigureOptions<SwaggerGenOptions>
{
private readonly MicrosoftEntraOptions _options;

public MicrosoftEntraSwaggerConfigurationOptions(IOptions<MicrosoftEntraOptions> options)
{
_options = options.Value;
}

public void Configure(SwaggerGenOptions options)
{
var authorizationUrl = _options?.AuthorizationUrl ?? string.Empty;
var tokenUrl = _options?.TokenUrl ?? string.Empty;
var clientId = _options?.ClientId ?? string.Empty;

options.AddSecurityDefinition("Entra", new OpenApiSecurityScheme
{
Type = SecuritySchemeType.OAuth2,
Flows = new OpenApiOAuthFlows
{
AuthorizationCode = new OpenApiOAuthFlow
{
AuthorizationUrl = new Uri(authorizationUrl),
TokenUrl = new Uri(tokenUrl),
Scopes = new[] { $"{AuthorizationScopes.AccessAsUser}" }
.ToDictionary(p => $"api://{clientId}/{p}")
}
}
});
options.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Entra"
},
UnresolvedReference = true
},
Array.Empty<string>()
}
});
}
}
29 changes: 0 additions & 29 deletions src/Api/Options/SwaggerConfigurationOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@ namespace Api.Options;

public class SwaggerConfigurationOptions : IConfigureOptions<SwaggerGenOptions>
{
public SwaggerConfigurationOptions()
{
}

public void Configure(SwaggerGenOptions options)
{
Expand All @@ -36,31 +33,5 @@ public void Configure(SwaggerGenOptions options)
Array.Empty<string>()
}
});

//options.AddSecurityDefinition("aad-jwt", new OpenApiSecurityScheme
//{
// Type = SecuritySchemeType.OAuth2,
// Flows = new OpenApiOAuthFlows
// {
// AuthorizationCode = new OpenApiOAuthFlow
// {
// }
// }
//});
//options.AddSecurityRequirement(new OpenApiSecurityRequirement
//{
// {
// new OpenApiSecurityScheme
// {
// Reference = new OpenApiReference
// {
// Type = ReferenceType.SecurityScheme,
// Id = "aad-jwt"
// },
// UnresolvedReference = true
// },
// Array.Empty<string>()
// }
//});
}
}
19 changes: 15 additions & 4 deletions src/Api/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,19 @@
.AllowAnyHeader()
.AllowAnyMethod()));

builder.Services.AddAndConfigureProblemDetails();

builder.Services.AddTransient<IConfigureOptions<SwaggerGenOptions>, SwaggerConfigurationOptions>();

builder.Services.AddAndConfigureProblemDetails();

builder.Services.AddAuthorization();
builder.Services.AddAuthentication().AddJwtBearer();
#if UseEntra
builder.Services.AddMicrosoftEntraAuth(builder.Configuration);
#else
builder.Services.AddTransient<IConfigureOptions<SwaggerGenOptions>, SwaggerConfigurationOptions>();
builder.Services.AddAuthentication()
.AddJwtBearer();
#endif

builder.Services.AddAuthorization();

builder.Services.AddApiServices();
builder.Services.AddCommonServices();
Expand All @@ -66,6 +71,8 @@
db.Database.Migrate();
}

var microsoftIdentityOptions = app.Services.GetService<IOptions<MicrosoftEntraOptions>>()?.Value;

// Enable middleware to serve generated Swagger as a JSON endpoint.
app.UseSwagger();

Expand All @@ -74,6 +81,10 @@
{
options.SwaggerEndpoint("/swagger/v1/swagger.json", "v1");
options.RoutePrefix = string.Empty;
#if UseEntra
options.OAuthClientId(microsoftIdentityOptions?.ClientId);
options.OAuthUsePkce();
#endif
});

app.UseCors();
Expand Down
13 changes: 0 additions & 13 deletions src/Api/appsettings.MsSql.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,5 @@
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"Authentication": {
"Schemes": {
"Bearer": {
"ValidAudiences": [
"http://localhost:44986",
"https://localhost:44337",
"http://localhost:5191",
"https://localhost:7079"
],
"ValidIssuer": "dotnet-user-jwts"
}
}
}
}
13 changes: 0 additions & 13 deletions src/Api/appsettings.PostgreSql.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,5 @@
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"Authentication": {
"Schemes": {
"Bearer": {
"ValidAudiences": [
"http://localhost:44986",
"https://localhost:44337",
"http://localhost:5191",
"https://localhost:7079"
],
"ValidIssuer": "dotnet-user-jwts"
}
}
}
}
9 changes: 8 additions & 1 deletion src/Api/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,12 @@
}
}
},
"AllowedHosts": "*"
"AllowedHosts": "*",
"Entra": {
"Instance": "https://login.microsoftonline.com",
"ClientId": "",
"ClientSecret": "",
"Domain": "",
"TenantId": ""
}
}
6 changes: 6 additions & 0 deletions src/Application/Authorization/Scopes.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Application.Authorization;

public static class AuthorizationScopes
{
public const string AccessAsUser = "access_as_user";
}

0 comments on commit 9d57c3d

Please sign in to comment.