Target by plan or role
Section titled “Target by plan or role”This is one of the biggest advantages of building flags on Bridge instead of
in isolation: Bridge already knows who’s signed in, what role they have, and
what their workspace (called a tenant in the API) is paying for. On a
backend, though, there’s one honest difference from the frontend worth stating
up front. In a browser SDK, if Bridge auth is enabled, user.role and
tenant.plan are already in every evaluation with no app code. A NestJS
process isn’t a signed-in user, so nothing about role or plan reaches the
evaluator automatically; you wire it in once, with an attribute provider
that reads your already-verified JWT claims.
That’s actually the safer default: the value can only come from a source you trust, never from something a client handed you.
Register an attribute provider
Section titled “Register an attribute provider”AuthAttributeProvider (from @nebulr-group/bridge-auth-core) takes a
synchronous getClaims() callback and flattens the returned JWT claims into
targeting attributes. Register it once at bootstrap on the underlying bridge
instance:
import { AuthAttributeProvider } from '@nebulr-group/bridge-auth-core';
import { BridgeFlagsService } from '@nebulr-group/bridge-nestjs/flags';
// once at bootstrap, e.g. in an OnModuleInit
this.flags.bridge.registerAttributeProvider(
new AuthAttributeProvider({ getClaims: () => getCurrentClaims() }),
);
The provider is consulted synchronously on every eval, so getClaims() must
return the current request’s decoded claims (or undefined when there are
none). It maps claims into attributes like this:
| Attribute | Claim | Example values |
|---|---|---|
| user.id | sub | the signed-in user’s id |
| user.role | role | MEMBER, ADMIN, OWNER (or your custom roles) |
| user.email | email | jane@acme.com |
| tenant.id | tid | the current workspace’s ID |
| tenant.plan | plan | FREE, PRO, ENTERPRISE |
| privileges | privileges | the signed-in user’s privilege list |
Namespacing
Section titled “Namespacing”Auth-derived attributes (user.id, user.role, user.email, tenant.id,
tenant.plan, privileges) aren’t prefixed: a targeting rule references
user.role, tenant.plan, and so on, exactly as it would against a Bridge
frontend. Billing-derived attributes are prefixed, under bridge:billing.*.
The provider itself counts as bridge-managed, so any dev-supplied attribute
you pass on a flag() call wins on key collision, and Control Center surfaces
the collision on the flag detail page.
Read claims from a verified source only
Section titled “Read claims from a verified source only”getClaims() must read from a server-verified source (req.bridgeUser or
req.bridgeApiToken, populated by BridgeAuthGuard) and never from anything
a client supplied. This is the same “never trust client-sent role/plan
attributes” rule as forwarded context: a browser can put any role in the
x-bridge-context header, so plan/role targeting has to resolve from your own
JWT, not from the wire.
Example: gate a feature by role
Section titled “Example: gate a feature by role”With the provider registered, an admin builds the rule once in Control Center
(your admin dashboard at app.thebridge.dev): on for users matching
user.role equals ADMIN. Your gated route just works, with no
attribute-passing code at the call site:
import { Controller, Get, UseGuards } from '@nestjs/common';
import { BridgeAuthGuard } from '@nebulr-group/bridge-nestjs';
import { RequireFlag, BridgeFlagGuard } from '@nebulr-group/bridge-nestjs/flags';
@Controller('billing-settings')
@UseGuards(BridgeAuthGuard, BridgeFlagGuard) // auth first, so claims are populated
export class BillingSettingsController {
@Get()
@RequireFlag('billing_settings')
get() { /* … */ }
}
Example: gate a feature by plan
Section titled “Example: gate a feature by plan”Same pattern for plan-gating a premium feature. Rule: on for users matching
tenant.plan equals ENTERPRISE:
@Get('export')
@RequireFlag('export_reports')
exportReports() { /* … */ }
If you’re gating something billing already grants access to, prefer targeting a billing entitlement over the raw plan name; it survives plan renames and custom per-workspace grants. See Lock features to a plan.
When to reach for the on-demand flag path instead
Section titled “When to reach for the on-demand flag path instead”The main @nebulr-group/bridge-nestjs entry point also ships an older
@RequireFeatureFlag / FeatureFlagService that evaluates over the Bridge
API, keyed on the caller’s access token. Because Bridge decodes that token
itself, role and plan targeting resolve with no provider wiring. If a
role-targeted rollout is all you need and you don’t want a live flags client,
that path is less setup. See
Gate features by role or privilege for the
full comparison.