Skip to content

Authentication

Crate integrates with vibe.d’s OAuth2 authenticator. Call enable() on the router to add token validation to all routes:

import vibeauth.authenticators.OAuth2;
auto crateRouter = router.crateSetup!RestApi;
auto oauth = new OAuth2(/* config */);
crateRouter.enable(oauth);
crateRouter.add(userCrate);
crateRouter.add(productCrate);

The enable() method registers OAuth2 token validation middleware on the vibe.d URLRouter before Crate’s handler. Every request is validated before reaching any Crate operation.

In practice, different endpoints need different access levels. Rather than a single global auth check, Crate applications use middleware classes that implement specific access patterns.

Allows unauthenticated GET requests but requires auth for writes:

class PublicDataMiddleware {
@getList @getItem
void readAccess(HTTPServerRequest req, HTTPServerResponse res) @safe {
// No authentication required for reading
}
@create @replace @patch @delete_
void writeAccess(HTTPServerRequest req, HTTPServerResponse res) @safe {
enforceAuthenticated(req, res);
}
}

Requires authentication for all operations:

class PrivateDataMiddleware {
@any
void any(HTTPServerRequest req, HTTPServerResponse res) @safe {
enforceAuthenticated(req, res);
}
}

Requires authentication and verifies the user has contributor rights:

class ContributionMiddleware {
@any
void any(HTTPServerRequest req, HTTPServerResponse res) @safe {
enforceAuthenticated(req, res);
enforceContributor(req, res);
}
}

Different models can have different auth requirements:

auto auth = new AuthMiddlewareFactory(/* config */);
// Public read, authenticated write
crateRouter
.prepare(articleCrate)
.and(auth.publicDataMiddleware);
// Fully private
crateRouter
.prepare(settingsCrate)
.and(auth.privateDataMiddleware);
// Contributor-only
crateRouter
.prepare(issueCrate)
.and(auth.contributionMiddleware);

A common pattern is injecting the authenticated user’s data into the request context:

class UserDataMiddleware {
@any
void any(HTTPServerRequest req, HTTPServerResponse res) @safe {
if (auto user = getAuthenticatedUser(req)) {
// Make user data available to downstream middleware
req.params["userId"] = user.id;
req.params["userRole"] = user.role;
}
}
}

Then downstream middleware can use req.params["userId"] to filter data by ownership.

Check if the current user has admin privileges:

class AdminRequestMiddleware {
@any
void any(HTTPServerRequest req, HTTPServerResponse res) @safe {
auto userId = req.params.get("userId", "");
if (userId.length > 0 && isAdmin(userId)) {
req.params["isAdmin"] = "true";
}
}
}

When a model references another model, Crate needs to look up the referenced item during POST/PATCH operations. This is done via crateGetters:

// Register getters for all referenced models
crateGetters["Team"] = &teamCrate.getItem;
crateGetters["Picture"] = &pictureCrate.getItem;
// Now Campaign (which references Team and Picture) can resolve references
crateRouter.add(campaignCrate);

If you forget to register a getter, POST/PATCH requests will fail with a 500 error: “no getter for Team model”.

Every model that uses Visibility (which references Team and Picture) needs at minimum:

crateGetters["Team"] = &teamCrate.getItem;
crateGetters["Picture"] = &pictureCrate.getItem;
  • Learn about multi-protocol support for serving REST, MCP, and GraphQL