Skip to content

Commit

Permalink
(#173) Backend for Frontend (BFF)
Browse files Browse the repository at this point in the history
  • Loading branch information
phongnguyend committed Aug 6, 2023
1 parent b43dfbb commit 26f08e2
Show file tree
Hide file tree
Showing 95 changed files with 22,902 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,37 @@ public static void MigrateIdServerDb(this IApplicationBuilder app)
});
}

if (!context.Clients.Any(x => x.ClientId == "ReverseProxy.Yarp"))
{
clients.Add(new Client
{
ClientId = "ReverseProxy.Yarp",
ClientName = "ReverseProxy Yarp",
AllowedGrantTypes = GrantTypes.Code.Combines(GrantTypes.ResourceOwnerPassword),
RequirePkce = true,
RedirectUris =
{
"https://localhost:44348/signin-oidc"
},
PostLogoutRedirectUris =
{
"https://localhost:44348/signout-callback-oidc"
},
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"ClassifiedAds.WebAPI",
},
ClientSecrets =
{
new Secret("secret".Sha256()),
},
AllowOfflineAccess = true,
RequireConsent = true,
});
}

if (!context.Clients.Any(x => x.ClientId == "ClassifiedAds.WebMVC"))
{
clients.Add(new Client
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,43 @@ public async Task StartAsync(CancellationToken cancellationToken)
Type = OpenIddictConstants.ClientTypes.Confidential,
}, cancellationToken);

await UpsertClientApplication(manager, new OpenIddictApplicationDescriptor
{
ClientId = "ReverseProxy.Yarp",
ClientSecret = "secret",
DisplayName = "ReverseProxy Yarp",
RedirectUris =
{
new Uri("https://localhost:44348/signin-oidc")
},
PostLogoutRedirectUris =
{
new Uri("https://localhost:44348/signout-callback-oidc")
},
Permissions =
{
OpenIddictConstants.Permissions.Endpoints.Authorization,
OpenIddictConstants.Permissions.Endpoints.Token,
OpenIddictConstants.Permissions.Endpoints.Logout,

OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode,
OpenIddictConstants.Permissions.GrantTypes.ClientCredentials,
OpenIddictConstants.Permissions.GrantTypes.Password,
OpenIddictConstants.Permissions.GrantTypes.RefreshToken,

OpenIddictConstants.Permissions.Prefixes.Scope + "openid",
OpenIddictConstants.Permissions.Scopes.Profile,
OpenIddictConstants.Permissions.Prefixes.Scope + "ClassifiedAds.WebAPI",
OpenIddictConstants.Permissions.Prefixes.Scope + "offline_access",
OpenIddictConstants.Permissions.ResponseTypes.Code
},
Requirements =
{
OpenIddictConstants.Requirements.Features.ProofKeyForCodeExchange
},
Type = OpenIddictConstants.ClientTypes.Confidential,
}, cancellationToken);

await UpsertClientApplication(manager, new OpenIddictApplicationDescriptor
{
ClientId = "ClassifiedAds.WebMVC",
Expand Down
2 changes: 1 addition & 1 deletion src/ModularMonolith/ClassifiedAds.WebAPI/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@
"AllowedHosts": "*",
"CORS": {
"AllowAnyOrigin": false,
"AllowedOrigins": [ "http://localhost:4200", "http://localhost:3000", "http://localhost:8080" ]
"AllowedOrigins": [ "http://localhost:4200", "http://localhost:3000", "http://localhost:8080", "https://localhost:44348" ]
},
"SecurityHeaders": {
"Cache-Control": "no-cache, no-store, must-revalidate",
Expand Down
18 changes: 18 additions & 0 deletions src/UIs/bff/ReverseProxy.Yarp/ConfigurationOptions/AppSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
namespace ReverseProxy.Yarp.ConfigurationOptions
{
public class AppSettings
{
public OpenIdConnect? OpenIdConnect { get; set; }
}

public class OpenIdConnect
{
public string? Authority { get; set; }

public string? ClientId { get; set; }

public string? ClientSecret { get; set; }

public bool RequireHttpsMetadata { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using Microsoft.AspNetCore.Antiforgery;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Mvc;

namespace ReverseProxy.Yarp.Controllers
{
public class AuthenticationController : ControllerBase
{
private readonly IAntiforgery _forgeryService;

public AuthenticationController(IAntiforgery forgeryService)
{
_forgeryService = forgeryService;
}

[HttpGet("/login")]
public async Task LoginAsync(string returnUrl)
{
if (HttpContext.User.Identity?.IsAuthenticated ?? false)
{
Response.Redirect(Url.Content("~/").ToString());
}
else
{
await HttpContext.ChallengeAsync(OpenIdConnectDefaults.AuthenticationScheme, new AuthenticationProperties
{
RedirectUri = Url.IsLocalUrl(returnUrl) ? returnUrl : "/"
});
}
}

[HttpGet("/logout")]
public async Task LogoutAsync()
{
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
await HttpContext.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme);
}

[HttpGet("/userinfor")]
public IActionResult UserInfor()
{
if (HttpContext.User.Identity?.IsAuthenticated ?? false)
{
var tokens = _forgeryService.GetAndStoreTokens(HttpContext);
HttpContext.Response.Cookies.Append("PHONG-XSRF-TOKEN", tokens.RequestToken!, new CookieOptions { HttpOnly = false });

return Ok(new
{
Id = "",
FirstName = "Phong",
LastName = "Nguyen",
Timestamp = DateTimeOffset.Now
});
}
else
{
return Unauthorized();
}
}
}
}
99 changes: 99 additions & 0 deletions src/UIs/bff/ReverseProxy.Yarp/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
using Microsoft.AspNetCore.Antiforgery;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using ReverseProxy.Yarp.ConfigurationOptions;
using System.Net;
using System.Net.Http.Headers;
using Yarp.ReverseProxy.Transforms;

namespace Practical.ReverseProxy.Yarp
{
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
var services = builder.Services;
var configuration = builder.Configuration;

var appSettings = new AppSettings();
configuration.Bind(appSettings);

// Add the reverse proxy to capability to the server
var proxyBuilder = builder.Services.AddReverseProxy();

// Initialize the reverse proxy from the "ReverseProxy" section of configuration
proxyBuilder.LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"))
.AddTransforms(transformBuilderContext =>
{
transformBuilderContext.AddRequestTransform(async transformContext =>
{
var user = transformContext.HttpContext.User;
var token = await transformContext.HttpContext.GetTokenAsync(OpenIdConnectParameterNames.AccessToken);
transformContext.ProxyRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
});
});

services.AddControllers();

services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN");

services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
options.AccessDeniedPath = "/Authorization/AccessDenied";
})
.AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.Authority = appSettings.OpenIdConnect?.Authority;
options.ClientId = appSettings.OpenIdConnect?.ClientId;
options.ClientSecret = appSettings.OpenIdConnect?.ClientSecret;
options.ResponseType = "code";
options.Scope.Add("openid");
options.Scope.Add("profile");
options.Scope.Add("ClassifiedAds.WebAPI");
options.Scope.Add("offline_access");
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.RequireHttpsMetadata = appSettings.OpenIdConnect?.RequireHttpsMetadata ?? false;
});

var app = builder.Build();

app.MapControllers();

app.Use(async (context, next) =>
{
if (context.Request.Path.Value?.StartsWith("/api/", StringComparison.OrdinalIgnoreCase) ?? false)
{
try
{
var antiForgeryService = context.RequestServices.GetRequiredService<IAntiforgery>();
await antiForgeryService.ValidateRequestAsync(context);
}
catch (AntiforgeryValidationException)
{
context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
return;
}
}
await next(context);
});

app.MapReverseProxy();

app.MapForwarder("{**rest}", "http://localhost:3000");

app.Run();
}
}
}
28 changes: 28 additions & 0 deletions src/UIs/bff/ReverseProxy.Yarp/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:27422",
"sslPort": 44348
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Practical.ReverseProxy.Yarp": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "http://localhost:5122",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
14 changes: 14 additions & 0 deletions src/UIs/bff/ReverseProxy.Yarp/ReverseProxy.Yarp.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="7.0.9" />
<PackageReference Include="Yarp.ReverseProxy" Version="2.0.1" />
</ItemGroup>

</Project>
25 changes: 25 additions & 0 deletions src/UIs/bff/ReverseProxy.Yarp/ReverseProxy.Yarp.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.6.33829.357
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReverseProxy.Yarp", "ReverseProxy.Yarp.csproj", "{FA60DBAB-849A-45E6-B321-100F7A596B04}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{FA60DBAB-849A-45E6-B321-100F7A596B04}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FA60DBAB-849A-45E6-B321-100F7A596B04}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FA60DBAB-849A-45E6-B321-100F7A596B04}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FA60DBAB-849A-45E6-B321-100F7A596B04}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {52006721-468B-45C6-BABD-9722959E0210}
EndGlobalSection
EndGlobal
9 changes: 9 additions & 0 deletions src/UIs/bff/ReverseProxy.Yarp/appsettings.Development.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"DetailedErrors": true,
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
40 changes: 40 additions & 0 deletions src/UIs/bff/ReverseProxy.Yarp/appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"OpenIdConnect": {
"Authority": "https://localhost:44367",
"ClientId": "ReverseProxy.Yarp",
"ClientSecret": "secret",
"RequireHttpsMetadata": "true"
},
"ReverseProxy": {
"Routes": {
"route1": {
"ClusterId": "cluster1",
"Match": {
"Path": "/api/{**remainder}"
}
},
"route2": {
"ClusterId": "cluster1",
"Match": {
"Path": "/hubs/{**remainder}"
}
}
},
"Clusters": {
"cluster1": {
"Destinations": {
"destination1": {
"Address": "https://localhost:44312"
}
}
}
}
}
}
1 change: 1 addition & 0 deletions src/UIs/bff/reactjs/.eslintcache

Large diffs are not rendered by default.

Loading

0 comments on commit 26f08e2

Please sign in to comment.