Skip to content

Decorators ​

This page is the canonical reference for every decorator the two packages export. If a decorator name does not appear here, it does not exist — see the No-such-decorator section below.

Matrix ​

DecoratorTargetPackageEffect
@Public()class / method@aooth/auth-moostSets BOTH authPublic=true AND arbacPublic=true on the mate. Single decorator hides the route from both guards.
@UserId()parameter@aooth/auth-moostResolves to useAuth().getUserId(). Throws 401 if no context.
@AuthGuarded(opts)class / method@aooth/auth-moostSugar for @Intercept(authGuardInterceptor(opts)).
@ArbacResource(name)class / method@aooth/arbac-moostSets arbacResourceId on the mate.
@ArbacAction(name)class / method (typically method)@aooth/arbac-moostSets arbacActionId on the mate.
@ArbacAuthorize()class / method@aooth/arbac-moostWraps arbacAuthorizeInterceptor via Authenticate() so swagger picks up auth-guard metadata.
@WfTrigger({ allow?, token? })method@aooth/auth-moostWraps a method in defineAfterInterceptor at INTERCEPTOR priority; instantiates WfTriggerProvider and replies with its handle(opts) when the inner handler returns undefined.

@Public() ​

ts
import { Public } from "@aooth/auth-moost";

@Public()
@Post("logout")
async logout() { /* ... */ }

The dual-purpose decorator. Writes authPublic=true AND arbacPublic=true on the moost mate. Effect:

  • Auth guard: no token → null context, handler runs anonymously. Invalid token → still null context (never throws).
  • ARBAC interceptor: short-circuit, no scope resolution. useArbac().isPublic reads true.

@Public() is dual-purpose — you cannot ARBAC-gate an @Public() route

This is the single biggest foot-gun the API tries to prevent. The combined write was an explicit design choice: an earlier draft had @AuthPublic() and @ArbacPublic() as separate decorators, and consumers kept forgetting one, producing routes that were "public to the auth guard but ARBAC-denied to anonymous callers" or vice versa.

If you genuinely need "anonymous, but ARBAC-checked" semantics (e.g. an anonymous role on MoostArbac), apply only @ArbacResource / @ArbacAction without @Public() and configure the auth guard to issue an anonymous context.

@UserId() ​

ts
import { UserId } from "@aooth/auth-moost";

@Get("me")
whoami(@UserId() userId: string) {
  return { userId };
}

A parameter decorator. Delegates to Resolve(() => useAuth().getUserId()). Throws HttpError(401, "Not authenticated") when no context — same as the underlying getUserId() call.

There is no @User() counterpart by design: AuthContext is credential context only (userId + claims + token metadata). The user record lives in your UserService/database — fetch it with await userService.getUser(useAuth().getUserId()) if you need it inside the handler.

@AuthGuarded(opts) ​

Sugar for @Intercept(authGuardInterceptor(opts)). Use it when you want a per-controller guard instead of the global one:

ts
@Controller("admin")
@AuthGuarded({ cookie: { name: "admin_session", path: "/admin" } })
class AdminController {
  /* ... */
}

@ArbacResource(name) and @ArbacAction(name) ​

ts
@Controller("articles")
@ArbacResource("articles")
class ArticlesController {
  @Get(":id")
  @ArbacAction("read")
  async read(@Param("id") id: string) {
    /* ... */
  }

  @Post()
  @ArbacAction("create")
  async create(@Body() body: NewArticle) {
    /* ... */
  }
}

Both write through getArbacMate() which is the shared moost Mate typed with TArbacMeta = { arbacResourceId?, arbacActionId?, arbacPublic? }. TArbacMeta is declaration-merged into Moost's TMoostMetadata.

@ArbacResource is most naturally a class decorator (one resource per controller). @ArbacAction is most naturally a method decorator (one action per endpoint). Both can be applied at either level — see the resolution chain.

@ArbacAuthorize() ​

Wraps arbacAuthorizeInterceptor via Authenticate(arbacAuthorizeInterceptor). Two reasons to use it:

  1. You apply the interceptor per-handler instead of globally.
  2. You want @moostjs/swagger to mark the endpoint as auth-required in the generated OpenAPI document.

When the interceptor is global, @ArbacAuthorize() is structurally redundant (attaches the same interceptor at the same priority — moost dedupes by reference).

@WfTrigger({ allow?, token? }) ​

Method decorator used on the AuthController.triggerWf() endpoint (and your subclasses of it). When the wrapped handler returns undefined, the after-interceptor instantiates WfTriggerProvider and replies with its handle(opts). Subclasses that need to short-circuit return any non-undefined value.

ts
@Inherit()
@Controller("auth")
class MyAuthController extends AuthController {
  @Post("trigger")
  @Public()
  @WfTrigger({ allow: [...DEFAULT_AUTH_WORKFLOWS, "project.handover"] })
  override triggerWf(): void {
    /* intentionally empty */
  }
}

See REST Controllers and Workflows for full coverage of the trigger surface.

Mate types ​

The mate shape is two small interfaces, declaration-merged into Moost's TMoostMetadata:

ts
// from @aooth/auth-moost
interface TAuthMeta {
  authPublic?: boolean;
}

// from @aooth/arbac-moost
interface TArbacMeta {
  arbacResourceId?: string;
  arbacActionId?: string;
  arbacPublic?: boolean;
}

Accessors:

ts
import { getAuthMate } from "@aooth/auth-moost";
import { getArbacMate } from "@aooth/arbac-moost";

const authMeta = getAuthMate().read(SomeClass); // class-level
const authMeta = getAuthMate().read(SomeClass.prototype, "methodName"); // method-level

Most apps never touch the mate directly. The decorators above are the supported surface.

Decorators that do NOT exist ​

The following names are commonly searched for but are not exported. Don't try to import them.

NameWhy it doesn't existUse instead
@ArbacPublic()The split-decorator design was a foot-gun; combined into auth-moost's @Public().@Public() from @aooth/auth-moost.
@AuthPublic()Same.@Public().
@ArbacScopes()Scopes are a runtime concept (produced by the engine, not declared via metadata).useArbac().getScopes<TScope>() directly in the handler.
@User()AuthContext is credential context, not user record.useAuth().getAuthContext() for the credential context, or userService.getUser(useAuth().getUserId()) for the user row.
@Roles('admin')Roles live in MoostArbac rules, not in route metadata.arbac.registerRole(defineRole().allow(...).build()) and let the interceptor enforce it.

No @ArbacPublic() — use @Public()

@ArbacPublic is not exported from @aooth/arbac-moost. The arbacPublic mate flag is written by @aooth/auth-moost's combined @Public(). The two packages share this single decorator on purpose; splitting them was a foot-gun.

See also ​

Released under the MIT License.