Added tests
This commit is contained in:
parent
8a222e8b38
commit
3b12961e2e
3 changed files with 112 additions and 0 deletions
|
|
@ -43,6 +43,36 @@ describe("OAuth Authorization", () => {
|
|||
});
|
||||
});
|
||||
|
||||
it("returns metadata when first fetch fails but second without MCP header succeeds", async () => {
|
||||
// First request with MCP header fails
|
||||
mockFetch.mockRejectedValueOnce(new Error("Network error"));
|
||||
|
||||
// Second request without header succeeds
|
||||
mockFetch.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
status: 200,
|
||||
json: async () => validMetadata,
|
||||
});
|
||||
|
||||
const metadata = await discoverOAuthMetadata("https://auth.example.com");
|
||||
expect(metadata).toEqual(validMetadata);
|
||||
|
||||
// Verify second call was made without header
|
||||
expect(mockFetch).toHaveBeenCalledTimes(2);
|
||||
const secondCallOptions = mockFetch.mock.calls[1][1];
|
||||
expect(secondCallOptions).toBeUndefined(); // No options means no headers
|
||||
});
|
||||
|
||||
it("returns undefined when all fetch attempts fail", async () => {
|
||||
// Both requests fail
|
||||
mockFetch.mockRejectedValueOnce(new Error("Network error"));
|
||||
mockFetch.mockRejectedValueOnce(new Error("Network error"));
|
||||
|
||||
const metadata = await discoverOAuthMetadata("https://auth.example.com");
|
||||
expect(metadata).toBeUndefined();
|
||||
expect(mockFetch).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it("returns undefined when discovery endpoint returns 404", async () => {
|
||||
mockFetch.mockResolvedValueOnce({
|
||||
ok: false,
|
||||
|
|
|
|||
|
|
@ -141,6 +141,37 @@ describe('Client Registration Handler', () => {
|
|||
|
||||
expect(response.status).toBe(201);
|
||||
expect(response.body.client_secret).toBeUndefined();
|
||||
expect(response.body.client_secret_expires_at).toBeUndefined();
|
||||
});
|
||||
|
||||
it('sets client_secret_expires_at for public clients only', async () => {
|
||||
// Test for public client (token_endpoint_auth_method not 'none')
|
||||
const publicClientMetadata: OAuthClientMetadata = {
|
||||
redirect_uris: ['https://example.com/callback'],
|
||||
token_endpoint_auth_method: 'client_secret_basic'
|
||||
};
|
||||
|
||||
const publicResponse = await supertest(app)
|
||||
.post('/register')
|
||||
.send(publicClientMetadata);
|
||||
|
||||
expect(publicResponse.status).toBe(201);
|
||||
expect(publicResponse.body.client_secret).toBeDefined();
|
||||
expect(publicResponse.body.client_secret_expires_at).toBeDefined();
|
||||
|
||||
// Test for non-public client (token_endpoint_auth_method is 'none')
|
||||
const nonPublicClientMetadata: OAuthClientMetadata = {
|
||||
redirect_uris: ['https://example.com/callback'],
|
||||
token_endpoint_auth_method: 'none'
|
||||
};
|
||||
|
||||
const nonPublicResponse = await supertest(app)
|
||||
.post('/register')
|
||||
.send(nonPublicClientMetadata);
|
||||
|
||||
expect(nonPublicResponse.status).toBe(201);
|
||||
expect(nonPublicResponse.body.client_secret).toBeUndefined();
|
||||
expect(nonPublicResponse.body.client_secret_expires_at).toBeUndefined();
|
||||
});
|
||||
|
||||
it('sets expiry based on clientSecretExpirySeconds', async () => {
|
||||
|
|
|
|||
|
|
@ -55,6 +55,57 @@ describe("requireBearerAuth middleware", () => {
|
|||
expect(mockResponse.status).not.toHaveBeenCalled();
|
||||
expect(mockResponse.json).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should reject expired tokens", async () => {
|
||||
const expiredAuthInfo: AuthInfo = {
|
||||
token: "expired-token",
|
||||
clientId: "client-123",
|
||||
scopes: ["read", "write"],
|
||||
expiresAt: Math.floor(Date.now() / 1000) - 100, // Token expired 100 seconds ago
|
||||
};
|
||||
mockVerifyAccessToken.mockResolvedValue(expiredAuthInfo);
|
||||
|
||||
mockRequest.headers = {
|
||||
authorization: "Bearer expired-token",
|
||||
};
|
||||
|
||||
const middleware = requireBearerAuth({ provider: mockProvider });
|
||||
await middleware(mockRequest as Request, mockResponse as Response, nextFunction);
|
||||
|
||||
expect(mockVerifyAccessToken).toHaveBeenCalledWith("expired-token");
|
||||
expect(mockResponse.status).toHaveBeenCalledWith(401);
|
||||
expect(mockResponse.set).toHaveBeenCalledWith(
|
||||
"WWW-Authenticate",
|
||||
expect.stringContaining('Bearer error="invalid_token"')
|
||||
);
|
||||
expect(mockResponse.json).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ error: "invalid_token", error_description: "Token has expired" })
|
||||
);
|
||||
expect(nextFunction).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should accept non-expired tokens", async () => {
|
||||
const nonExpiredAuthInfo: AuthInfo = {
|
||||
token: "valid-token",
|
||||
clientId: "client-123",
|
||||
scopes: ["read", "write"],
|
||||
expiresAt: Math.floor(Date.now() / 1000) + 3600, // Token expires in an hour
|
||||
};
|
||||
mockVerifyAccessToken.mockResolvedValue(nonExpiredAuthInfo);
|
||||
|
||||
mockRequest.headers = {
|
||||
authorization: "Bearer valid-token",
|
||||
};
|
||||
|
||||
const middleware = requireBearerAuth({ provider: mockProvider });
|
||||
await middleware(mockRequest as Request, mockResponse as Response, nextFunction);
|
||||
|
||||
expect(mockVerifyAccessToken).toHaveBeenCalledWith("valid-token");
|
||||
expect(mockRequest.auth).toEqual(nonExpiredAuthInfo);
|
||||
expect(nextFunction).toHaveBeenCalled();
|
||||
expect(mockResponse.status).not.toHaveBeenCalled();
|
||||
expect(mockResponse.json).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should require specific scopes when configured", async () => {
|
||||
const authInfo: AuthInfo = {
|
||||
|
|
|
|||
Loading…
Reference in a new issue