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
19 changes: 7 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ UltimateAuth is an open-source auth framework with platform-level capabilities t
| Security Expansion | 0.3.0 | MFA, Reauth | 🟡 In Progress | Q1 2027 |
| Infrastructure Expansion| 0.4.0 | Redis, Distributed Cache, Password Hashers | 🔜 Planned | Q1 2027 |
| Multi-Tenant Expansion | 0.5.0 | Multi tenant management | 🔜 Planned | Q2 2027 |
| Extensibility Expansion | 0.6.0 | Audit, events, hooks | 🔜 Planned | Q2 2027 |
| Extensibility Expansion | 0.6.0 | Audit, Passwordless Login | 🔜 Planned | Q2 2027 |
| Performance Expansion | 0.7.0 | Benchmarks, caching | 🔜 Planned | Q3 2027 |
| Ecosystem Expansion | 0.8.0 | Migration tools | 🔜 Planned | Q3 2027 |
| v1.0 | 1.0.0 | Locked API, align with .NET 12 | 🔜 Planned | Q4 2027 |
Expand All @@ -62,9 +62,9 @@ UltimateAuth is designed to deliver a simple, consistent developer experience fr
| Milestone | Version | Target |
|---|---|---|
| **Developer Foundation** | 0.1.x | A functional, security-conscious authentication framework with a complete core architecture, extensibility, and a developer-first API. Suitable for development, early adoption, and production use following application-specific evaluation. |
| **Production Expansion** | 0.2.0 | Ready to power real-world applications, particularly small and medium-sized products, with expanded authentication modes, built-in rate limiting, and a proven integration experience. Larger applications can also adopt UltimateAuth when its available capabilities meet their requirements. |
| **Advanced Security & Scale** | 0.4.0 | Extends production capabilities with MFA, reauthentication, distributed infrastructure, and additional security options for applications with more demanding requirements. Builds on the same simple developer experience. |
| **Migration & Adoption** | 0.8.0 | Makes adopting UltimateAuth in existing applications easier through migration tools, guidance, and established integration patterns, without requiring new applications to wait for this milestone. |
| **Production Expansion** | 0.2.x | Ready to power real-world applications, particularly small and medium-sized products, with expanded authentication modes, built-in rate limiting, and a proven integration experience. Larger applications can also adopt UltimateAuth when its available capabilities meet their requirements. |
| **Advanced Security & Scale** | 0.4.x | Extends production capabilities with MFA, reauthentication, distributed infrastructure, and additional security options for applications with more demanding requirements. Builds on the same simple developer experience. |
| **Migration & Adoption** | 0.8.x | Makes adopting UltimateAuth in existing applications easier through migration tools, guidance, and established integration patterns, without requiring new applications to wait for this milestone. |

**The UltimateAuth philosophy:** Simple from day one, more capable with every release. Version milestones describe expanding capabilities — not a progression from difficult to easy, or from unusable to usable.

Expand Down Expand Up @@ -225,25 +225,20 @@ Change `Components/Pages/Home.razor`:

<div class="container mt-5" style="max-width: 400px;">
<h3>Sign in</h3>
<UAuthLoginForm Identifier="@_identifier" Secret="@_password" ReturnUrl="/dashboard">
<UAuthLoginForm ReturnUrl="/dashboard">
<div class="d-block mb-3">
<label>Username</label>
<input @bind="_identifier" class="form-control w-100" autocomplete="username" />
<input name="Identifier" class="form-control w-100" autocomplete="username" />
</div>

<div class="d-block mb-3">
<label>Password</label>
<input type="password" @bind="_password" class="form-control w-100" autocomplete="current-password" />
<input type="password" name="Secret" class="form-control w-100" autocomplete="current-password" />
</div>

<button class="btn btn-primary w-100" type="submit">Sign in</button>
</UAuthLoginForm>
</div>

@code {
private string _identifier = "";
private string _password = "";
}
```

### 7. Protect your pages
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,6 @@

<MudDivider Class="mt-14 mb-2" />

@* <div class="lab-welcome-session">
<div class="lab-welcome-session-title">
<MudIcon Icon="@Icons.Material.Filled.VerifiedUser" Color="Color.Success" Size="Size.Small" />
<strong>Authenticated session</strong>
</div>

<p>We recognize your account. Your Identity Lab workspace is ready whenever you are.</p>
</div> *@

<MudButton Variant="Variant.Filled"
Color="Color.Primary"
Size="Size.Large"
Expand Down Expand Up @@ -107,7 +98,7 @@
</Authorized>

<NotAuthorized>
<UAuthLoginForm Identifier="@_identifier" Secret="@_password" ReturnUrl="/dashboard" OnTryResult="HandleLoginResult" OnSubmittingChanged="HandleSubmittingChanged">
<UAuthLoginForm ReturnUrl="/dashboard" OnTryResult="HandleLoginResult" OnSubmittingChanged="HandleSubmittingChanged">

@if (IsLockedOut)
{
Expand Down Expand Up @@ -174,7 +165,7 @@
<div class="lab-login-fields">

<MudTextField T="string"
@bind-Value="_identifier"
name="Identifier"
Label="Email or Username"
Placeholder="Enter your email or username"
Variant="Variant.Outlined"
Expand All @@ -184,18 +175,17 @@
MaxLength="256"
Required="true"
RequiredError="Email or username is required."
Disabled="@_loginBusy"
Immediate="true" />


@* Do not add loginbusy disabled to the text and password fields, it will prevent the form from submitting with required fields. *@
<MudPasswordField T="string"
@bind-Value="_password"
name="Secret"
Label="Password"
Variant="Variant.Outlined"
autocomplete="current-password"
MaxLength="256"
Required="true"
RequiredError="Password is required."
Disabled="@_loginBusy"
Immediate="true" />

<MudLoadingButton ButtonType="ButtonType.Submit"
Expand All @@ -208,7 +198,6 @@
EndIcon="@Icons.Material.Filled.ArrowForward">
@(IsLockedOut ? "Temporarily locked" : "Sign In")
</MudLoadingButton>

</div>
</UAuthLoginForm>

Expand Down
101 changes: 69 additions & 32 deletions docs/content/client-sdk/authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -895,23 +895,59 @@ The component supports password-based login, PKCE completion through UAuthHub, c

### Basic Usage

A login form can be implemented with the following component:
`UAuthLoginForm` provides a complete, ready-to-use login form out of the box. No additional markup or credential bindings are required.

```razor
<UAuthLoginForm />
```

That's it. UltimateAuth automatically renders the identifier field, password field, and submit button, and handles the authentication submission workflow.

**Customizing the Form**

When you need a custom layout or styling, simply provide your own form content:

```razor
@using CodeBeam.UltimateAuth.Client.Blazor

<UAuthLoginForm Identifier="@email" Secret="@password">
<input @bind="email" placeholder="Email" />
<input @bind="password" type="password" placeholder="Password" />
<UAuthLoginForm>
<input name="Identifier" placeholder="Email" autocomplete="username" />
<input name="Secret" type="password" placeholder="Password" autocomplete="current-password" />
<button type="submit">Sign in</button>
</UAuthLoginForm>

@code {
private string email = "";
private string password = "";
}
```

UltimateAuth handles the authentication workflow in both cases. Your custom fields only need the correct HTML `name` attributes; there is no need to bind credentials to Blazor component state.

### Native Credential Submission

`UAuthLoginForm` reads credentials directly from native browser form fields. It does not accept `Identifier` or `Secret` component parameters.

When providing custom `ChildContent`, the form must contain enabled input elements with the exact names `Identifier` and `Secret`.

Credential values should not be bound to Blazor component state solely for submission. This is particularly important in Blazor Server, where interactive component bindings can transmit credential values over the SignalR connection.

**Important: Do Not Disable Credential Fields During Submission**

HTML inputs marked as `disabled` are excluded from `FormData` and native form submissions.

Do not set `Disabled="@isSubmitting"` on the username or password fields. A login preview may succeed, but the subsequent native commit can fail because the credential fields are missing from the submitted form.

Instead, disable the submit button while leaving the credential inputs enabled.

If you need to prevent editing during submission, consider `readonly` where supported, while accounting for component behavior, browser autofill, and accessibility.

**Custom Form Requirements**

- Use `name="Identifier"` and `name="Secret"` exactly.
- Keep both credential inputs enabled throughout submission.
- Avoid duplicate inputs with these names.
- Use `type="password"` and `autocomplete="current-password"` for password fields.
- Keep custom submit controls inside `UAuthLoginForm`.
- Use `OnSubmittingChanged` to manage loading indicators and submit-button state.

`UAuthLoginForm` handles the try and commit orchestration. The native commit includes the browser form fields, device information, and antiforgery data required by the configured authentication flow.

The component automatically uses `TryAndCommit` as its default submission mode.

When submitted, it:
Expand All @@ -934,13 +970,13 @@ For example, with MudBlazor:
@using CodeBeam.UltimateAuth.Client.Blazor
@using CodeBeam.UltimateAuth.Client

<UAuthLoginForm Identifier="@email" Secret="@password" SubmitMode="UAuthSubmitMode.TryAndCommit" OnTryResult="HandleLoginResult">
<UAuthLoginForm SubmitMode="UAuthSubmitMode.TryAndCommit" OnTryResult="HandleLoginResult" OnSubmittingChanged="HandleSubmittingChanged">
<MudStack Spacing="3">
<MudText Typo="Typo.h5">Sign in</MudText>
<MudTextField @bind-Value="email" Label="Email" Variant="Variant.Outlined" />
<MudTextField @bind-Value="password" Label="Password" InputType="InputType.Password" Variant="Variant.Outlined" />
<MudTextField name="Identifier" Label="Email" Variant="Variant.Outlined" />
<MudTextField name="Secret" Label="Password" InputType="InputType.Password" Variant="Variant.Outlined" />

<MudButton ButtonType="ButtonType.Submit" Variant="Variant.Filled" Color="Color.Primary" FullWidth>Sign in</MudButton>
<MudButton ButtonType="ButtonType.Submit" Disabled="@_isSubmitting" Variant="Variant.Filled" Color="Color.Primary" FullWidth>Sign in</MudButton>

@if (!string.IsNullOrWhiteSpace(error))
{
Expand All @@ -950,10 +986,15 @@ For example, with MudBlazor:
</UAuthLoginForm>

@code {
private string email = "";
private string password = "";
private bool _isSubmitting;
private string? error;

private Task HandleSubmittingChanged(bool submitting)
{
_isSubmitting = submitting;
return Task.CompletedTask;
}

private Task HandleLoginResult(IUAuthTryResult result)
{
error = result.IsSuccess ? null : "Unable to sign in.";
Expand All @@ -973,7 +1014,7 @@ Because the component renders an HTML form internally, place the submit button i
Use `SubmitMode` to control how the authentication attempt is executed.

```razor
<UAuthLoginForm Identifier="@email" Secret="@password" SubmitMode="UAuthSubmitMode.TryAndCommit">
<UAuthLoginForm SubmitMode="UAuthSubmitMode.TryAndCommit">
...
</UAuthLoginForm>
```
Expand All @@ -995,7 +1036,7 @@ For interactive login forms that need validation feedback, it is generally the r
The component exposes an `OnTryResult` callback:

```razor
<UAuthLoginForm Identifier="@email" Secret="@password" OnTryResult="HandleResult">
<UAuthLoginForm OnTryResult="HandleResult">
...
</UAuthLoginForm>
```
Expand Down Expand Up @@ -1059,9 +1100,9 @@ The same component also supports PKCE login completion when hosted within a supp
Set `LoginType` to `Pkce`:

```razor
<UAuthLoginForm LoginType="UAuthLoginType.Pkce" Identifier="@email" Secret="@password" SubmitMode="UAuthSubmitMode.TryAndCommit" OnTryResult="HandleLoginResult">
<input @bind="email" placeholder="Email" />
<input @bind="password" type="password" placeholder="Password" />
<UAuthLoginForm LoginType="UAuthLoginType.Pkce" SubmitMode="UAuthSubmitMode.TryAndCommit" OnTryResult="HandleLoginResult">
<input name="Identifier" placeholder="Email" />
<input name="Secret" type="password" placeholder="Password" />
<button type="submit">Continue</button>
</UAuthLoginForm>
```
Expand All @@ -1085,7 +1126,7 @@ A Blazor WebAssembly application should initiate PKCE using `BeginPkceAsync`; th
You can provide an explicit return URL:

```razor
<UAuthLoginForm Identifier="@email" Secret="@password" ReturnUrl="/dashboard">
<UAuthLoginForm ReturnUrl="/dashboard">
...
</UAuthLoginForm>
```
Expand All @@ -1103,7 +1144,7 @@ This allows a shared login component to participate in different authentication
For advanced integrations, the `Endpoint` parameter allows the form's HTML action endpoint to be overridden:

```razor
<UAuthLoginForm Identifier="@email" Secret="@password" Endpoint="/custom-login" SubmitMode="UAuthSubmitMode.DirectCommit">
<UAuthLoginForm Endpoint="/custom-login" SubmitMode="UAuthSubmitMode.DirectCommit">
...
</UAuthLoginForm>
```
Expand All @@ -1117,9 +1158,9 @@ Note that `Endpoint` affects the form's HTML action. Try-based submissions invok
`UAuthLoginForm` exposes `SubmitAsync`, allowing submission through a component reference.

```razor
<UAuthLoginForm @ref="loginForm" Identifier="@email" Secret="@password" AllowEnterKeyToSubmit="false">
<input @bind="email" placeholder="Email" />
<input @bind="password" type="password" />
<UAuthLoginForm @ref="loginForm" AllowEnterKeyToSubmit="false">
<input name="Identifier" placeholder="Email" />
<input name="Secret" type="password" />
</UAuthLoginForm>

<button type="button" @onclick="SubmitLoginAsync">
Expand All @@ -1129,9 +1170,6 @@ Note that `Endpoint` affects the form's HTML action. Try-based submissions invok
@code {
private UAuthLoginForm? loginForm;

private string email = "";
private string password = "";

private async Task SubmitLoginAsync()
{
if (loginForm is not null)
Expand All @@ -1153,7 +1191,7 @@ By default, the component supports Enter-key submission.
You can disable its built-in hidden submit button:

```razor
<UAuthLoginForm Identifier="@email" Secret="@password" AllowEnterKeyToSubmit="false">
<UAuthLoginForm AllowEnterKeyToSubmit="false">
...
</UAuthLoginForm>
```
Expand All @@ -1165,7 +1203,7 @@ This option controls the component's built-in Enter-key submit mechanism. Other
The component exposes `ReloadAsync` to reload its Hub credentials and flow state.

```razor
<UAuthLoginForm @ref="loginForm" LoginType="UAuthLoginType.Pkce" Identifier="@email" Secret="@password">
<UAuthLoginForm @ref="loginForm" LoginType="UAuthLoginType.Pkce">
...
</UAuthLoginForm>

Expand All @@ -1190,15 +1228,14 @@ It does not initiate a new PKCE authorization flow by itself.

| Parameter | Type | Default | Description |
|---|---|---|---|
| `Identifier` | `string?` | `null` | Login identifier |
| `Secret` | `string?` | `null` | Authentication secret |
| `LoginType` | `UAuthLoginType` | `Password` | Password or PKCE login |
| `SubmitMode` | `UAuthSubmitMode` | `TryAndCommit` | Submission strategy |
| `ReturnUrl` | `string?` | `null` | Redirect destination |
| `Endpoint` | `string?` | `null` | Custom HTML form endpoint |
| `HubSessionId` | `HubSessionId?` | `null` | Explicit Hub session context |
| `AllowEnterKeyToSubmit` | `bool` | `true` | Built-in Enter-key submit support |
| `OnTryResult` | `EventCallback<IUAuthTryResult>` | — | Structured try result callback |
| `OnSubmittingChanged` | `EventCallback<bool>` | — | Submission state change callback |
| `ChildContent` | `RenderFragment?` | — | Custom form content |

### When to Use UAuthLoginForm
Expand Down
11 changes: 3 additions & 8 deletions docs/content/getting-started/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,25 +190,20 @@ Change `Components/Pages/Home.razor`:

<div class="container mt-5" style="max-width: 400px;">
<h3>Sign in</h3>
<UAuthLoginForm Identifier="@_identifier" Secret="@_password" ReturnUrl="/dashboard">
<UAuthLoginForm ReturnUrl="/dashboard">
<div class="d-block mb-3">
<label>Username</label>
<input @bind="_identifier" class="form-control w-100" autocomplete="username" />
<input name="Identifier" class="form-control w-100" autocomplete="username" />
</div>

<div class="d-block mb-3">
<label>Password</label>
<input type="password" @bind="_password" class="form-control w-100" autocomplete="current-password" />
<input type="password" name="Secret" class="form-control w-100" autocomplete="current-password" />
</div>

<button class="btn btn-primary w-100" type="submit">Sign in</button>
</UAuthLoginForm>
</div>

@code {
private string _identifier = "";
private string _password = "";
}
```

`UAuthLoginForm` handles the login submission through UltimateAuth's client flow.
Expand Down

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Loading
Loading