Skip to content

Effects Composition

The IdentityModule is an [EffectsModule(typeof(IIdentityStore))] that generates Eff<RT, T> effect methods for identity and role management. ICurrentUser is a per-request context (not effects-based) provided by middleware.

Capability Interface

public interface IHasIdentityStore
{
    IIdentityStore IdentityStore { get; }
}

Effect Methods

public static partial class IdentityModule
{
    public static partial class Identity
    {
        public static Eff<RT, Option<UserIdentity>> GetAsync<RT>(string userId)
            where RT : IHasIdentityStore => ...

        public static Eff<RT, IReadOnlyList<RoleAssignment>> GetRolesAsync<RT>(string userId)
            where RT : IHasIdentityStore => ...

        public static Eff<RT, Unit> AssignRoleAsync<RT>(string userId, string roleName)
            where RT : IHasIdentityStore => ...

        public static Eff<RT, Unit> RevokeRoleAsync<RT>(string userId, string roleName)
            where RT : IHasIdentityStore => ...
    }
}

ICurrentUser (Non-Effects)

ICurrentUser is resolved from the HTTP context per-request, not through effects. Access it directly in handlers:

public interface ICurrentUser
{
    string UserId { get; }
    string Email { get; }
    string Name { get; }
    IReadOnlySet<string> Roles { get; }
    IReadOnlySet<int> Permissions { get; }
    bool IsAuthenticated { get; }
    bool HasPermission<TPermission>(TPermission permission) where TPermission : Enum;
    bool HasRole(string roleName);
}

Effect Pipeline Examples

Assign a role

var promoteToAdmin =
    from user in IdentityModule.Identity.GetAsync<AppRuntime>(userId)
        .Require(Error.New(404, "User not found"))
    from _ in IdentityModule.Identity.AssignRoleAsync<AppRuntime>(userId, "admin")
    from __ in AuditModule.Audit.SaveAsync<AppRuntime>(new AuditEntry
    {
        EntityType = "User",
        EntityId = userId,
        Action = "role_assigned:admin",
        Actor = CorrelationContext.Current?.UserId ?? "system"
    })
    select unit;

Check roles in an effect pipeline

var adminOnlyOperation =
    from roles in IdentityModule.Identity.GetRolesAsync<AppRuntime>(userId)
    from _ in Guard<AppRuntime>(roles.Any(r => r.RoleName == "admin"),
        Error.New(403, "Admin role required"))
    from result in PerformAdminAction()
    select result;

Integration with Dispatch Authorization

The [Authorize], [Role], and [Permissions] attributes on dispatch handlers are evaluated before the handler runs — they use ICurrentUser automatically. Effects-based identity queries are for programmatic role management, not handler authorization. See Authorization for attribute-based access control.