11/**
22 * Two seams a delegated user-actor token meets outside the route builder: the PAT-only
3- * authentication (which enforces nothing, so it refuses the token outright), and the
3+ * authentication (which enforces nothing, so it refuses the token outright while still
4+ * authenticating an ordinary PAT), and the
45 * environment JWT exchange (which ceilings the minted scopes by the token's cap and only
56 * mints for the environment it was signed for).
67 */
@@ -24,7 +25,10 @@ vi.mock("~/db.server", () => {
2425 return { prisma : proxy , $replica : proxy , sqlDatabaseSchema : undefined } ;
2526} ) ;
2627vi . mock ( "~/env.server" , ( ) => ( {
27- env : { SESSION_SECRET : "test-session-secret-for-user-actor-claims" } ,
28+ env : {
29+ SESSION_SECRET : "test-session-secret-for-user-actor-claims" ,
30+ ENCRYPTION_KEY : "12345678901234567890123456789012" ,
31+ } ,
2832} ) ) ;
2933vi . mock ( "~/services/logger.server" , ( ) => ( {
3034 logger : { debug : vi . fn ( ) , error : vi . fn ( ) , warn : vi . fn ( ) , info : vi . fn ( ) } ,
@@ -63,7 +67,7 @@ vi.mock("~/services/rbac.server", async () => {
6367 } ;
6468} ) ;
6569
66- const { authenticateApiRequestWithPersonalAccessToken } =
70+ const { authenticateApiRequestWithPersonalAccessToken, createPersonalAccessToken } =
6771 await import ( "~/services/personalAccessToken.server" ) ;
6872const { action : jwtAction } = await import ( "~/routes/api.v1.projects.$projectRef.$env.jwt" ) ;
6973
@@ -108,6 +112,29 @@ function suffix() {
108112 return Math . random ( ) . toString ( 36 ) . slice ( 2 , 10 ) ;
109113}
110114
115+ // The other direction: the refusal is aimed at the token type only, so an ordinary PAT still
116+ // authenticates through the same helper.
117+ postgresTest (
118+ "authenticates an ordinary personal access token" ,
119+ async ( { prisma } ) => {
120+ ctx . prisma = prisma ;
121+ const user = await prisma . user . create ( {
122+ data : { email : `pat_${ suffix ( ) } @example.com` , authenticationMethod : "MAGIC_LINK" } ,
123+ } ) ;
124+ const pat = await createPersonalAccessToken ( { name : "claims-test" , userId : user . id } ) ;
125+
126+ const result = await authenticateApiRequestWithPersonalAccessToken ( bearer ( pat . token ) ) ;
127+
128+ expect ( result ?. userId ) . toBe ( user . id ) ;
129+
130+ // And it is the stored token that authenticates, not the prefix.
131+ expect (
132+ await authenticateApiRequestWithPersonalAccessToken ( bearer ( `tr_pat_${ suffix ( ) } ` ) )
133+ ) . toBeUndefined ( ) ;
134+ } ,
135+ 60_000
136+ ) ;
137+
111138/** An org with one project, a prod and a staging environment, and a member user. */
112139async function seedProject ( prisma : PrismaClient ) {
113140 const slug = `jwt_${ suffix ( ) } ` ;
0 commit comments