Skip to main content
Glama
auth.ts1.65 kB
import { SignJWT, importPKCS8 } from "jose"; import type { AppStoreConfig } from "@/packages/configs/secrets-config/types"; type JwtOptions = { now?: number; // seconds expirationSeconds?: number; // default 600, capped at 1200 }; const AUDIENCE = "appstoreconnect-v1"; const MAX_EXP_SECONDS = 60 * 20; const DEFAULT_EXP_SECONDS = 60 * 10; export async function createAppStoreJWT( config: AppStoreConfig, options: JwtOptions = {} ): Promise<string> { const nowSeconds = options.now ?? Math.floor(Date.now() / 1000); const expSeconds = Math.min( options.expirationSeconds ?? DEFAULT_EXP_SECONDS, MAX_EXP_SECONDS ); // Normalize private key const normalizedKey = config.privateKey.replace(/\\n/g, "\n").trim(); // Import private key in PKCS8 format const privateKey = await importPKCS8(normalizedKey, "ES256"); // Create JWT (App Store Connect API does not require iat) const jwt = await new SignJWT({ iss: config.issuerId, aud: AUDIENCE, }) .setProtectedHeader({ alg: "ES256", kid: config.keyId, typ: "JWT", }) .setExpirationTime(nowSeconds + expSeconds) .sign(privateKey); return jwt; } export function decodeJwt(token: string): { header: Record<string, unknown>; payload: Record<string, unknown>; signature: string; } { const [header, payload, signature] = token.split("."); if (!header || !payload || !signature) { throw new Error("Invalid JWT format"); } return { header: JSON.parse(Buffer.from(header, "base64url").toString("utf-8")), payload: JSON.parse(Buffer.from(payload, "base64url").toString("utf-8")), signature, }; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/quartz-labs-dev/pabal-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server