Skip to content

Commit

Permalink
[TeamHub] Initial configuration for IdentityServer
Browse files Browse the repository at this point in the history
  • Loading branch information
leandromonaco committed Apr 22, 2022
1 parent f03c1cb commit 30f40db
Show file tree
Hide file tree
Showing 13 changed files with 238 additions and 66 deletions.
15 changes: 8 additions & 7 deletions TeamHub/src/TeamHub.API/Controllers/EmployeesController.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Authorization;
using Microsoft.EntityFrameworkCore;
using System.Text.Json;
using TeamHub.API.Database;

Expand All @@ -9,14 +10,14 @@ public static class EmployeesController
public static void MapEmployeesControllerEndpoints(this WebApplication app, TeamHubDatabaseContext dataContext, JsonSerializerOptions jsonSerializerOptions)
{
//All
app.MapGet("/employees", async () =>
app.MapGet("/employees", [Authorize] async () =>
{
var employees = await dataContext.Employees.Include(e => e.Specialization).ToListAsync();
return JsonSerializer.Serialize(employees, jsonSerializerOptions);
});
}).RequireCors("MyAllowSpecificOrigins");

//Create
app.MapPost("/employees", async (Employee employee) =>
app.MapPost("/employees", [Authorize] async (Employee employee) =>
{
employee.Id = Guid.NewGuid();
await dataContext.Employees.AddAsync(employee);
Expand All @@ -25,15 +26,15 @@ public static void MapEmployeesControllerEndpoints(this WebApplication app, Team
});

//Retrieve
app.MapGet("/employees/{id}", async (Guid id) =>
app.MapGet("/employees/{id}", [Authorize] async (Guid id) =>
{
var employee = await dataContext.Employees.Include(e => e.Specialization)
.Include(e => e.ReportsToNavigation).SingleOrDefaultAsync(x => x.Id == id);
return JsonSerializer.Serialize(employee, jsonSerializerOptions);
});

//Update
app.MapPut("/employees", async (Employee employee) =>
app.MapPut("/employees", [Authorize] async (Employee employee) =>
{
var emp = await dataContext.Employees.FindAsync(employee.Id);
if (emp != null)
Expand All @@ -46,7 +47,7 @@ public static void MapEmployeesControllerEndpoints(this WebApplication app, Team
});

//Delete
app.MapDelete("/employees/{id}", async (Guid id) =>
app.MapDelete("/employees/{id}", [Authorize] async (Guid id) =>
{
var employee = await dataContext.Employees.FindAsync(id);
if (employee != null)
Expand Down
45 changes: 45 additions & 0 deletions TeamHub/src/TeamHub.API/Controllers/IdentityController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using IdentityModel.Client;
using Microsoft.AspNetCore.Authorization;

namespace TeamHub.API.Controllers
{
public static class IdentityController
{
public static void MapIdentityControllerEndpoints(this WebApplication app)
{
//All
app.MapGet("/token", [AllowAnonymous] async () =>
{
// discover endpoints from metadata
var client = new HttpClient();
var disco = await client.GetDiscoveryDocumentAsync("https://localhost:5001");
//if (disco.IsError)
//{
// Console.WriteLine(disco.Error);
// return;
//}
// request token
var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
{
Address = disco.TokenEndpoint,
ClientId = "client",
ClientSecret = "secret",
Scope = "api1"
});
//if (tokenResponse.IsError)
//{
// Console.WriteLine(tokenResponse.Error);
// return;
//}
return tokenResponse.AccessToken;
}).RequireCors("MyAllowSpecificOrigins");



}
}
}
82 changes: 78 additions & 4 deletions TeamHub/src/TeamHub.API/Program.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,89 @@
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using System.Text.Json;
using System.Text.Json.Serialization;
using TeamHub.API.Controllers;
using TeamHub.API.Database;
using TeamHub.Core;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddCors();

//Configure Swagger with Bearer Token authentication
builder.Services.AddSwaggerGen(o =>
{
//o.SwaggerDoc("v1", info);
o.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme()
{
Name = "Authorization",
Type = SecuritySchemeType.ApiKey,
Scheme = "Bearer",
BearerFormat = "JWT",
In = ParameterLocation.Header,
Description = "JSON Web Token based security",
});
o.AddSecurityRequirement(new OpenApiSecurityRequirement()
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
new string[] {}
}
});
});

//CORS Support
builder.Services.AddCors(options =>
options.AddPolicy("MyAllowSpecificOrigins",
builder =>
{
builder.WithOrigins("https://localhost:4200")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials();
}));

//Authentication with Identity Server
builder.Services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options =>
{
options.Authority = "https://localhost:5001";
//https://devblogs.microsoft.com/dotnet/jwt-validation-and-authorization-in-asp-net-core/
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
ValidateIssuer = true,
ValidIssuer = "http://localhost:5000/",
ValidateAudience = false,
IssuerSigningKey = new X509SecurityKey(Security.LoadCertificate())
};
});

//Authorization with Identity Server
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("ApiScope", policy =>
{
policy.RequireAuthenticatedUser();
policy.RequireClaim("scope", "api1");
});
});

var app = builder.Build();

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

//https://github.com/dotnet/aspnetcore/issues/35904
var jsonSerializerOptions = new JsonSerializerOptions()
{
Expand All @@ -29,12 +100,15 @@
app.UseSwaggerUI();
}

app.UseHttpsRedirection();
app.UseCors("MyAllowSpecificOrigins");

app.UseCors(policy => policy.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin());
app.UseHttpsRedirection();

TeamHubDatabaseContext dataContext = new TeamHubDatabaseContext();

//Controllers
app.MapEmployeesControllerEndpoints(dataContext, jsonSerializerOptions);
app.MapIdentityControllerEndpoints();

app.Run();

6 changes: 6 additions & 0 deletions TeamHub/src/TeamHub.API/TeamHub.API.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="IdentityModel" Version="6.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand All @@ -15,4 +17,8 @@
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\TeamHub.Core\TeamHub.Core.csproj" />
</ItemGroup>

</Project>
7 changes: 0 additions & 7 deletions TeamHub/src/TeamHub.Core/Class1.cs

This file was deleted.

16 changes: 16 additions & 0 deletions TeamHub/src/TeamHub.Core/Security.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System.Security.Cryptography.X509Certificates;

namespace TeamHub.Core
{
public static class Security
{
public static X509Certificate2 LoadCertificate()
{
X509Store store = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
store.Open(OpenFlags.MaxAllowed);
X509Certificate2 cert = store.Certificates.Cast<X509Certificate2>().FirstOrDefault(c => c.Subject == "CN=localhost");
store.Close();
return cert;
}
}
}
40 changes: 26 additions & 14 deletions TeamHub/src/TeamHub.Identity/Config.cs
Original file line number Diff line number Diff line change
@@ -1,21 +1,33 @@
using Duende.IdentityServer.Models;

namespace TeamHub.Identity
namespace IdentityServer;

public static class Config
{
public static class Config
{
public static IEnumerable<IdentityResource> IdentityResources =>
new IdentityResource[]
public static IEnumerable<ApiScope> ApiScopes =>
new List<ApiScope>
{
new ApiScope("api1", "My API")
};

public static IEnumerable<Client> Clients =>
new List<Client>
{
new Client
{
new IdentityResources.OpenId()
};
ClientId = "client",

// no interactive user, use the clientid/secret for authentication
AllowedGrantTypes = GrantTypes.ClientCredentials,

public static IEnumerable<ApiScope> ApiScopes =>
new ApiScope[]
{ };
// secret for authentication
ClientSecrets =
{
new Secret("secret".Sha256())
},

public static IEnumerable<Client> Clients =>
new Client[]
{ };
}
// scopes that client has access to
AllowedScopes = { "api1" }
}
};
}
21 changes: 11 additions & 10 deletions TeamHub/src/TeamHub.Identity/HostingExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
using IdentityServer;
using Serilog;
using TeamHub.Core;

namespace TeamHub.Identity
{
internal static class HostingExtensions
{
public static WebApplication ConfigureServices(this WebApplicationBuilder builder)
{
// uncomment if you want to add a UI
//builder.Services.AddRazorPages();
// Configuring IdentityServer
var x509 = Security.LoadCertificate();

builder.Services.AddIdentityServer(options =>
{
// https://docs.duendesoftware.com/identityserver/v6/fundamentals/resources/api_scopes#authorization-based-on-scopes
options.EmitStaticAudienceClaim = true;
})
.AddInMemoryIdentityResources(Config.IdentityResources)
.AddInMemoryApiScopes(Config.ApiScopes)
.AddInMemoryClients(Config.Clients);
builder.Services.AddIdentityServer()
.AddInMemoryApiScopes(Config.ApiScopes)
.AddInMemoryClients(Config.Clients)
.AddSigningCredential(x509)
.AddValidationKey(x509);

return builder.Build();
}



public static WebApplication ConfigurePipeline(this WebApplication app)
{
app.UseSerilogRequestLogging();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"profiles": {
"SelfHost": {
"TeamHub.Identity": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
Expand Down
8 changes: 8 additions & 0 deletions TeamHub/src/TeamHub.Identity/TeamHub.Identity.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,12 @@

<PackageReference Include="Serilog.AspNetCore" Version="4.1.0" />
</ItemGroup>

<ItemGroup>
<Folder Include="keys\" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\TeamHub.Core\TeamHub.Core.csproj" />
</ItemGroup>
</Project>
29 changes: 20 additions & 9 deletions TeamHub/src/TeamHub.Identity/appsettings.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
{
"Serilog": {
"MinimumLevel": {
"Default": "Debug",
"Override": {
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information",
"Microsoft.AspNetCore.Authentication": "Debug",
"System": "Warning"
}
"Serilog": {
"MinimumLevel": {
"Default": "Debug",
"Override": {
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information",
"Microsoft.AspNetCore.Authentication": "Debug",
"System": "Warning"
}
}
},
"Kestrel": {
"Endpoints": {
"HttpsInlineCertAndKeyFile": {
"Url": "https://localhost:5001",
"Certificate": {
"Path": "C:\\GitHub\\Workbench\\Misc\\SSL\\localhost.crt",
"KeyPath": "C:\\GitHub\\Workbench\\Misc\\SSL\\localhost.key"
}
}
}
}
}

0 comments on commit 30f40db

Please sign in to comment.