Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>true</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="IdentityModel" Version="7.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.2" />
<PackageReference Include="Serilog.AspNetCore" Version="10.0.0" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright (c) Duende Software. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace TokenExchange.Api;

public class GreetingsController : ControllerBase
{
[HttpGet("{**catch-all}")]
public IActionResult Get()
{
string message;
var sub = User.FindFirst("sub");

if (!User.Identity.IsAuthenticated)
{
message = "Hello, anonymous caller";
}
else if (sub != null)
{
var userName = User.FindFirst("name");
message = $"Hello user, {userName.Value}";
}
else
{
var client = User.FindFirst("client_id");
message = $"Hello client, {client.Value}";
}

var response = new
{
path = Request.Path.Value,
message = message,
time = DateTime.UtcNow.ToString(),
headers = Request.Headers.ToDictionary(x => x.Key, x => string.Join(',', x))
};

return Ok(response);
}
}
79 changes: 79 additions & 0 deletions BFF/v4/BlazorAutoRendering/BlazorAutoRendering.Api/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright (c) Duende Software. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

using System.Diagnostics;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.IdentityModel.Tokens;
using Serilog;
using Serilog.Events;
using Serilog.Sinks.SystemConsole.Themes;

Console.Title = "Simple API";
Activity.DefaultIdFormat = ActivityIdFormat.W3C;

Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.MinimumLevel.Override("Microsoft.Hosting.Lifetime", LogEventLevel.Information)
.MinimumLevel.Override("System", LogEventLevel.Warning)
.MinimumLevel.Override("Microsoft.AspNetCore.Authentication", LogEventLevel.Information)
.Enrich.FromLogContext()
.WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}", theme: AnsiConsoleTheme.Code)
.CreateLogger();

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddSerilog();
builder.Services.AddControllers();

builder.Services.AddAuthentication("token")
.AddJwtBearer("token", options =>
{
options.Authority = "https://demo.duendesoftware.com";
options.MapInboundClaims = false;

options.TokenValidationParameters = new TokenValidationParameters()
{
ValidateAudience = false,
ValidTypes = new[] { "at+jwt" },

NameClaimType = "name",
RoleClaimType = "role"
};
});

builder.Services.AddAuthorization(options =>
{
options.AddPolicy("ApiCaller", policy =>
{
policy.RequireClaim("scope", "api");
});

options.AddPolicy("RequireInteractiveUser", policy =>
{
policy.RequireClaim("sub");
});
});

var app = builder.Build();

app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto | ForwardedHeaders.XForwardedHost,
});

app.UseSerilogRequestLogging();

if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}

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

app.MapControllers()
.RequireAuthorization("ApiCaller");

app.Run();
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"profiles": {
"Api": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "https://localhost:7001",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">

<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<NoDefaultLaunchSettingsFile>true</NoDefaultLaunchSettingsFile>
<StaticWebAssetProjectMode>Default</StaticWebAssetProjectMode>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="10.0.2" />
<PackageReference Include="Duende.BFF.Blazor.Client" Version="4.0.2" />
<PackageReference Include="Microsoft.Extensions.Http" Version="10.0.2" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
@using Duende.Bff.Blazor.Client
@using Microsoft.Extensions.Options

@rendermode InteractiveAuto

@inject IOptions<BffBlazorClientOptions> Options

<AuthorizeView>
<Authorized>
<strong>Hello, @context.User.Identity?.Name</strong>
<a class="nav-link btn btn-link" href="@BffLogoutUrl(context)">Log Out</a>
</Authorized>
<Authorizing>
<a class="nav-link btn btn-link disabled">Log in</a>
</Authorizing>
<NotAuthorized>
<a class="nav-link btn btn-link" href="bff/login">Log in</a>
</NotAuthorized>
</AuthorizeView>


@code {
string BffLogoutUrl(AuthenticationState context)
{
var logoutUrl = context.User.FindFirst(Constants.ClaimTypes.LogoutUrl);
return $"{Options.Value.StateProviderBaseAddress}{logoutUrl?.Value}";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@inject NavigationManager Navigation

@rendermode InteractiveAuto

@code {
protected override void OnInitialized()
{
var returnUrl = Uri.EscapeDataString("/" + Navigation.ToBaseRelativePath(Navigation.Uri));
Navigation.NavigateTo($"bff/login?returnUrl={returnUrl}", forceLoad: true);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
@using BlazorAutoRendering.Client.Components
@inherits LayoutComponentBase

<div class="page">
<div class="sidebar">
<NavMenu />
</div>

<main>
<div class="top-row px-4">
<LoginDisplay />
</div>

<article class="content px-4">
@Body
</article>
</main>
</div>

<div id="blazor-error-ui">
An unhandled error has occurred.
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
.page {
position: relative;
display: flex;
flex-direction: column;
}

main {
flex: 1;
}

.sidebar {
background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
}

.top-row {
background-color: #f7f7f7;
border-bottom: 1px solid #d6d5d5;
justify-content: flex-end;
height: 3.5rem;
display: flex;
align-items: center;
}

.top-row ::deep a, .top-row ::deep .btn-link {
white-space: nowrap;
margin-left: 1.5rem;
text-decoration: none;
}

.top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
text-decoration: underline;
}

.top-row ::deep a:first-child {
overflow: hidden;
text-overflow: ellipsis;
}

@media (max-width: 640.98px) {
.top-row {
justify-content: space-between;
}

.top-row ::deep a, .top-row ::deep .btn-link {
margin-left: 0;
}
}

@media (min-width: 641px) {
.page {
flex-direction: row;
}

.sidebar {
width: 250px;
height: 100vh;
position: sticky;
top: 0;
}

.top-row {
position: sticky;
top: 0;
z-index: 1;
}

.top-row.auth ::deep a:first-child {
flex: 1;
text-align: right;
width: 0;
}

.top-row, article {
padding-left: 2rem !important;
padding-right: 1.5rem !important;
}
}

#blazor-error-ui {
background: lightyellow;
bottom: 0;
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
display: none;
left: 0;
padding: 0.6rem 1.25rem 0.7rem 1.25rem;
position: fixed;
width: 100%;
z-index: 1000;
}

#blazor-error-ui .dismiss {
cursor: pointer;
position: absolute;
right: 0.75rem;
top: 0.5rem;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<div class="top-row ps-3 navbar navbar-dark">
<div class="container-fluid">
<a class="navbar-brand" href="">WebAssembly</a>
</div>
</div>

<input type="checkbox" title="Navigation menu" class="navbar-toggler" />

<div class="nav-scrollable" onclick="document.querySelector('.navbar-toggler').click()">
<nav class="flex-column">
<div class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="bi bi-house-door-fill-nav-menu" aria-hidden="true"></span> Home
</NavLink>
</div>

<div class="nav-item px-3">
<NavLink class="nav-link" href="counter">
<span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> Counter
</NavLink>
</div>

<div class="nav-item px-3">
<NavLink class="nav-link" href="weather">
<span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> Weather (local api)
</NavLink>
</div>

<div class="nav-item px-3">
<NavLink class="nav-link" href="greet">
<span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> Greet (remote api)
</NavLink>
</div>
</nav>
</div>

Loading
Loading