alepha
Version:
Easy-to-use modern TypeScript framework for building many kind of applications.
166 lines (144 loc) • 5.25 kB
text/typescript
import { createHash } from "node:crypto";
import { Alepha } from "alepha";
import { AlephaOrmPostgres } from "alepha/orm/postgres";
import { JwtProvider } from "alepha/security";
import { describe, it } from "vitest";
import { oauthClientEntity } from "../entities/oauthClientEntity.ts";
import { OAuthClientService } from "../services/OAuthClientService.ts";
describe("oauthClientEntity", () => {
it("declares the oauth_clients table", ({ expect }) => {
expect(oauthClientEntity.name).toBe("oauth_clients");
});
});
describe("OAuthClientService.register", () => {
it("registers a public DCR client and returns a client_id", async ({
expect,
}) => {
const alepha = Alepha.create().with(AlephaOrmPostgres);
const service = alepha.inject(OAuthClientService);
await alepha.start();
const client = await service.register({
realm: "users",
clientName: "Claude",
redirectUris: ["https://claude.ai/api/mcp/auth_callback"],
scopes: ["mcp"],
});
expect(client.clientId).toMatch(/^mcp_/);
expect(client.source).toBe("dcr");
const found = await service.findByClientId(client.clientId);
expect(found?.clientName).toBe("Claude");
});
it("rejects an unknown redirect_uri", async ({ expect }) => {
const alepha = Alepha.create().with(AlephaOrmPostgres);
const service = alepha.inject(OAuthClientService);
await alepha.start();
const client = await service.register({
realm: "users",
clientName: "Claude",
redirectUris: ["https://claude.ai/cb"],
scopes: ["mcp"],
});
expect(service.isRedirectUriAllowed(client, "https://claude.ai/cb")).toBe(
true,
);
expect(service.isRedirectUriAllowed(client, "https://evil.com/cb")).toBe(
false,
);
});
});
describe("OAuthClientService.intersectScopes", () => {
const setup = async () => {
const alepha = Alepha.create().with(AlephaOrmPostgres);
const service = alepha.inject(OAuthClientService);
await alepha.start();
return service;
};
it("drops requested scopes the client is not registered for", async ({
expect,
}) => {
const service = await setup();
// Client registered for ["mcp"] but asks for admin → admin is stripped.
expect(service.intersectScopes(["mcp", "admin"], ["mcp"])).toEqual(["mcp"]);
});
it("falls back to the client's registered scopes when none requested", async ({
expect,
}) => {
const service = await setup();
expect(service.intersectScopes(undefined, ["mcp", "openid"])).toEqual([
"mcp",
"openid",
]);
expect(service.intersectScopes([], ["mcp"])).toEqual(["mcp"]);
});
it("preserves requested order and de-duplicates", async ({ expect }) => {
const service = await setup();
expect(
service.intersectScopes(["openid", "mcp", "openid"], ["mcp", "openid"]),
).toEqual(["openid", "mcp"]);
});
it("returns nothing when no requested scope is allowed", async ({
expect,
}) => {
const service = await setup();
expect(service.intersectScopes(["admin"], ["mcp"])).toEqual([]);
});
});
describe("OAuthClientService authorization code", () => {
it("mints and verifies a single-use auth code bound to PKCE", async ({
expect,
}) => {
const alepha = Alepha.create().with(AlephaOrmPostgres);
const jwt = alepha.inject(JwtProvider);
jwt.setKeyLoader("users", "test-secret-for-oauth-code-tests");
const service = alepha.inject(OAuthClientService);
await alepha.start();
const verifier = "a".repeat(64);
const challenge = createHash("sha256").update(verifier).digest("base64url");
const code = await service.createAuthorizationCode("users", {
userId: "user-1",
clientId: "mcp_x",
redirectUri: "https://claude.ai/cb",
codeChallenge: challenge,
scopes: ["mcp"],
resource: "https://app.com/mcp",
});
const claims = await service.consumeAuthorizationCode("users", code, {
clientId: "mcp_x",
redirectUri: "https://claude.ai/cb",
codeVerifier: verifier,
});
expect(claims.userId).toBe("user-1");
await expect(
service.consumeAuthorizationCode("users", code, {
clientId: "mcp_x",
redirectUri: "https://claude.ai/cb",
codeVerifier: verifier,
}),
).rejects.toThrow();
});
it("rejects a wrong PKCE verifier", async ({ expect }) => {
const alepha = Alepha.create().with(AlephaOrmPostgres);
const jwt = alepha.inject(JwtProvider);
jwt.setKeyLoader("users", "test-secret-for-oauth-code-tests");
const service = alepha.inject(OAuthClientService);
await alepha.start();
const challenge = createHash("sha256")
.update("a".repeat(64))
.digest("base64url");
const code = await service.createAuthorizationCode("users", {
userId: "user-1",
clientId: "mcp_x",
redirectUri: "https://claude.ai/cb",
codeChallenge: challenge,
scopes: ["mcp"],
resource: "https://app.com/mcp",
});
await expect(
service.consumeAuthorizationCode("users", code, {
clientId: "mcp_x",
redirectUri: "https://claude.ai/cb",
codeVerifier: "b".repeat(64),
}),
).rejects.toThrow();
});
});