From cb8135840c22e2c3d21e91f491639d4869164328 Mon Sep 17 00:00:00 2001 From: Allen Zhou <46854522+allenzhou101@users.noreply.github.com> Date: Mon, 24 Feb 2025 17:39:50 -0800 Subject: [PATCH] Change verifyToken to match parent interface name --- src/server/auth/handlers/token.test.ts | 2 +- src/server/auth/proxyProvider.test.ts | 86 +++++++++++++++++++------- src/server/auth/proxyProvider.ts | 8 +-- 3 files changed, 68 insertions(+), 28 deletions(-) diff --git a/src/server/auth/handlers/token.test.ts b/src/server/auth/handlers/token.test.ts index 1b44721f..000b2908 100644 --- a/src/server/auth/handlers/token.test.ts +++ b/src/server/auth/handlers/token.test.ts @@ -300,7 +300,7 @@ describe('Token Handler', () => { endpoints: { tokenUrl: 'https://example.com/token' }, - verifyToken: async (token) => ({ + verifyAccessToken: async (token) => ({ token, clientId: 'valid-client', scopes: ['read', 'write'], diff --git a/src/server/auth/proxyProvider.test.ts b/src/server/auth/proxyProvider.test.ts index 9a0ae3da..8c6106ff 100644 --- a/src/server/auth/proxyProvider.test.ts +++ b/src/server/auth/proxyProvider.test.ts @@ -3,6 +3,8 @@ import { ProxyOAuthServerProvider, ProxyOptions } from "./proxyProvider.js"; import { AuthInfo } from "./types.js"; import { OAuthClientInformationFull, OAuthTokens } from "../../shared/auth.js"; import { ServerError } from "./errors.js"; +import { InvalidTokenError } from "./errors.js"; +import { InsufficientScopeError } from "./errors.js"; describe("Proxy OAuth Server Provider", () => { // Mock client data @@ -17,6 +19,10 @@ describe("Proxy OAuth Server Provider", () => { redirect: jest.fn(), } as unknown as Response; + // Mock provider functions + const mockVerifyToken = jest.fn(); + const mockGetClient = jest.fn(); + // Base provider options const baseOptions: ProxyOptions = { endpoints: { @@ -25,23 +31,8 @@ describe("Proxy OAuth Server Provider", () => { revocationUrl: "https://auth.example.com/revoke", registrationUrl: "https://auth.example.com/register", }, - verifyToken: jest.fn().mockImplementation(async (token: string) => { - if (token === "valid-token") { - return { - token, - clientId: "test-client", - scopes: ["read", "write"], - expiresAt: Date.now() / 1000 + 3600, - } as AuthInfo; - } - throw new Error("Invalid token"); - }), - getClient: jest.fn().mockImplementation(async (clientId: string) => { - if (clientId === "test-client") { - return validClient; - } - return undefined; - }), + verifyAccessToken: mockVerifyToken, + getClient: mockGetClient, }; let provider: ProxyOAuthServerProvider; @@ -51,6 +42,26 @@ describe("Proxy OAuth Server Provider", () => { provider = new ProxyOAuthServerProvider(baseOptions); originalFetch = global.fetch; global.fetch = jest.fn(); + + // Setup mock implementations + mockVerifyToken.mockImplementation(async (token: string) => { + if (token === "valid-token") { + return { + token, + clientId: "test-client", + scopes: ["read", "write"], + expiresAt: Date.now() / 1000 + 3600, + } as AuthInfo; + } + throw new InvalidTokenError("Invalid token"); + }); + + mockGetClient.mockImplementation(async (clientId: string) => { + if (clientId === "test-client") { + return validClient; + } + return undefined; + }); }); afterEach(() => { @@ -271,15 +282,44 @@ describe("Proxy OAuth Server Provider", () => { describe("token verification", () => { it("verifies valid token", async () => { + const validAuthInfo: AuthInfo = { + token: "valid-token", + clientId: "test-client", + scopes: ["read", "write"], + expiresAt: Date.now() / 1000 + 3600, + }; + mockVerifyToken.mockResolvedValue(validAuthInfo); + const authInfo = await provider.verifyAccessToken("valid-token"); - expect(authInfo.token).toBe("valid-token"); - expect(baseOptions.verifyToken).toHaveBeenCalledWith("valid-token"); + expect(authInfo).toEqual(validAuthInfo); + expect(mockVerifyToken).toHaveBeenCalledWith("valid-token"); }); - it("rejects invalid token", async () => { - await expect( - provider.verifyAccessToken("invalid-token") - ).rejects.toThrow("Invalid token"); + it("passes through InvalidTokenError", async () => { + const error = new InvalidTokenError("Token expired"); + mockVerifyToken.mockRejectedValue(error); + + await expect(provider.verifyAccessToken("invalid-token")) + .rejects.toBe(error); + expect(mockVerifyToken).toHaveBeenCalledWith("invalid-token"); + }); + + it("passes through InsufficientScopeError", async () => { + const error = new InsufficientScopeError("Required scopes: read, write"); + mockVerifyToken.mockRejectedValue(error); + + await expect(provider.verifyAccessToken("token-with-insufficient-scope")) + .rejects.toBe(error); + expect(mockVerifyToken).toHaveBeenCalledWith("token-with-insufficient-scope"); + }); + + it("passes through unexpected errors", async () => { + const error = new Error("Unexpected error"); + mockVerifyToken.mockRejectedValue(error); + + await expect(provider.verifyAccessToken("valid-token")) + .rejects.toBe(error); + expect(mockVerifyToken).toHaveBeenCalledWith("valid-token"); }); }); }); \ No newline at end of file diff --git a/src/server/auth/proxyProvider.ts b/src/server/auth/proxyProvider.ts index d164b646..81727548 100644 --- a/src/server/auth/proxyProvider.ts +++ b/src/server/auth/proxyProvider.ts @@ -26,7 +26,7 @@ export type ProxyOptions = { /** * Function to verify access tokens and return auth info */ - verifyToken: (token: string) => Promise; + verifyAccessToken: (token: string) => Promise; /** * Function to fetch client information from the upstream server @@ -40,7 +40,7 @@ export type ProxyOptions = { */ export class ProxyOAuthServerProvider implements OAuthServerProvider { private readonly _endpoints: ProxyEndpoints; - private readonly _verifyToken: (token: string) => Promise; + private readonly _verifyAccessToken: (token: string) => Promise; private readonly _getClient: (clientId: string) => Promise; public revokeToken?: ( @@ -50,7 +50,7 @@ export class ProxyOAuthServerProvider implements OAuthServerProvider { constructor(options: ProxyOptions) { this._endpoints = options.endpoints; - this._verifyToken = options.verifyToken; + this._verifyAccessToken = options.verifyAccessToken; this._getClient = options.getClient; if (options.endpoints?.revocationUrl) { this.revokeToken = async ( @@ -234,6 +234,6 @@ export class ProxyOAuthServerProvider implements OAuthServerProvider { } async verifyAccessToken(token: string): Promise { - return this._verifyToken(token); + return this._verifyAccessToken(token); } } \ No newline at end of file