import type { DeviceAuthorizationResponse, OAuthDiscoveryDocument, OAuthToken, OpenSeaOAuthConfig } from "./oauth-types"; /** * OAuth 2.1 authorization-code + PKCE helper for OpenSea (OS2-33722). * * Runs the standard flow against the OpenSea authorization server (Zitadel at * `auth.opensea.io`) using a public client โ€” no client secret, no private key, * no SIWE message signing. This is the same flow spec-compliant MCP clients * discover via the MCP server's Protected Resource Metadata, exposed here for * programmatic consumers (the CLI's keyless `opensea login` is built on it). * * Endpoints are resolved from OIDC discovery * (`/.well-known/openid-configuration`) rather than hardcoded. * * @example * ```ts * const oauth = new OpenSeaOAuth({ clientId: "..." }) * * // Authorization code + PKCE (browser-capable environments) * const req = await oauth.createAuthorizationRequest({ * redirectUri: "http://127.0.0.1:8151/callback", * scopes: ["read:eligibility"], * }) * // ...direct the user to req.url, receive `code` + `state` on the redirect... * const token = await oauth.exchangeCode({ * code, * codeVerifier: req.codeVerifier, * redirectUri: "http://127.0.0.1:8151/callback", * }) * * // Device flow (headless environments) * const device = await oauth.requestDeviceAuthorization() * // ...show device.user_code + device.verification_uri to the user... * const token2 = await oauth.pollDeviceToken(device) * ``` * * @category Authentication */ export declare class OpenSeaOAuth { private readonly issuer; private readonly clientId; private readonly timeoutMs; private discovery; constructor(config: OpenSeaOAuthConfig); /** * Fetch (and cache) the OIDC discovery document for the issuer, then * validate it: the advertised `issuer` must match the configured issuer * (OpenID Connect Discovery ยง4.3 / OAuth 2.1 mix-up defense) and the * authorization/token endpoints must be secure (`https:`, or loopback for * local testing). This prevents a compromised discovery response from * redirecting the flow to an attacker-controlled endpoint. */ getDiscovery(): Promise; /** * Build an authorization request for the code + PKCE flow. Returns the URL * to open in a browser plus the `codeVerifier` and `state` the caller must * hold on to for {@link exchangeCode} and redirect validation. */ createAuthorizationRequest(options: { redirectUri: string; scopes?: string[]; }): Promise<{ url: string; codeVerifier: string; state: string; }>; /** Exchange an authorization code (plus PKCE verifier) for tokens. */ exchangeCode(options: { code: string; codeVerifier: string; redirectUri: string; }): Promise; /** Refresh an access token using a refresh token. */ refresh(refreshToken: string): Promise; /** Revoke an access or refresh token. */ revoke(token: string): Promise; /** * Start a device authorization flow (RFC 8628) โ€” for headless environments * where a browser redirect is not possible. */ requestDeviceAuthorization(options?: { scopes?: string[]; }): Promise; /** * Poll the token endpoint until the user completes device authorization. * Respects the server-provided `interval` and `slow_down` responses, and * stops when `expires_in` elapses. */ pollDeviceToken(device: DeviceAuthorizationResponse): Promise; private postToken; /** * `fetch` wrapper that aborts the request after `this.timeoutMs`, so a hung * authorization server can't stall the flow indefinitely. */ private fetchWithTimeout; } /** * Read OpenSea API scopes from a JWT access token for client-side display and * persistence. This does not verify the token and must not be used to make an * authorization decision. */ export declare function extractOpenSeaScopes(accessToken: string): string[]; /** * Read the wallet address from decoded JWT claims. Zitadel injects it as a * top-level `wallet` claim (plaintext) via the `inject_wallet_claim` action, * the same claim `opensea-mcp` and the os2-core REST API read. The `sub` claim * is an account identifier, not a wallet address, so it must never be used as * a fallback. Returns `undefined` when the wallet claim is absent. */ export declare function extractWalletAddress(claims: Record): string | undefined; /** * Decode a JWT payload without verifying the signature. Intended for reading * claims out of a token the authorization server just issued to us over TLS โ€” * NOT for validating inbound tokens. */ export declare function decodeJwtPayload(token: string): Record;