Service principals
Create and manage machine identities and service-owned API keys with convex-auth.
convex-auth supports service principals: machine identities with their own permissions that can own API keys and act on your backend. The @vortex-api/convex-auth/react package ships useConvexServicePrincipals, which wires a consumer Convex API to a create form and list.
How it works
- Service principals live in the
service_principalscomponent table. - Each principal has a
key,name,description,status(activeordisabled), andpermissions. - A principal is scoped to an organization.
- You can associate service-owned API keys with a principal so the principal’s permissions can be enforced at request time.
Mount the service principals component
Mount the servicePrincipals component alongside the core and apiKeys components:
import { defineApp } from "convex/server";
import { v } from "convex/values";
import authCore from "@vortex-api/convex-auth/convex.config/core";
import authServicePrincipals from "@vortex-api/convex-auth/convex.config/servicePrincipals";
const app = defineApp({
env: {
JWT_PRIVATE_KEY: v.string(),
JWKS: v.string(),
},
});
app.use(authCore, {
env: {
JWT_PRIVATE_KEY: app.env.JWT_PRIVATE_KEY,
JWKS: app.env.JWKS,
},
});
app.use(authServicePrincipals, {
env: {
JWT_PRIVATE_KEY: app.env.JWT_PRIVATE_KEY,
JWKS: app.env.JWKS,
},
});
export default app;
Consumer functions
Add queries and mutations in your own convex/ directory. The example app uses:
api.servicePrincipals.listMyServicePrincipals— list principals for the active organization.api.servicePrincipals.createServicePrincipal— create a new principal.api.servicePrincipals.updateServicePrincipal— update name, description, or permissions.api.servicePrincipals.setServicePrincipalStatus— enable or disable a principal.
React hook
useConvexServicePrincipals
import { useConvexServicePrincipals } from "@vortex-api/convex-auth/react";
import { api } from "../convex/_generated/api";
const PERMISSIONS = ["read", "write", "delete"] as const;
export function ServicePrincipals() {
const {
servicePrincipals,
state,
creating,
onKeyChange,
onNameChange,
onDescriptionChange,
onPermissionsChange,
onSubmit,
onStatusChange,
} = useConvexServicePrincipals(api, { permissionOptions: PERMISSIONS });
return (
<>
<ServicePrincipalCreateForm
state={state}
creating={creating}
onKeyChange={onKeyChange}
onNameChange={onNameChange}
onDescriptionChange={onDescriptionChange}
onPermissionsChange={onPermissionsChange}
onSubmit={onSubmit}
/>
<ServicePrincipalList servicePrincipals={servicePrincipals} onStatusChange={onStatusChange} />
</>
);
}
The hook returns:
servicePrincipals— the list fromapi.servicePrincipals.listMyServicePrincipals.state— the current create-form state.creating— whether a create is in flight.onKeyChange,onNameChange,onDescriptionChange,onPermissionsChange— form change handlers.onSubmit— creates a principal from the form state.onStatusChange(servicePrincipalId, status)— enables or disables a principal.
Service-owned API keys
Once a service principal is created, you can issue API keys owned by that principal. The consumer createApiKey can set the principal as the owner by passing servicePrincipalId (or letting the component’s issueServiceOwnedApiKey path handle it). A principal’s permissions are checked when the service-owned key is used to authorize a request.