Skip to content

Commit 6a793fd

Browse files
authored
Merge pull request #293
* New React BFF v4 sample
1 parent 06af4e3 commit 6a793fd

36 files changed

Lines changed: 6262 additions & 0 deletions

BFF/v4/React/.vscode/launch.json

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"version": "0.2.0",
3+
"compounds": [
4+
{
5+
"name": "All",
6+
"configurations": ["API", "BFF"],
7+
"presentation": {
8+
"hidden": false,
9+
"group": "compunds",
10+
}
11+
},
12+
],
13+
"configurations": [
14+
{
15+
"name": "API",
16+
"type": "coreclr",
17+
"request": "launch",
18+
"preLaunchTask": "build-api",
19+
"program": "${workspaceFolder}/React.Api/bin/Debug/net8.0/React.Api.dll",
20+
"args": [],
21+
"cwd": "${workspaceFolder}/React.Api",
22+
"env": {
23+
"ASPNETCORE_ENVIRONMENT": "Development"
24+
},
25+
"console": "externalTerminal",
26+
},
27+
{
28+
"name": "BFF",
29+
"type": "coreclr",
30+
"request": "launch",
31+
"preLaunchTask": "build-bff",
32+
"program": "${workspaceFolder}/React.Bff/bin/Debug/net8.0/React.Bff.dll",
33+
"args": [],
34+
"cwd": "${workspaceFolder}/React.Bff",
35+
"env": {
36+
"ASPNETCORE_ENVIRONMENT": "Development"
37+
},
38+
"console": "externalTerminal",
39+
},
40+
]
41+
}

BFF/v4/React/.vscode/tasks.json

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
{
2+
"version": "2.0.0",
3+
"tasks": [
4+
{
5+
"label": "build",
6+
"type": "process",
7+
"command": "dotnet",
8+
"args": [
9+
"build",
10+
"${workspaceFolder}/React.sln",
11+
"/property:GenerateFullPaths=true",
12+
"/consoleloggerparameters:NoSummary"
13+
],
14+
"problemMatcher": "$msCompile"
15+
},
16+
{
17+
"label": "build-api",
18+
"type": "process",
19+
"command": "dotnet",
20+
"args": [
21+
"build",
22+
"${workspaceFolder}/React.Api/React.Api.csproj",
23+
"/property:GenerateFullPaths=true",
24+
"/consoleloggerparameters:NoSummary"
25+
],
26+
"problemMatcher": "$msCompile"
27+
},
28+
{
29+
"label": "build-bff",
30+
"type": "process",
31+
"command": "dotnet",
32+
"args": [
33+
"build",
34+
"${workspaceFolder}/React.Bff/React.Bff.csproj",
35+
"/property:GenerateFullPaths=true",
36+
"/consoleloggerparameters:NoSummary"
37+
],
38+
"problemMatcher": "$msCompile"
39+
},
40+
]
41+
42+
}

BFF/v4/React/React.Api/Program.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using React.Api;
2+
3+
var builder = WebApplication.CreateBuilder(args);
4+
5+
builder.Services.AddAuthentication("token")
6+
.AddJwtBearer("token", options =>
7+
{
8+
options.Authority = "https://demo.duendesoftware.com";
9+
options.Audience = "api";
10+
11+
options.MapInboundClaims = false;
12+
});
13+
14+
builder.Services.AddAuthorization(options =>
15+
{
16+
options.AddPolicy("ApiCaller", policy =>
17+
{
18+
policy.RequireClaim("scope", "api");
19+
});
20+
21+
options.AddPolicy("InteractiveUser", policy =>
22+
{
23+
policy.RequireClaim("sub");
24+
});
25+
});
26+
27+
var app = builder.Build();
28+
29+
app.UseHttpsRedirection();
30+
31+
app.MapGroup("/todos")
32+
.ToDoGroup()
33+
.RequireAuthorization("ApiCaller", "InteractiveUser");
34+
35+
36+
app.Run();
37+
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"$schema": "http://json.schemastore.org/launchsettings.json",
3+
"profiles": {
4+
"https": {
5+
"commandName": "Project",
6+
"dotnetRunMessages": true,
7+
"launchBrowser": false,
8+
"applicationUrl": "https://localhost:7001",
9+
"environmentVariables": {
10+
"ASPNETCORE_ENVIRONMENT": "Development"
11+
}
12+
}
13+
}
14+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<Nullable>enable</Nullable>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<InvariantGlobalization>true</InvariantGlobalization>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.1" />
12+
</ItemGroup>
13+
14+
</Project>
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
using Microsoft.AspNetCore.Http.Extensions;
2+
using System.Security.Claims;
3+
4+
namespace React.Api;
5+
6+
public static class TodoEndpointGroup
7+
{
8+
private static readonly List<ToDo> data = new List<ToDo>()
9+
{
10+
new ToDo { Id = ToDo.NewId(), Date = DateTimeOffset.UtcNow, Name = "Demo ToDo API", User = "2 (Bob Smith)" },
11+
new ToDo { Id = ToDo.NewId(), Date = DateTimeOffset.UtcNow.AddHours(1), Name = "Stop Demo", User = "2 (Bob Smith)" },
12+
new ToDo { Id = ToDo.NewId(), Date = DateTimeOffset.UtcNow.AddHours(4), Name = "Have Dinner", User = "1 (Alice Smith)" },
13+
};
14+
15+
public static RouteGroupBuilder ToDoGroup(this RouteGroupBuilder group)
16+
{
17+
// GET
18+
group.MapGet("/", () => data);
19+
group.MapGet("/{id}", (int id) =>
20+
{
21+
var item = data.FirstOrDefault(x => x.Id == id);
22+
});
23+
24+
// POST
25+
group.MapPost("/", (ToDo model, ClaimsPrincipal user, HttpContext context) =>
26+
{
27+
model.Id = ToDo.NewId();
28+
model.User = $"{user.FindFirst("sub")?.Value} ({user.FindFirst("name")?.Value})";
29+
30+
data.Add(model);
31+
32+
var url = new Uri($"{context.Request.GetEncodedUrl()}/{model.Id}");
33+
34+
return Results.Created(url, model);
35+
});
36+
37+
// PUT
38+
group.MapPut("/{id}", (int id, ToDo model, ClaimsPrincipal User) =>
39+
{
40+
var item = data.FirstOrDefault(x => x.Id == id);
41+
if (item == null) return Results.NotFound();
42+
43+
item.Date = model.Date;
44+
item.Name = model.Name;
45+
46+
return Results.NoContent();
47+
});
48+
49+
// DELETE
50+
group.MapDelete("/{id}", (int id) =>
51+
{
52+
var item = data.FirstOrDefault(x => x.Id == id);
53+
if (item == null) return Results.NotFound();
54+
55+
data.Remove(item);
56+
57+
return Results.NoContent();
58+
});
59+
60+
return group;
61+
}
62+
}
63+
64+
public class ToDo
65+
{
66+
static int _nextId = 1;
67+
public static int NewId()
68+
{
69+
return _nextId++;
70+
}
71+
72+
public int Id { get; set; }
73+
public DateTimeOffset Date { get; set; }
74+
public string? Name { get; set; }
75+
public string? User { get; set; }
76+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Information",
5+
"Microsoft.AspNetCore": "Warning"
6+
}
7+
}
8+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Information",
5+
"Microsoft.AspNetCore": "Warning"
6+
}
7+
},
8+
"AllowedHosts": "*"
9+
}

BFF/v4/React/React.Bff/Program.cs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// Copyright (c) Duende Software. All rights reserved.
2+
// Licensed under the MIT License. See LICENSE in the project root for license information.
3+
4+
using Duende.Bff;
5+
using Duende.Bff.DynamicFrontends;
6+
using Duende.Bff.Yarp;
7+
using React.Bff;
8+
9+
var builder = WebApplication.CreateBuilder(args);
10+
11+
builder.Services.AddBff()
12+
.AddRemoteApis()
13+
.ConfigureOpenIdConnect(options =>
14+
{
15+
options.Authority = "https://demo.duendesoftware.com";
16+
options.ClientId = "interactive.confidential";
17+
options.ClientSecret = "secret";
18+
options.ResponseType = "code";
19+
options.ResponseMode = "query";
20+
21+
options.GetClaimsFromUserInfoEndpoint = true;
22+
options.MapInboundClaims = false;
23+
options.SaveTokens = true;
24+
options.DisableTelemetry = true;
25+
26+
options.Scope.Clear();
27+
options.Scope.Add("openid");
28+
options.Scope.Add("profile");
29+
options.Scope.Add("api");
30+
options.Scope.Add("offline_access");
31+
32+
options.TokenValidationParameters = new()
33+
{
34+
NameClaimType = "name",
35+
RoleClaimType = "role"
36+
};
37+
})
38+
.ConfigureCookies(options =>
39+
{
40+
options.Cookie.Name = "__Host-bff";
41+
options.Cookie.SameSite = SameSiteMode.Strict;
42+
});
43+
44+
builder.Services.AddAuthentication(options =>
45+
{
46+
options.DefaultScheme = BffAuthenticationSchemes.BffCookie;
47+
options.DefaultChallengeScheme = BffAuthenticationSchemes.BffOpenIdConnect;
48+
options.DefaultSignOutScheme = BffAuthenticationSchemes.BffOpenIdConnect;
49+
});
50+
51+
builder.Services.AddAuthorization();
52+
53+
var app = builder.Build();
54+
55+
app.UseDefaultFiles();
56+
app.UseStaticFiles();
57+
58+
// Configure the HTTP request pipeline.
59+
60+
app.UseHttpsRedirection();
61+
app.UseAuthentication();
62+
app.UseBff();
63+
app.UseAuthorization();
64+
app.MapBffManagementEndpoints();
65+
66+
// Comment this out to use the external api
67+
app.MapGroup("/todos")
68+
.ToDoGroup()
69+
.RequireAuthorization()
70+
.AsBffApiEndpoint();
71+
72+
// Comment this in to use the external api
73+
//app.MapRemoteBffApiEndpoint("/todos", "https://localhost:7001/todos")
74+
// .RequireAccessToken(Duende.Bff.TokenType.User);
75+
76+
app.MapFallbackToFile("/index.html");
77+
78+
app.Run();
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"$schema": "http://json.schemastore.org/launchsettings.json",
3+
"profiles": {
4+
"https": {
5+
"commandName": "Project",
6+
"dotnetRunMessages": true,
7+
"launchBrowser": true,
8+
"applicationUrl": "https://localhost:6001",
9+
"environmentVariables": {
10+
"ASPNETCORE_ENVIRONMENT": "Development",
11+
"ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.SpaProxy"
12+
}
13+
}
14+
}
15+
}

0 commit comments

Comments
 (0)