1+ import { trace } from "@internal/tracing" ;
12import { json , type ActionFunctionArgs } from "@remix-run/node" ;
23import { decodeJwt } from "jose" ;
34import { z } from "zod" ;
45import { env } from "~/env.server" ;
56import { logger } from "~/services/logger.server" ;
6- import {
7- externalOidcOutcomes ,
8- getExternalOidcController ,
9- getExternalOidcRateLimits ,
10- } from "./externalOidc.server" ;
7+ import { getExternalOidcController , getExternalOidcRateLimits } from "./externalOidc.server" ;
118import { internalRunSigningService } from "./instance.server" ;
12- import { isRunTokenFormat , verifyRunToken } from "./runTokenVerify" ;
9+ import { externalOidcTelemetry } from "./externalOidcTelemetry.server" ;
10+ import { isRunTokenFormat , verifyRunToken , type RunTokenClaims } from "./runTokenVerify" ;
1311
1412const MAX_AUDIENCE_LENGTH = 2_048 ;
1513
@@ -26,35 +24,40 @@ const TokenRequest = z
2624
2725const NO_STORE = { "Cache-Control" : "no-store" } ;
2826
29- function failure (
30- code : string ,
31- error : string ,
32- status : number ,
33- headers : Record < string , string > = { }
34- ) {
35- externalOidcOutcomes . add ( 1 , { outcome : code } ) ;
36- return json ( { error, code } , { status, headers : { ...NO_STORE , ...headers } } ) ;
37- }
38-
3927export async function externalOidcTokenAction ( { request } : ActionFunctionArgs ) {
40- if ( request . method !== "POST" ) {
41- return failure ( "METHOD_NOT_ALLOWED" , "Method not allowed" , 405 , { Allow : "POST" } ) ;
42- }
28+ const startedAt = performance . now ( ) ;
29+ const span = trace . getActiveSpan ( ) ;
30+ let outcome = "OIDC_SERVICE_UNAVAILABLE" ;
31+ let claims : RunTokenClaims | undefined ;
4332
44- const controller = await getExternalOidcController ( ) ;
45- if ( ! controller ) {
46- return failure (
47- "OIDC_UNAVAILABLE" ,
48- "External OIDC is only available on Trigger.dev Cloud" ,
49- 503 ,
50- { "x-should-retry" : "false" }
51- ) ;
33+ function failure (
34+ code : string ,
35+ error : string ,
36+ status : number ,
37+ headers : Record < string , string > = { }
38+ ) {
39+ outcome = code ;
40+ return json ( { error , code } , { status , headers : { ... NO_STORE , ... headers } } ) ;
5241 }
5342
5443 try {
44+ if ( request . method !== "POST" ) {
45+ return failure ( "METHOD_NOT_ALLOWED" , "Method not allowed" , 405 , { Allow : "POST" } ) ;
46+ }
47+
48+ const controller = await getExternalOidcController ( ) ;
49+ if ( ! controller ) {
50+ return failure (
51+ "OIDC_UNAVAILABLE" ,
52+ "External OIDC is only available on Trigger.dev Cloud" ,
53+ 503 ,
54+ { "x-should-retry" : "false" }
55+ ) ;
56+ }
57+
5558 // Only run tokens: root keys, PATs, sessions and public JWTs cannot mint.
5659 const bearer = / ^ B e a r e r ( [ ^ \s ] + ) $ / i. exec ( request . headers . get ( "Authorization" ) ?? "" ) ?. [ 1 ] ;
57- const claims =
60+ claims =
5861 bearer && env . RUN_TOKEN_ISSUER_URL && isRunTokenFormat ( bearer )
5962 ? await verifyRunToken ( {
6063 token : bearer ,
@@ -100,17 +103,40 @@ export async function externalOidcTokenAction({ request }: ActionFunctionArgs) {
100103
101104 const expiry = decodeJwt ( result . token ) . exp ;
102105 if ( typeof expiry !== "number" ) throw new Error ( "OIDC plugin returned a token without expiry" ) ;
103- externalOidcOutcomes . add ( 1 , { outcome : "minted" } ) ;
106+ outcome = "minted" ;
104107 return json (
105108 { token : result . token , expiresAt : new Date ( expiry * 1_000 ) . toISOString ( ) } ,
106109 { headers : NO_STORE }
107110 ) ;
108111 } catch ( error ) {
109- logger . error ( "External OIDC mint failed" , { error } ) ;
112+ logger . error ( "External OIDC mint failed" , {
113+ error,
114+ organizationId : claims ?. org_id ,
115+ projectId : claims ?. project_id ,
116+ environmentId : claims ?. environment_id ,
117+ runId : claims ?. run_id ,
118+ } ) ;
110119 return failure (
111120 "OIDC_SERVICE_UNAVAILABLE" ,
112121 "OIDC token service is temporarily unavailable" ,
113122 503
114123 ) ;
124+ } finally {
125+ const telemetry = externalOidcTelemetry ( ) ;
126+ telemetry . tokenRequests . add ( 1 , { outcome } ) ;
127+ telemetry . tokenDuration . record ( performance . now ( ) - startedAt , { outcome } ) ;
128+ // Reuse the request's sampling decision; tenant IDs never become metric labels.
129+ if ( span ?. isRecording ( ) ) {
130+ span . setAttributes ( {
131+ "oidc.outcome" : outcome ,
132+ ...( claims && {
133+ "oidc.organization_id" : claims . org_id ,
134+ "oidc.project_id" : claims . project_id ,
135+ "oidc.environment_id" : claims . environment_id ,
136+ "oidc.run_id" : claims . run_id ,
137+ "oidc.attempt" : claims . attempt ,
138+ } ) ,
139+ } ) ;
140+ }
115141 }
116142}
0 commit comments