var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
import { CryptoUtils } from '@enbox/crypto';
import { DidJwk } from '@enbox/dids';
import { Oidc } from './oidc.js';
import { pollWithTtl } from './utils.js';
import { Convert, logger } from '@enbox/common';
import { DwnInterfaceName, DwnMethodName } from '@enbox/dwn-sdk-js';
/**
 * Initiates the wallet connect process. Used when a client wants to obtain
 * a did from a provider.
 */
function initClient(_a) {
    return __awaiter(this, arguments, void 0, function* ({ displayName, connectServerUrl, walletUri, permissionRequests, onWalletUriReady, validatePin, }) {
        // ephemeral client did for ECDH, signing, verification
        const clientDid = yield DidJwk.create();
        // TODO: properly implement PKCE. this implementation is lacking server side validations and more.
        // https://github.com/enboxorg/enbox/issues/829
        // Derive the code challenge based on the code verifier
        // const { codeChallengeBytes, codeChallengeBase64Url } =
        //   await Oidc.generateCodeChallenge();
        const encryptionKey = CryptoUtils.randomBytes(32);
        // build callback URL to pass into the auth request
        const callbackEndpoint = Oidc.buildOidcUrl({
            baseURL: connectServerUrl,
            endpoint: 'callback',
        });
        // build the PAR request
        const request = yield Oidc.createAuthRequest({
            client_id: clientDid.uri,
            scope: 'openid did:jwk',
            redirect_uri: callbackEndpoint,
            // custom properties:
            // code_challenge        : codeChallengeBase64Url,
            // code_challenge_method : 'S256',
            permissionRequests: permissionRequests,
            displayName,
        });
        // Sign the Request Object using the Client DID's signing key.
        const requestJwt = yield Oidc.signJwt({
            did: clientDid,
            data: request,
        });
        if (!requestJwt) {
            throw new Error('Unable to sign requestObject');
        }
        // Encrypt the Request Object JWT using the code challenge.
        const requestObjectJwe = yield Oidc.encryptAuthRequest({
            jwt: requestJwt,
            encryptionKey,
        });
        // Convert the encrypted Request Object to URLSearchParams for form encoding.
        const formEncodedRequest = new URLSearchParams({
            request: requestObjectJwe,
        });
        const pushedAuthorizationRequestEndpoint = Oidc.buildOidcUrl({
            baseURL: connectServerUrl,
            endpoint: 'pushedAuthorizationRequest',
        });
        const parResponse = yield fetch(pushedAuthorizationRequestEndpoint, {
            body: formEncodedRequest,
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
            },
            signal: AbortSignal.timeout(30000),
        });
        if (!parResponse.ok) {
            throw new Error(`${parResponse.status}: ${parResponse.statusText}`);
        }
        const parData = yield parResponse.json();
        // a deeplink to a web5 compatible wallet. if the wallet scans this link it should receive
        // a route to its web5 connect provider flow and the params of where to fetch the auth request.
        logger.log(`Wallet URI: ${walletUri}`);
        const generatedWalletUri = new URL(walletUri);
        generatedWalletUri.searchParams.set('request_uri', parData.request_uri);
        generatedWalletUri.searchParams.set('encryption_key', Convert.uint8Array(encryptionKey).toBase64Url());
        // call user's callback so they can send the URI to the wallet as they see fit
        onWalletUriReady(generatedWalletUri.toString());
        const tokenUrl = Oidc.buildOidcUrl({
            baseURL: connectServerUrl,
            endpoint: 'token',
            tokenParam: request.state,
        });
        // subscribe to receiving a response from the wallet with default TTL. receive ciphertext of {@link Web5ConnectAuthResponse}
        const authResponse = yield pollWithTtl(() => fetch(tokenUrl, { signal: AbortSignal.timeout(30000) }));
        if (authResponse) {
            const jwe = yield (authResponse === null || authResponse === void 0 ? void 0 : authResponse.text());
            // get the pin from the user and use it as AAD to decrypt
            const pin = yield validatePin();
            const jwt = yield Oidc.decryptAuthResponse(clientDid, jwe, pin);
            const verifiedAuthResponse = (yield Oidc.verifyJwt({
                jwt,
            }));
            return {
                delegateGrants: verifiedAuthResponse.delegateGrants,
                delegatePortableDid: verifiedAuthResponse.delegatePortableDid,
                connectedDid: verifiedAuthResponse.iss,
            };
        }
    });
}
/**
 * Creates a set of Dwn Permission Scopes to request for a given protocol.
 *
 * If no permissions are provided, the default is to request all relevant record permissions (write, read, delete, query, subscribe).
 * 'configure' is not included by default, as this gives the application a lot of control over the protocol.
 */
function createPermissionRequestForProtocol({ definition, permissions }) {
    const requests = [];
    // Add the ability to query for the specific protocol
    requests.push({
        protocol: definition.protocol,
        interface: DwnInterfaceName.Protocols,
        method: DwnMethodName.Query,
    });
    // A Messages.Read grant is a unified scope that covers MessagesRead, MessagesSync, and MessagesSubscribe.
    // This single grant enables sync and real-time subscriptions for the protocol.
    requests.push({
        protocol: definition.protocol,
        interface: DwnInterfaceName.Messages,
        method: DwnMethodName.Read,
    });
    // We also request any additional permissions the user has requested for this protocol
    for (const permission of permissions) {
        switch (permission) {
            case 'write':
                requests.push({
                    protocol: definition.protocol,
                    interface: DwnInterfaceName.Records,
                    method: DwnMethodName.Write,
                });
                break;
            case 'read':
                requests.push({
                    protocol: definition.protocol,
                    interface: DwnInterfaceName.Records,
                    method: DwnMethodName.Read,
                });
                break;
            case 'delete':
                requests.push({
                    protocol: definition.protocol,
                    interface: DwnInterfaceName.Records,
                    method: DwnMethodName.Delete,
                });
                break;
            case 'query':
                requests.push({
                    protocol: definition.protocol,
                    interface: DwnInterfaceName.Records,
                    method: DwnMethodName.Query,
                });
                break;
            case 'subscribe':
                requests.push({
                    protocol: definition.protocol,
                    interface: DwnInterfaceName.Records,
                    method: DwnMethodName.Subscribe,
                });
                break;
            case 'configure':
                requests.push({
                    protocol: definition.protocol,
                    interface: DwnInterfaceName.Protocols,
                    method: DwnMethodName.Configure,
                });
                break;
        }
    }
    return {
        protocolDefinition: definition,
        permissionScopes: requests,
    };
}
export const WalletConnect = { initClient, createPermissionRequestForProtocol };
//# sourceMappingURL=connect.js.map