Principal, Identity & IdentityProperty
The Principal system in SimpleW represents the authenticated user and all associated identity data.
It is composed of three core types :
HttpPrincipal→ the userHttpIdentity→ the identity of the userIdentityProperty→ extensible key/value metadata
Conceptually :
The Principal system is the single source of truth for user identity in SimpleW.
Architecture Overview
HttpPrincipal
└── HttpIdentity
├── Identifier
├── Name
├── Email
├── Roles
└── Properties (IdentityProperty[])INFO
The HttpPrincipal here is quite the same in AspNet Core except that :
- it contains only one
HttpIdentity Claimsare renamed asIdentityProperty
HttpPrincipal
HttpPrincipal represents the current user.
It contains exactly one identity. Key properties :
Identity→ the underlying identityIsAuthenticated→ shortcut toIdentity.IsAuthenticatedName,Email→ shortcutsIsInRole(string role)IsInRoles(string rolesCsv)Get(string key)→ access custom properties
Example
if (session.Principal.IsAuthenticated) {
Console.WriteLine(session.Principal.Name);
}
if (session.Principal.IsInRole("admin")) {
// authorized
}Mental Model
Principal = "who is making this request"
HttpIdentity
HttpIdentity represents how the user is identified.
Key properties
IsAuthenticatedAuthenticationType(e.g."Bearer","Cookie")Identifier(unique ID, usuallysub)NameEmailRoles(IReadOnlyCollection<string>)Properties(IReadOnlyCollection<IdentityProperty>)
Example
var identity = new HttpIdentity(
isAuthenticated: true,
authenticationType: "Bearer",
identifier: "user-123",
name: "John",
email: "john@example.com",
roles: new[] { "admin", "user" },
properties: new[] {
new IdentityProperty("tenant_id", "acme"),
new IdentityProperty("plan", "pro")
}
);Mental Model
Identity = "how we know who the user is"
IdentityProperty
IdentityProperty is a flexible key/value pair used to store custom data.
Example
new IdentityProperty("tenant_id", "acme");
new IdentityProperty("feature_flag", "beta");Access
string? tenant = session.User.Get("tenant_id");Use cases :
- multi-tenant systems
- feature flags
- permissions
- custom claims (JWT, OAuth, etc.)
Mental Model
IdentityProperty = "everything that does not belong to core identity fields"
Accessing the Principal
The principal is available in both execution contexts :
HttpSession.PrincipalController.Principal
This follows the same pattern as Request and Response.
server.MapGet("/me", (HttpSession session) => {
if (!session.Principal.IsAuthenticated) {
return session.Response.Unauthorized("Not authenticated");
}
return new {
id = session.Principal.Identity.Identifier,
name = session.Principal.Name,
roles = session.Principal.Identity.Roles
};
});[Route("/user")]
public class UserController : Controller {
[Route("GET", "/me")]
public object Me() {
if (!Principal.IsAuthenticated) {
return Response.Unauthorized("Not authenticated");
}
return new {
id = Principal.Identity.Identifier,
name = Principal.Name
};
}
}Setting the Principal
There are two ways to define the principal :
- ConfigurePrincipalResolver (recommended)
- Direct assignment (manual)
server.ConfigurePrincipalResolver(session => {
string? token = session.Request.Headers.Authorization;
if (string.IsNullOrWhiteSpace(token)) {
return null;
}
if (!JwtBearerHelper.TryValidateToken(options, token, out HttpPrincipal? principal, out _)) {
return null;
}
return principal;
});session.Principal = new HttpPrincipal(new HttpIdentity(
isAuthenticated: true,
authenticationType: "Custom",
identifier: "user-123",
name: "John",
email: null,
roles: new[] { "admin" },
properties: null
));INFO
The principal is resolved only when needed by lazy loading. This avoids unnecessary work and improves performance.
Mental Model
PrincipalResolver = "how the server builds the user from the request"
Real Example of Principal integration
Examples of HttpPrincipal integration :
