How the Authentication Process Works in OpenWork with better-auth Integration
OpenWork uses the better-auth library as its central authentication engine, configured in ee/apps/den-api/src/auth.ts with a Drizzle ORM adapter, organization-based access control, and custom middleware hooks to secure email, OAuth, SSO, and SCIM flows.
The different-ai/openwork repository implements a sophisticated authentication process using the extensible better-auth framework. By centralizing configuration in a single TypeScript file and layering custom business logic through plugins and access control lists, OpenWork secures multi-tenant API endpoints while supporting Managed Control Plane (MCP) integrations.
Initializing the Better-Auth Server
The authentication pipeline begins in ee/apps/den-api/src/auth.ts where the betterAuth() function is instantiated with a comprehensive configuration object (lines 78-84). This setup defines the baseURL, a cryptographic secret, trusted origins, and social providers. Critically, it initializes a Drizzle adapter that connects better-auth to OpenWork’s MySQL schema, allowing the library to persist users, sessions, and organizations in the application database.
import { auth } from "./auth.ts";
export const server = createServer({
middleware: [auth.middleware],
});
Session Lifecycle and Organization Context
Session management is customized through the session.create.before hook (lines 252-267). OpenWork sets a configurable lifetime via DEN_SESSION_EXPIRES_IN_SECONDS and automatically refreshes sessions. During creation, the hook injects the user’s active organization ID directly into the session payload, ensuring that every authenticated request carries the necessary tenant context for downstream authorization checks.
Organization-Based Access Control
Access control relies on the better-auth organization plugin wired to a custom ACL defined in ee/apps/den-api/src/organization-access.ts. The denOrganizationAccess catalog maps organization roles—such as owner, admin, and member—to specific permissions. When protecting routes, the auth.protect method validates the session’s organization membership and checks the user’s role against the required action.
import { auth } from "./auth.ts";
import { denOrganizationAccess } from "./organization-access.ts";
export const secureEndpoint = auth.protect(async (ctx) => {
const { session } = ctx;
const orgId = session.activeOrganizationId!;
const member = await ctx.context.getMember({ organizationId: orgId, userId: session.user.id });
if (!denOrganizationAccess.can(member.role, "security_configuration", "manage")) {
throw new APIError("FORBIDDEN", { message: "Insufficient permissions" });
}
return { ok: true };
});
MCP OAuth Integration
For Managed Control Plane authentication, the oauthProvider plugin is configured with custom token prefixes (ow_mcp_at_ for access tokens and ow_mcp_rt_ for refresh tokens). A specialized post-login flow forces users to select an organization before MCP scopes are granted. The shouldRedirect logic checks if requested scopes include the mcp: prefix and if the session lacks an activeOrganizationId, redirecting to the organization selection page when necessary (lines 380-406).
auth.oauthProvider.postLogin = {
page: `${env.betterAuthUrl}/mcp/select-organization`,
shouldRedirect: async ({ session, scopes }) => {
return scopes.some((s) => s.startsWith("mcp:")) && !session.activeOrganizationId;
},
consentReferenceId: async ({ session }) => session.activeOrganizationId,
};
SCIM, SSO, and Email Authentication
Enterprise features are enabled through dedicated plugins. The scim and sso plugins (lines 418-424) allow Just-In-Time (JIT) organization provisioning and domain-verified single sign-on. Traditional authentication is handled by the emailAndPassword plugin (lines 426-438), which supports email verification, password reset flows, and automatic session revocation upon password changes.
Middleware Enforcement and Rate Limiting
Business rules are enforced via createAuthMiddleware wrappers registered in the hooks.before and hooks.after configuration (lines 442-525). This middleware validates live MCP sessions, enforces invitation role constraints, and blocks disallowed Better-Auth mutations—such as organization deletion—through the getRawBetterAuthMutationDenial utility. Additionally, the configuration applies rate limiting to auth endpoints and tracks token usage with custom prefixes (lines 540-559).
Summary
- OpenWork centralizes its authentication process in
ee/apps/den-api/src/auth.tsusing thebetterAuth()constructor with a Drizzle MySQL adapter. - The session lifecycle is customized via hooks to inject the active organization ID, enabling multi-tenant access control.
- Role-based permissions are enforced through a custom ACL (
denOrganizationAccess) integrated with the better-auth organization plugin. - MCP OAuth flows use custom token prefixes and a forced organization selection post-login step before issuing tokens.
- Enterprise features (SCIM, SSO, email/password) are implemented as better-auth plugins, with request-level middleware blocking unauthorized mutations and applying rate limits.
Frequently Asked Questions
How is the database adapter configured for better-auth in OpenWork?
According to the source code in ee/apps/den-api/src/auth.ts, the authentication instance uses a Drizzle adapter pointing to the application's MySQL schema, instantiated within the betterAuth({ ... }) configuration block at lines 78-84.
What triggers the organization selection screen during MCP authentication?
The oauthProvider plugin defines a postLogin hook that checks if requested scopes include the mcp: prefix and if the session lacks an activeOrganizationId. If both conditions are true, the user is redirected to the organization selection page before tokens are issued.
How does OpenWork restrict destructive operations on organizations?
A custom middleware layer created with createAuthMiddleware intercepts requests to block specific Better-Auth mutations—such as organization deletion—via the getRawBetterAuthMutationDenial utility, while also enforcing invitation role rules and enterprise authentication requirements.
Which better-auth plugins enable enterprise user provisioning?
OpenWork adds the scim and sso plugins to the better-auth configuration, allowing Just-In-Time (JIT) organization provisioning and domain-verified single sign-on, alongside the emailAndPassword plugin for traditional credential management.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →