Authentication & Authorization

Configure authentication strategies and entity-level authorization in your Spiderly application.

Authentication Strategies

Spiderly supports two authentication strategies. Both use JWT tokens under the hood — the difference is how tokens are transported between client and server.

Token-Based (Default)

Tokens are returned in the response body and sent via the Authorization header. The client is responsible for storing and managing tokens.

Angular methods:

MethodReturnsDescription
login(request)AuthResultLogin with email verification token
loginExternal(provider)AuthResultLogin with external provider (e.g., Google)
logout(browserId)Invalidate tokens
refreshTokenWithHeaders(request)AuthResultRefresh an expired access token

AuthResult includes accessToken, refreshToken, userId, and email.

Tokens are stored in HttpOnly cookies — the browser handles storage and sends them automatically. No token management needed on the client side.

Angular methods:

MethodReturnsDescription
loginWithCookies(request)AuthResultWithCookiesLogin with email verification token
loginExternalWithCookies(provider)AuthResultWithCookiesLogin with external provider (e.g., Google)
logoutWithCookies(browserId)Clear auth cookies
refreshTokenWithCookies(browserId)AuthResultWithCookiesRefresh via cookie

AuthResultWithCookies includes userId, email, and accessTokenExpiresAt — no tokens in the response body.

Cookie-based authentication requires the [AuthGuard] attribute's CSRF protection. All state-changing requests (POST, PUT, DELETE, PATCH) must include the X-CSRF: "1" header.

Choosing a Strategy

Token-BasedCookie-Based
Best forMobile apps, cross-domain APIs, SPAs with explicit token handlingSame-domain/subdomain setups, simpler frontend code
Token storageClient manages (localStorage, memory)Browser manages (HttpOnly cookies)
CSRF protectionNot needed (tokens in headers)Required (X-CSRF header)
Cross-subdomainWorks out of the boxRequires CookieDomain and CookieSameSite configuration

Overview

By default, all entities require authorization for Create, Read, Update, and Delete operations. If your code encounters an authorization issue (e.g., an UnauthorizedException - You don't have the necessary rights to perform the operation.), it means the current user lacks the necessary permissions.

If your entity requires access control, follow the steps in this tutorial to configure proper authorization.

Registration

Authentication and authorization are turned on by a single call inside the AddSpiderly builder in your Startup.cs. The spiderly init template scaffolds it for you — you rarely write it by hand:

services.AddSpiderly<ApplicationDbContext>(Configuration, spiderly =>
{
    spiderly.UsePostgreSQL();
    spiderly.AddSecurity<User, UserExternalLogin, AuthorizationService>();
});

AddSecurity<TUser, TUserExternalLogin, TAuthorizationService> registers the whole auth core as one unit: the email-login and JWT services, the User principal kind, your AuthorizationService, and — crucially — the [HasPermission] handler that forwards to it. These pieces are co-required: an app with some but not all of them is silently broken, so AddSecurity bundles them all-or-nothing. A single-principal app needs nothing more.

Optional add-ons opt in through a sub-builder. API keys, for example, register a second principal kind and its auth scheme:

spiderly.AddSecurity<User, UserExternalLogin, AuthorizationService>(s => s.AddApiKeys<ApiKey>());

If you replace the scaffolded AddSecurity call with hand-rolled registration and forget the [HasPermission] handler, the app fails at startup: "Spiderly authentication is enabled, so [HasPermission] materializes a PermissionRequirement policy, but no IAuthorizationHandler that satisfies PermissionRequirement is registered." Fix it by calling services.AddSpiderlyAuthorization<AuthorizationServiceGenerated>() — or simply keep using AddSecurity, which registers the handler for you. It's a fail-loud boot guard, never a silent 403.

Authorization Attributes

[AuthGuard]

Protects API endpoints by checking the authentication status set by the authentication middleware.

For cookie-based authentication, [AuthGuard] also enforces CSRF protection on state-changing requests (POST, PUT, DELETE, PATCH). These requests must include an X-CSRF header — the value doesn't matter, only its presence is checked. If the header is missing, the request is rejected with 403 Forbidden.

Requests authenticated via the Authorization header (Bearer token) are not affected by CSRF checks. GET, HEAD, and OPTIONS requests are also not affected, regardless of the auth strategy.

[SpiderlyController]
[ApiController]
[Route("/api/[controller]/[action]")]
public class ReportController : ReportBaseController
{
    [HttpPost]
    [AuthGuard] // Authenticated users only; cookie auth requires X-CSRF header
    public async Task<IActionResult> RunExport() { /* ... */ }
}

[DoNotAuthorize]

Disables authorization checks for all CRUD operations on the decorated entity. By default, every entity requires the user to hold the relevant Read{Entity} / Insert{Entity} / Update{Entity} / Delete{Entity} permission.

[DoNotAuthorize] bypasses security checks entirely and is intended primarily for testing or for entities that are truly public. Avoid it on entities that contain user data or any value worth protecting in production.

[SpiderlyEntity]
[DoNotAuthorize] // Public catalog — no permissions required
public class PublicAnnouncement : BusinessObject<long>
{
    [DisplayName]
    public string Title { get; set; }
}

[HasPermission]

Gates a custom controller action on a specific permission code. The principal — a logged-in user or an API key — must hold that permission, or the request is rejected with 403 Forbidden. Use it on endpoints you write yourself; entity CRUD already enforces the default Read{Entity} / Insert{Entity} / Update{Entity} / Delete{Entity} permissions with no attribute needed.

[SpiderlyController]
[ApiController]
[Route("/api/[controller]/[action]")]
public class ReportController : ReportBaseController
{
    [HttpPost]
    [HasPermission("RunSalesReport")] // Caller must hold the RunSalesReport permission
    public async Task<IActionResult> RunSalesReport() { /* ... */ }
}

The permission code must be seeded and assigned to the caller's role.

Per-action permissions

For finer-grained control — e.g. requiring an extra permission on top of the default Insert{Entity} / Update{Entity} permission — see [UIAdditionalPermissionCodeForInsert] and [UIAdditionalPermissionCodeForUpdate] in UI Customization — Per-Action Permissions.

Add Permissions to Your Application

Navigate to your ApplicationDbContext.cs file:

Backend\{your-app-name}.Infrastructure\{your-app-name}ApplicationDbContext.cs

In the SeedData method, add your entity permissions to the permissions array. Replace YourEntityName with your actual entity name:

private static void SeedData(ModelBuilder modelBuilder)
{
    Permission[] permissions =
    [
        // ... existing permissions ...

        // Add your new entity permissions
        new Permission { Id = 13, Name = "View YourEntityName", Code = "ReadYourEntityName" },
        new Permission { Id = 14, Name = "Edit existing YourEntityName", Code = "UpdateYourEntityName" },
        new Permission { Id = 15, Name = "Add new YourEntityName", Code = "InsertYourEntityName" },
        new Permission { Id = 16, Name = "Delete YourEntityName", Code = "DeleteYourEntityName" },
    ];

    modelBuilder.Entity<Permission>().HasData(permissions);

    // ... rest of seed data ...
}

Important: Make sure to use sequential IDs that don't conflict with existing permissions.

After adding the permissions, create and apply a migration:

spiderly add-migration AddYourEntityNamePermissions
spiderly update-database

Assign Permissions to a Role

In the application UI:

  1. Navigate to Administration → Roles.
  2. Select the role you want to modify.
  3. In the Permissions control, add the newly created permissions.

This ensures users assigned to this role will have access to the specified entity operations.

See Also