Skip to content

Commit baee7bc

Browse files
committed
chore(webapp,rbac): remove additional API key rollout flags
1 parent 56823c3 commit baee7bc

18 files changed

Lines changed: 33 additions & 392 deletions

apps/webapp/app/models/api-key.server.ts

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import { customAlphabet } from "nanoid";
88
import { MAX_API_KEY_TASK_IDENTIFIERS } from "~/consts";
99
import { $transaction, boundedIn, prisma } from "~/db.server";
1010
import { RuntimeEnvironmentType } from "~/database-types";
11-
import { canIssueAdditionalApiKeys } from "~/services/additionalApiKeyIssuance.server";
1211
import { apiKeyTelemetry, type ApiKeyTelemetry } from "~/services/apiKeyTelemetry.server";
1312
import { rbac } from "~/services/rbac.server";
1413
import { generateAdditionalApiKey, generateRootApiKey } from "~/utils/apiKeys";
@@ -225,15 +224,10 @@ export async function createEnvironmentApiKey(
225224
{
226225
prismaClient = prisma,
227226
rbacController = rbac,
228-
issuanceAllowed,
229227
telemetryRecorder = apiKeyTelemetry,
230228
}: {
231-
prismaClient?: Pick<
232-
PrismaClient,
233-
"apiKey" | "featureFlag" | "organization" | "runtimeEnvironment" | "taskIdentifier"
234-
>;
229+
prismaClient?: Pick<PrismaClient, "apiKey" | "runtimeEnvironment" | "taskIdentifier">;
235230
rbacController?: Pick<HostRbacController, "prepareApiKeyPolicy">;
236-
issuanceAllowed?: (organizationId: string) => Promise<boolean>;
237231
telemetryRecorder?: ApiKeyTelemetry;
238232
} = {}
239233
) {
@@ -249,13 +243,6 @@ export async function createEnvironmentApiKey(
249243
throw new Error("Environment not found");
250244
}
251245

252-
const canIssue =
253-
issuanceAllowed ??
254-
((organizationId) => canIssueAdditionalApiKeys(organizationId, prismaClient));
255-
if (!(await canIssue(environment.organizationId))) {
256-
throw new Error("Creating additional API keys is not enabled.");
257-
}
258-
259246
if (expiresAt && expiresAt.getTime() <= Date.now()) {
260247
throw new Error("Expiration must be in the future");
261248
}

apps/webapp/app/models/runtimeEnvironment.server.ts

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import { BuildRuntime } from "@trigger.dev/core/v3";
1010
import { isAdditionalApiKey } from "@trigger.dev/core/v3/apiKeys";
1111
import { isDefaultDevBranch, sanitizeBranchName } from "@trigger.dev/core/v3/utils/gitBranch";
1212
import { scopesGrantFullAccess } from "@trigger.dev/rbac";
13-
import { authFeatureControls } from "~/services/authFeatureControls.server";
1413

1514
export type { RuntimeEnvironment };
1615

@@ -101,7 +100,7 @@ export function toAuthenticated(
101100

102101
export type ApiKeyEnvironmentResolution =
103102
| { ok: true; environment: AuthenticatedEnvironment }
104-
| { ok: false; reason: "not-found" | "restricted" | "disabled" };
103+
| { ok: false; reason: "not-found" | "restricted" };
105104

106105
/**
107106
* Resolve an environment from a raw API key for legacy routes that do not
@@ -112,8 +111,7 @@ export type ApiKeyEnvironmentResolution =
112111
async function resolveEnvironmentByApiKey(
113112
apiKey: string,
114113
branchName: string | undefined,
115-
tx: PrismaClientOrTransaction,
116-
additionalApiKeyLookupEnabled: () => boolean
114+
tx: PrismaClientOrTransaction
117115
): Promise<ApiKeyEnvironmentResolution> {
118116
const branch = sanitizeBranchName(branchName) ?? undefined;
119117

@@ -131,9 +129,6 @@ async function resolveEnvironmentByApiKey(
131129

132130
const now = new Date();
133131
const routesToAdditionalKey = isAdditionalApiKey(apiKey);
134-
if (routesToAdditionalKey && !additionalApiKeyLookupEnabled()) {
135-
return { ok: false, reason: "disabled" };
136-
}
137132

138133
let rootEnvironment = routesToAdditionalKey
139134
? null
@@ -277,15 +272,9 @@ async function resolveEnvironmentByApiKey(
277272
export async function findEnvironmentByApiKey(
278273
apiKey: string,
279274
branchName: string | undefined,
280-
tx: PrismaClientOrTransaction = $replica,
281-
additionalApiKeyLookupEnabled = authFeatureControls.additionalApiKeyLookupEnabled
275+
tx: PrismaClientOrTransaction = $replica
282276
): Promise<AuthenticatedEnvironment | null> {
283-
const resolution = await resolveEnvironmentByApiKey(
284-
apiKey,
285-
branchName,
286-
tx,
287-
additionalApiKeyLookupEnabled
288-
);
277+
const resolution = await resolveEnvironmentByApiKey(apiKey, branchName, tx);
289278
return resolution.ok ? resolution.environment : null;
290279
}
291280

@@ -297,10 +286,9 @@ export async function findEnvironmentByApiKey(
297286
export async function findEnvironmentByApiKeyWithResolution(
298287
apiKey: string,
299288
branchName: string | undefined,
300-
tx: PrismaClientOrTransaction = $replica,
301-
additionalApiKeyLookupEnabled = authFeatureControls.additionalApiKeyLookupEnabled
289+
tx: PrismaClientOrTransaction = $replica
302290
): Promise<ApiKeyEnvironmentResolution> {
303-
return resolveEnvironmentByApiKey(apiKey, branchName, tx, additionalApiKeyLookupEnabled);
291+
return resolveEnvironmentByApiKey(apiKey, branchName, tx);
304292
}
305293

306294
export type PrivateApiKeyRateLimitScope = {

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.apikeys/route.tsx

Lines changed: 9 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -64,14 +64,12 @@ import {
6464
typedJsonWithErrorMessage,
6565
typedJsonWithSuccessMessage,
6666
} from "~/models/message.server";
67-
import { resolveOrgIdFromSlug } from "~/models/organization.server";
6867
import { findProjectBySlug } from "~/models/project.server";
6968
import { findEnvironmentBySlug } from "~/models/runtimeEnvironment.server";
7069
import { ApiKeysPresenter } from "~/presenters/v3/ApiKeysPresenter.server";
7170
import { useFeatures } from "~/hooks/useFeatures";
7271
import { useOrganization } from "~/hooks/useOrganizations";
7372
import { useShowSelfServe } from "~/hooks/useShowSelfServe";
74-
import { canIssueAdditionalApiKeys } from "~/services/additionalApiKeyIssuance.server";
7573
import {
7674
validateCreateApiKeyPreset,
7775
type ApiKeyPreset,
@@ -135,25 +133,18 @@ export const loader = dashboardLoader(
135133
{
136134
params: EnvironmentParamSchema,
137135
searchParams: ApiKeySearchParams,
138-
context: async (params) => {
139-
const organizationId = await resolveOrgIdFromSlug(params.organizationSlug);
140-
return organizationId ? { organizationId } : {};
141-
},
142136
},
143-
async ({ params, searchParams, user, ability, context }) => {
137+
async ({ params, searchParams, user, ability }) => {
144138
try {
145139
const presenter = new ApiKeysPresenter();
146-
const [data, additionalApiKeyIssuanceEnabled, isRbacPluginAvailable] = await Promise.all([
140+
const [data, isRbacPluginAvailable] = await Promise.all([
147141
presenter.call({
148142
userId: user.id,
149143
organizationSlug: params.organizationSlug,
150144
projectSlug: params.projectParam,
151145
environmentSlug: params.envParam,
152146
showRevoked: searchParams.showRevoked,
153147
}),
154-
context.organizationId
155-
? canIssueAdditionalApiKeys(context.organizationId)
156-
: Promise.resolve(false),
157148
rbac.isUsingPlugin(),
158149
]);
159150

@@ -176,7 +167,6 @@ export const loader = dashboardLoader(
176167
apiKeys: canReadApiKeys ? data.apiKeys : [],
177168
canReadApiKeys,
178169
canWriteApiKeys,
179-
additionalApiKeyIssuanceEnabled,
180170
isRbacPluginAvailable,
181171
showRevoked: searchParams.showRevoked ?? false,
182172
loadedAt: Date.now(),
@@ -194,10 +184,6 @@ export const loader = dashboardLoader(
194184
export const action = dashboardAction(
195185
{
196186
params: EnvironmentParamSchema,
197-
context: async (params) => {
198-
const organizationId = await resolveOrgIdFromSlug(params.organizationSlug);
199-
return organizationId ? { organizationId } : {};
200-
},
201187
// The environment tier is only known after resolving the route params,
202188
// so write:apiKeys is enforced in the handler before any mutation.
203189
},
@@ -241,15 +227,6 @@ export const action = dashboardAction(
241227
try {
242228
switch (submission.data.action) {
243229
case "create": {
244-
if (!(await canIssueAdditionalApiKeys(project.organizationId))) {
245-
const message = "Creating additional API keys is not enabled.";
246-
return typedJsonWithErrorMessage(
247-
{ ok: false as const, error: message },
248-
request,
249-
message
250-
);
251-
}
252-
253230
const presets = await rbac.apiKeyPresets(project.organizationId);
254231
const preset = validateCreateApiKeyPreset({
255232
presets,
@@ -321,7 +298,6 @@ export default function Page() {
321298
apiKeys,
322299
canReadApiKeys,
323300
canWriteApiKeys,
324-
additionalApiKeyIssuanceEnabled,
325301
isRbacPluginAvailable,
326302
showRevoked,
327303
hasVercelIntegration,
@@ -386,15 +362,13 @@ export default function Page() {
386362
/>
387363
) : null}
388364
<RevokedFilter checked={showRevoked} />
389-
{additionalApiKeyIssuanceEnabled ? (
390-
<NewApiKeyDialog
391-
canWrite={canWriteApiKeys}
392-
availableTasks={availableTasks}
393-
presets={presets}
394-
isRbacPluginAvailable={isRbacPluginAvailable}
395-
environment={apiKeyEnvironmentLabel}
396-
/>
397-
) : null}
365+
<NewApiKeyDialog
366+
canWrite={canWriteApiKeys}
367+
availableTasks={availableTasks}
368+
presets={presets}
369+
isRbacPluginAvailable={isRbacPluginAvailable}
370+
environment={apiKeyEnvironmentLabel}
371+
/>
398372
</div>
399373
</div>
400374

apps/webapp/app/services/additionalApiKeyIssuance.server.ts

Lines changed: 0 additions & 35 deletions
This file was deleted.

apps/webapp/app/services/additionalApiKeyIssuance.ts

Lines changed: 0 additions & 19 deletions
This file was deleted.

apps/webapp/app/services/authFeatureControls.server.ts

Lines changed: 0 additions & 10 deletions
This file was deleted.

apps/webapp/app/services/authFeatureControls.ts

Lines changed: 0 additions & 16 deletions
This file was deleted.

apps/webapp/app/services/authTelemetry.server.ts

Lines changed: 6 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,10 @@ import type {
88
HostBearerAuthResult,
99
RbacResource,
1010
} from "@trigger.dev/rbac";
11-
import { authFeatureControls } from "~/services/authFeatureControls.server";
1211
import { rbac } from "~/services/rbac.server";
1312
import { singleton } from "~/utils/singleton";
1413

15-
type ApiAuthResult = "success" | "invalid" | "forbidden" | "disabled" | "error";
14+
type ApiAuthResult = "success" | "invalid" | "forbidden" | "error";
1615

1716
const telemetry = singleton("apiAuthTelemetry", () => {
1817
const meter = getMeter("api-auth");
@@ -24,17 +23,6 @@ const telemetry = singleton("apiAuthTelemetry", () => {
2423
unit: "ms",
2524
});
2625

27-
meter
28-
.createObservableGauge("api_auth.rollout_mode", {
29-
description: "Active API authentication rollout modes",
30-
})
31-
.addCallback((result) => {
32-
result.observe(1, {
33-
control: "additional_key_lookup",
34-
mode: authFeatureControls.additionalApiKeyLookupEnabled() ? "enabled" : "disabled",
35-
});
36-
});
37-
3826
return { attempts, duration };
3927
});
4028

@@ -54,13 +42,7 @@ export async function authenticateBearerWithTelemetry(
5442
final = {
5543
credentialKind: resolution.credentialKind,
5644
lookupPath: resolution.lookupPath,
57-
result: result.ok
58-
? "success"
59-
: resolution.lookupPath === "additional_skipped"
60-
? "disabled"
61-
: result.status === 403
62-
? "forbidden"
63-
: "invalid",
45+
result: result.ok ? "success" : result.status === 403 ? "forbidden" : "invalid",
6446
};
6547
recordAuthAttempt("rbac", final.credentialKind, final.lookupPath, final.result);
6648
return result;
@@ -102,27 +84,22 @@ export async function observeLegacyBearerAuthentication<T extends { ok: boolean
10284
): Promise<T> {
10385
const startedAt = performance.now();
10486
const classified = classifyCredential(request, true);
105-
const lookupPath: BearerLookupPath =
106-
classified.credentialKind === "additional_api_key" &&
107-
!authFeatureControls.additionalApiKeyLookupEnabled()
108-
? "additional_skipped"
109-
: classified.lookupPath;
11087
let result: ApiAuthResult = "error";
11188

11289
try {
11390
const value = await operation();
114-
result = value?.ok ? "success" : lookupPath === "additional_skipped" ? "disabled" : "invalid";
115-
recordAuthAttempt("legacy", classified.credentialKind, lookupPath, result);
91+
result = value?.ok ? "success" : "invalid";
92+
recordAuthAttempt("legacy", classified.credentialKind, classified.lookupPath, result);
11693
return value;
11794
} catch (error) {
118-
recordAuthAttempt("legacy", classified.credentialKind, lookupPath, result);
95+
recordAuthAttempt("legacy", classified.credentialKind, classified.lookupPath, result);
11996
throw error;
12097
} finally {
12198
telemetry.duration.record(performance.now() - startedAt, {
12299
resolver: "legacy",
123100
credential_kind: classified.credentialKind,
124101
result,
125-
lookup_path: lookupPath,
102+
lookup_path: classified.lookupPath,
126103
});
127104
}
128105
}

apps/webapp/app/services/rbac.server.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { $replica, prisma } from "~/db.server";
22
import type { PrismaClient } from "@trigger.dev/database";
33
import plugin from "@trigger.dev/rbac";
44
import { env } from "~/env.server";
5-
import { authFeatureControls } from "~/services/authFeatureControls.server";
65

76
// plugin.create() is synchronous — returns a lazy controller that resolves
87
// any installed RBAC plugin on first call. Top-level await is not used
@@ -31,7 +30,6 @@ export const rbac = plugin.create(
3130
{
3231
forceFallback: env.RBAC_FORCE_FALLBACK,
3332
userActorSecret: env.SESSION_SECRET,
34-
additionalApiKeyLookupEnabled: authFeatureControls.additionalApiKeyLookupEnabled,
3533
// A plugin that owns its own database client gets the same
3634
// writer/replica topology the webapp's Prisma clients use (see
3735
// getClient/getReplicaClient in db.server.ts): control-plane URLs win,

0 commit comments

Comments
 (0)