|
1 | 1 | import { beforeEach, describe, expect, it, vi } from "vitest"; |
2 | | -import { authenticateApiKeyRequest, authenticateApiKeyWithScope } from "~/services/apiAuth.server"; |
| 2 | +import { |
| 3 | + authenticateApiKeyRequest, |
| 4 | + authenticateApiKeyWithScope, |
| 5 | + authenticateRequestWithScopedApiKey, |
| 6 | +} from "~/services/apiAuth.server"; |
3 | 7 |
|
4 | 8 | const authorizeBearer = vi.fn(); |
5 | 9 |
|
@@ -151,3 +155,61 @@ describe("authenticateApiKeyWithScope", () => { |
151 | 155 | expect(ability.can).not.toHaveBeenCalled(); |
152 | 156 | }); |
153 | 157 | }); |
| 158 | + |
| 159 | +describe("authenticateRequestWithScopedApiKey", () => { |
| 160 | + const options = { |
| 161 | + personalAccessToken: true as const, |
| 162 | + organizationAccessToken: true as const, |
| 163 | + apiKey: { |
| 164 | + action: "write", |
| 165 | + resource: { type: "branches" }, |
| 166 | + allowPreviewParent: true, |
| 167 | + }, |
| 168 | + }; |
| 169 | + |
| 170 | + it("keeps user and organization tokens on the legacy path", async () => { |
| 171 | + const authentication = { |
| 172 | + type: "personalAccessToken", |
| 173 | + result: { userId: "user_123" }, |
| 174 | + } as const; |
| 175 | + const authenticateRequest = vi.fn().mockResolvedValueOnce(authentication); |
| 176 | + const authenticateApiKeyWithScope = vi.fn(); |
| 177 | + |
| 178 | + await expect( |
| 179 | + authenticateRequestWithScopedApiKey(new Request("https://example.com"), options, { |
| 180 | + authenticateRequest, |
| 181 | + authenticateApiKeyWithScope, |
| 182 | + }) |
| 183 | + ).resolves.toEqual({ ok: true, authentication }); |
| 184 | + expect(authenticateRequest).toHaveBeenCalledWith(expect.any(Request), { |
| 185 | + personalAccessToken: true, |
| 186 | + organizationAccessToken: true, |
| 187 | + apiKey: false, |
| 188 | + }); |
| 189 | + expect(authenticateApiKeyWithScope).not.toHaveBeenCalled(); |
| 190 | + }); |
| 191 | + |
| 192 | + it("uses scoped RBAC authentication for API keys", async () => { |
| 193 | + const apiKeyAuthentication = { |
| 194 | + ok: true, |
| 195 | + apiKey: "tr_preview_sk_test", |
| 196 | + type: "PRIVATE", |
| 197 | + environment: {}, |
| 198 | + } as const; |
| 199 | + const authenticateRequest = vi.fn().mockResolvedValueOnce(undefined); |
| 200 | + const authenticateApiKeyWithScope = vi |
| 201 | + .fn() |
| 202 | + .mockResolvedValueOnce({ ok: true, authentication: apiKeyAuthentication }); |
| 203 | + |
| 204 | + await expect( |
| 205 | + authenticateRequestWithScopedApiKey(new Request("https://example.com"), options, { |
| 206 | + authenticateRequest, |
| 207 | + authenticateApiKeyWithScope, |
| 208 | + }) |
| 209 | + ).resolves.toEqual({ |
| 210 | + ok: true, |
| 211 | + authentication: { type: "apiKey", result: apiKeyAuthentication }, |
| 212 | + }); |
| 213 | + expect(authenticateApiKeyWithScope).toHaveBeenCalledWith(expect.any(Request), options.apiKey); |
| 214 | + }); |
| 215 | +}); |
0 commit comments