Skip to content

Commit 5e08ed7

Browse files
committed
Verify Identity Lab runtime integration
1 parent 783f3e8 commit 5e08ed7

14 files changed

Lines changed: 332 additions & 0 deletions

File tree

‎UltimateAuth.slnx‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
<Project Path="src/bundle/CodeBeam.UltimateAuth.Reference.Bundle/CodeBeam.UltimateAuth.Reference.Bundle.csproj" Id="f01dee6a-a60d-40a2-bb6a-9894b03e7122" />
1010
</Folder>
1111
<Folder Name="/Samples/">
12+
<Project Path="samples/identity-lab/CodeBeam.UltimateAuth.IdentityLab/CodeBeam.UltimateAuth.IdentityLab.csproj" />
1213
<Project Path="samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore.csproj" Id="84201d45-0298-4bf3-9bd4-0567b373f6a0" />
1314
<Project Path="samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer/CodeBeam.UltimateAuth.Sample.BlazorServer.csproj" />
1415
<Project Path="samples/blazor-standalone-wasm/CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm/CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm.csproj" Id="27bd3c4d-65a9-4c70-a6c9-4178b1897730" />
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Identity Lab Phase 1 capability audit
2+
3+
This audit records the public API boundary used before implementing the three screens. Identity Lab does not access an UltimateAuth store or persistence model directly.
4+
5+
| Screen / UI feature | UltimateAuth capability | Public API / type | Usable? | Ownership / decision |
6+
|---|---|---|---|---|
7+
| Landing: isolated playground copy and transition | None required | — | Yes | Identity Lab presentation. The environment uses the real in-memory UltimateAuth bundle. |
8+
| Sign in | Flow-based login | `UAuthLoginForm`, `IUAuthClient.Flows`, `LoginRequest` | Yes | UltimateAuth owns validation, login, session establishment and errors. |
9+
| Create account | User creation | `IUAuthClient.Users.CreateAsync(CreateUserRequest)` | Yes | UltimateAuth owns users and credentials. Identity Lab only collects input. |
10+
| Remember me | No matching public semantic was found | — | No | Omitted rather than emulated. |
11+
| GitHub login | No configured external-provider public flow in the inspected samples | — | No | Omitted rather than presenting a non-functional control. |
12+
| Current identity and authorization state | Auth state snapshot | `UAuthPageBase.AuthState`, `UAuthStateView`, `UAuthAuthorize` | Yes | UltimateAuth-backed identity, tenant, roles and session state. |
13+
| Device/session overview | Root → Chain → Session model | `IUAuthClient.Sessions.GetMyChainsAsync`, `SessionChainSummary` | Yes | A chain is labelled as a device/client context; it is not presented as a raw session. |
14+
| Revoke a device chain | Chain revocation | `IUAuthClient.Sessions.RevokeMyChainAsync(SessionChainId)` | Yes | Security decision and revocation remain in UltimateAuth. |
15+
| Validate and logout | Session flow operations | `IUAuthClient.Flows.ValidateAsync`, `LogoutAsync` | Yes | UltimateAuth-backed. |
16+
| Client profile/runtime | Client product metadata | `IUAuthClientProductInfoProvider.Get()` | Yes | UltimateAuth-backed. |
17+
| Playground label and storage description | Environment metadata | app configuration | Yes | Identity Lab-owned presentation metadata. No security meaning. |
18+
| All-user count/list | Admin query exists but should only appear after permission-aware product design | `IUAuthClient.Users.QueryAsync(UserQuery)` | Deferred | Not queried in Phase 1; avoids conflating the current-user dashboard with admin access. |
19+
| Global active-session count | No unrestricted aggregate API identified | — | No | Omitted; current user's chain count is shown instead. |
20+
| Authentication/security event feed | No public query API identified | — | No | Omitted; the landing preview describes capabilities rather than displaying fabricated events. |
21+
| Playground creation, expiry and quotas | No Identity Lab persistence/service exists in Phase 1 | — | No | Omitted until genuine playground metadata storage is introduced. |
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
2+
<PropertyGroup>
3+
<TargetFramework>net10.0</TargetFramework>
4+
<Nullable>enable</Nullable>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<IsPackable>false</IsPackable>
7+
</PropertyGroup>
8+
<ItemGroup>
9+
<PackageReference Include="MudBlazor" Version="9.10.0" />
10+
</ItemGroup>
11+
<ItemGroup>
12+
<ProjectReference Include="..\..\..\src\bundle\CodeBeam.UltimateAuth.InMemory.Bundle\CodeBeam.UltimateAuth.InMemory.Bundle.csproj" />
13+
<ProjectReference Include="..\..\..\src\client\CodeBeam.UltimateAuth.Client.Blazor\CodeBeam.UltimateAuth.Client.Blazor.csproj" />
14+
<ProjectReference Include="..\..\CodeBeam.UltimateAuth.Sample.Seed\CodeBeam.UltimateAuth.Sample.Seed.csproj" />
15+
</ItemGroup>
16+
</Project>
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1" />
6+
<base href="/" />
7+
<link rel="preconnect" href="https://fonts.googleapis.com" />
8+
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&amp;display=swap" />
9+
<link href="_content/MudBlazor/MudBlazor.min.css" rel="stylesheet" />
10+
<link href="app.css" rel="stylesheet" />
11+
<HeadOutlet @rendermode="InteractiveServer" />
12+
</head>
13+
<body>
14+
<Routes @rendermode="InteractiveServer" />
15+
<script src="_framework/blazor.web.js"></script>
16+
<script src="_content/MudBlazor/MudBlazor.min.js"></script>
17+
<script src="_content/CodeBeam.UltimateAuth.Client.Blazor/uauth.min.js"></script>
18+
</body>
19+
</html>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
@inherits LayoutComponentBase
2+
3+
<MudLayout>
4+
<MudAppBar Elevation="0" Class="lab-appbar">
5+
<a class="brand" href="/"><span class="brand-mark">U</span><strong>UltimateAuth</strong></a>
6+
<MudSpacer />
7+
<MudLink Href="/" Underline="Underline.None">Playground</MudLink>
8+
<MudLink Href="https://ultimateauth.com" Target="_blank" Underline="Underline.None">Documentation</MudLink>
9+
<MudLink Href="https://github.com/CodeBeamOrg/UltimateAuth" Target="_blank" Underline="Underline.None">GitHub ↗</MudLink>
10+
</MudAppBar>
11+
<MudMainContent>@Body</MudMainContent>
12+
</MudLayout>
13+
14+
<div id="blazor-error-ui">An unexpected error occurred. <a href="">Reload</a></div>
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
@page "/access"
2+
@page "/login"
3+
@attribute [UAuthLoginPage]
4+
@inherits UAuthPageBase
5+
@inject IUAuthClient UAuthClient
6+
@inject ISnackbar Snackbar
7+
8+
<PageTitle>Sign in · Identity Lab</PageTitle>
9+
<main class="access-shell">
10+
<div class="access-heading"><div class="eyebrow">ULTIMATEAUTH</div><h1>Identity Lab</h1><p>Explore authentication, authorization and session management<br />in a live application built with Blazor and UltimateAuth.</p></div>
11+
<MudPaper Class="access-card" Elevation="0">
12+
<div class="auth-tabs"><button class="@(!create ? "active" : null)" @onclick="() => create = false">Sign In</button><button class="@(create ? "active" : null)" @onclick="() => create = true">Create Account</button></div>
13+
@if (!create)
14+
{
15+
<UAuthLoginForm Identifier="username" Secret="password" ReturnUrl="/dashboard" OnTryResult="HandleLogin">
16+
<MudStack Spacing="2">
17+
<MudTextField @bind-Value="username" Label="Username or email" Placeholder="you@example.com" Variant="Variant.Outlined" Required="true" />
18+
<MudPasswordField @bind-Value="password" Label="Password" Placeholder="Your password" Variant="Variant.Outlined" Required="true" />
19+
<MudButton Class="auth-submit" Variant="Variant.Filled" ButtonType="ButtonType.Submit" EndIcon="@Icons.Material.Filled.ArrowForward">Sign In</MudButton>
20+
<MudText Typo="Typo.caption" Align="Align.Center">Try the seeded accounts <b>admin/admin</b> or <b>user/user</b>.</MudText>
21+
</MudStack>
22+
</UAuthLoginForm>
23+
}
24+
else
25+
{
26+
<MudForm @ref="form"><MudStack Spacing="2">
27+
<MudTextField @bind-Value="username" Label="Username" Variant="Variant.Outlined" Required="true" />
28+
<MudTextField @bind-Value="email" Label="Email" Variant="Variant.Outlined" Required="true" />
29+
<MudPasswordField @bind-Value="password" Label="Password" Variant="Variant.Outlined" Required="true" />
30+
<MudButton Class="auth-submit" Variant="Variant.Filled" OnClick="Register" EndIcon="@Icons.Material.Filled.ArrowForward">Create Account</MudButton>
31+
</MudStack></MudForm>
32+
}
33+
</MudPaper>
34+
<section class="access-features"><span>♙ <b>User Management</b><small>Real UltimateAuth users</small></span><span>⚿ <b>Live Sessions</b><small>Device-aware session chains</small></span><span>♢ <b>Authorization</b><small>Policies and permissions</small></span><span>▥ <b>Honest Data</b><small>No fabricated security events</small></span></section>
35+
</main>
36+
37+
@code {
38+
private bool create;
39+
private string? username, email, password;
40+
private MudForm form = null!;
41+
42+
private void HandleLogin(IUAuthTryResult result)
43+
{
44+
if (!result.Success) Snackbar.Add(result.Reason is null ? "Sign in failed." : $"Sign in failed: {result.Reason}.", Severity.Error);
45+
}
46+
47+
private async Task Register()
48+
{
49+
await form.ValidateAsync();
50+
if (!form.IsValid) return;
51+
var result = await UAuthClient.Users.CreateAsync(new CodeBeam.UltimateAuth.Users.Contracts.CreateUserRequest { UserName = username, Email = email, Password = password });
52+
if (result.IsSuccess) { Snackbar.Add("Account created. You can now sign in.", Severity.Success); create = false; }
53+
else Snackbar.Add(result.ErrorText ?? "Account creation failed.", Severity.Error);
54+
}
55+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
@page "/dashboard"
2+
@attribute [UAuthAuthorize]
3+
@inherits UAuthPageBase
4+
@inject IUAuthClient UAuthClient
5+
@inject IUAuthClientProductInfoProvider ProductInfoProvider
6+
@inject ISnackbar Snackbar
7+
8+
<PageTitle>Dashboard · Identity Lab</PageTitle>
9+
<main class="dashboard-shell">
10+
<aside class="side-nav">
11+
<div><span class="lab-icon">⚗</span><strong>Identity Lab</strong><small>Playground Environment</small></div>
12+
<nav><a class="selected">⌂ Dashboard</a><a>♙ My Identity</a><a>▱ Device Chains</a><a>♢ Authorization</a></nav>
13+
<div class="side-bottom"><a href="https://ultimateauth.com">Documentation ↗</a><a href="https://github.com/CodeBeamOrg/UltimateAuth">GitHub ↗</a><small>A CodeBeam Product</small></div>
14+
</aside>
15+
<section class="dashboard-content">
16+
<header class="dash-heading"><div><span>DASHBOARD</span><h1>Welcome back, @DisplayName.</h1><p>This is your isolated playground environment for exploring authentication,<br />authorization and session management with UltimateAuth.</p></div><MudChip T="string" Color="Color.Success" Variant="Variant.Outlined">● Playground Environment</MudChip></header>
17+
<div class="metric-grid">
18+
<article><i>♙</i><div><b>Current user</b><strong>@UserName</strong><small>UltimateAuth identity</small></div></article>
19+
<article><i>▱</i><div><b>Device chains</b><strong>@(chains?.TotalCount.ToString() ?? "—")</strong><small>From GetMyChainsAsync</small></div></article>
20+
<article><i>✓</i><div><b>Session state</b><strong>@(AuthState?.Identity?.SessionState?.ToString() ?? "—")</strong><small>Server-controlled</small></div></article>
21+
<article><i>♢</i><div><b>Authorization</b><strong>@(AuthState?.Claims.Roles.Count ?? 0)</strong><small>Assigned roles</small></div></article>
22+
</div>
23+
<div class="dashboard-grid">
24+
<MudPaper Class="dash-panel chains-panel" Elevation="0">
25+
<div class="panel-title"><h2>Your device chains</h2><MudButton Variant="Variant.Text" OnClick="LoadChains">Refresh</MudButton></div>
26+
@if (loading) { <MudProgressLinear Indeterminate="true" Color="Color.Primary" /> }
27+
else if (chains?.Items.Count > 0)
28+
{
29+
<MudTable Items="chains.Items" Dense="true" Hover="true" Elevation="0">
30+
<HeaderContent><MudTh>Device / client</MudTh><MudTh>Last seen</MudTh><MudTh>State</MudTh><MudTh></MudTh></HeaderContent>
31+
<RowTemplate>
32+
<MudTd><b>@DeviceName(context)</b><br /><small>@(context.IsCurrentDevice ? "Current device" : context.Platform)</small></MudTd>
33+
<MudTd>@FormatDate(context.LastSeenAt)</MudTd>
34+
<MudTd><MudChip T="string" Size="Size.Small" Color="@(context.IsRevoked ? Color.Error : Color.Success)">@context.State</MudChip></MudTd>
35+
<MudTd><MudButton Size="Size.Small" Color="Color.Error" Variant="Variant.Text" Disabled="context.IsRevoked" OnClick="() => Revoke(context)">Revoke</MudButton></MudTd>
36+
</RowTemplate>
37+
</MudTable>
38+
}
39+
else { <div class="empty-state"><b>No device chains returned</b><p>UltimateAuth has not exposed an active chain for this account yet.</p></div> }
40+
</MudPaper>
41+
<MudPaper Class="dash-panel" Elevation="0">
42+
<div class="panel-title"><h2>Authentication state</h2></div>
43+
<dl class="info-list"><dt>Authenticated</dt><dd>@(AuthState?.IsAuthenticated == true ? "Yes" : "No")</dd><dt>Tenant</dt><dd>@AuthState?.Identity?.Tenant.Value</dd><dt>User key</dt><dd class="truncate">@AuthState?.Identity?.UserKey.Value</dd><dt>Roles</dt><dd>@Roles</dd><dt>Client profile</dt><dd>@product?.ClientProfile</dd></dl>
44+
</MudPaper>
45+
<MudPaper Class="dash-panel" Elevation="0">
46+
<div class="panel-title"><h2>Quick actions</h2></div>
47+
<div class="quick-actions"><MudButton Variant="Variant.Outlined" StartIcon="@Icons.Material.Filled.Refresh" OnClick="Validate">Validate session</MudButton><MudButton Variant="Variant.Outlined" StartIcon="@Icons.Material.Filled.Devices" OnClick="LoadChains">Explore chains</MudButton><MudButton Variant="Variant.Outlined" Color="Color.Error" StartIcon="@Icons.Material.Filled.Logout" OnClick="Logout">Sign out</MudButton></div>
48+
</MudPaper>
49+
<MudPaper Class="dash-panel" Elevation="0">
50+
<div class="panel-title"><h2>Playground info</h2></div>
51+
<dl class="info-list"><dt>Environment</dt><dd><span class="green-dot">●</span> Isolated</dd><dt>Storage</dt><dd>In-memory</dd><dt>Started</dt><dd>@product?.StartedAt.ToLocalTime().ToString("g")</dd><dt>UltimateAuth</dt><dd>v@product?.Version</dd></dl>
52+
</MudPaper>
53+
</div>
54+
</section>
55+
</main>
56+
57+
@code {
58+
private PagedResult<SessionChainSummary>? chains;
59+
private UAuthClientProductInfo? product;
60+
private bool loading;
61+
private string DisplayName => AuthState?.Identity?.DisplayName ?? AuthState?.Identity?.PrimaryUserName ?? "Explorer";
62+
private string UserName => AuthState?.Identity?.PrimaryUserName ?? "—";
63+
private string Roles => AuthState is null || AuthState.Claims.Roles.Count == 0 ? "None" : string.Join(", ", AuthState.Claims.Roles);
64+
65+
protected override async Task OnInitializedAsync()
66+
{
67+
product = ProductInfoProvider.Get();
68+
if (AuthState.IsAuthenticated)
69+
await LoadChains();
70+
}
71+
72+
protected override void HandleAuthStateChanged(UAuthStateChangeReason reason)
73+
{
74+
if (reason == UAuthStateChangeReason.Authenticated && chains is null)
75+
_ = InvokeAsync(async () => { await LoadChains(); StateHasChanged(); });
76+
}
77+
private async Task LoadChains() { loading = true; var result = await UAuthClient.Sessions.GetMyChainsAsync(); chains = result.Value; loading = false; }
78+
private async Task Revoke(SessionChainSummary chain) { var result = await UAuthClient.Sessions.RevokeMyChainAsync(chain.ChainId); Snackbar.Add(result.IsSuccess ? "Device chain revoked." : result.ErrorText ?? "Revocation failed.", result.IsSuccess ? Severity.Success : Severity.Error); await LoadChains(); }
79+
private async Task Validate() { var result = await UAuthClient.Flows.ValidateAsync(); Snackbar.Add(result.IsValid ? "Session is valid." : $"Session state: {result.State}", result.IsValid ? Severity.Success : Severity.Warning); }
80+
private Task Logout() => UAuthClient.Flows.LogoutAsync();
81+
private static string DeviceName(SessionChainSummary chain) => string.Join(" · ", new[] { chain.Browser, chain.OperatingSystem, chain.DeviceType }.Where(x => !string.IsNullOrWhiteSpace(x)));
82+
private static string FormatDate(DateTimeOffset? value) => value?.ToLocalTime().ToString("g") ?? "Not reported";
83+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
@page "/"
2+
@inject NavigationManager Navigation
3+
4+
<PageTitle>UltimateAuth Identity Lab</PageTitle>
5+
<main class="landing-shell @(leaving ? "leaving" : null)">
6+
<section class="hero">
7+
<div class="hero-copy">
8+
<div class="eyebrow">ULTIMATEAUTH</div>
9+
<h1>Identity <span>Lab</span></h1>
10+
<h2>A live playground and sandbox for authentication, authorization and session management.</h2>
11+
<p>Explore real users, device chains, sessions and permissions in an isolated environment built with Blazor, MudBlazor and UltimateAuth.</p>
12+
<MudButton Class="explore-button" Variant="Variant.Filled" EndIcon="@Icons.Material.Filled.ArrowForward" OnClick="StartExploring">Start Exploring</MudButton>
13+
</div>
14+
<div class="hero-preview" aria-label="Identity Lab dashboard preview">
15+
<div class="preview-top"><strong><span class="mini-mark">U</span> UltimateAuth</strong><span>⌕ &nbsp; Playground</span></div>
16+
<div class="preview-body">
17+
<aside>▣ Dashboard<br />♙ Users<br />▱ Sessions<br />◇ Authorization</aside>
18+
<div><h3>Dashboard</h3><p>Overview of your playground environment.</p><div class="preview-cards"><b>Identity<br /><em>Real</em></b><b>Device chains<br /><em>Live</em></b><b>Auth state<br /><em>Secure</em></b></div><div class="preview-events"><strong>UltimateAuth capabilities</strong><hr />Authentication <span>flow based</span><hr />Sessions <span>device aware</span><hr />Authorization <span>policy driven</span></div></div>
19+
</div>
20+
</div>
21+
</section>
22+
<section class="feature-row">
23+
<article><i>⚗</i><div><strong>Safe Playground</strong><p>Isolated environment for learning and testing.</p></div></article>
24+
<article><i>♢</i><div><strong>Real Authentication</strong><p>Powered by UltimateAuth. No mock authentication.</p></div></article>
25+
<article><i>♙</i><div><strong>Full Scenarios</strong><p>Users, permissions, devices and session control.</p></div></article>
26+
<article><i>&lt;/&gt;</i><div><strong>Open Source</strong><p>Explore the code and use it in your own projects.</p></div></article>
27+
</section>
28+
<footer><span>A CodeBeam Product</span><span>Playground &nbsp;&nbsp; Documentation &nbsp;&nbsp; GitHub ↗</span></footer>
29+
</main>
30+
31+
@code {
32+
private bool leaving;
33+
private async Task StartExploring()
34+
{
35+
leaving = true;
36+
await Task.Delay(180);
37+
Navigation.NavigateTo("/access");
38+
}
39+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
@inject NavigationManager Navigation
2+
<main class="center-state"><MudPaper Class="pa-8" Elevation="0"><MudIcon Icon="@Icons.Material.Filled.Lock" Color="Color.Primary" Size="Size.Large" /><h2>Authentication required</h2><p>Sign in through UltimateAuth to enter this playground.</p><MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="() => Navigation.NavigateTo(&quot;/access&quot;)">Sign In</MudButton></MudPaper></main>

0 commit comments

Comments
 (0)