Skip to content

Commit

Permalink
add PostMessageProvider, it work on iframe of zkid wallet pwa app. (#20)
Browse files Browse the repository at this point in the history
  • Loading branch information
zzcwoshizz authored Mar 27, 2023
1 parent d08c36c commit f042638
Show file tree
Hide file tree
Showing 16 changed files with 351 additions and 64 deletions.
12 changes: 12 additions & 0 deletions .changeset/pre.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"mode": "pre",
"tag": "beta",
"initialVersions": {
"@zcloak/login-did": "1.1.1",
"@zcloak/login-providers": "1.1.1",
"@zcloak/login-rpc": "1.0.1",
"@zcloak/login-rpc-defines": "1.1.1",
"@zcloak/login-verify": "1.1.3"
},
"changesets": []
}
7 changes: 7 additions & 0 deletions .changeset/short-flowers-clean.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@zcloak/login-rpc-defines": minor
"@zcloak/login-providers": minor
"@zcloak/login-rpc": minor
---

add PostMessageProvider, it work on iframe of zkid wallet pwa app.
80 changes: 80 additions & 0 deletions packages/providers/src/ExtensionProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright 2021-2023 zcloak authors & contributors
// SPDX-License-Identifier: Apache-2.0

import type { Request, RpcMethods, RpcRequest } from '@zcloak/login-rpc';
import type { ZkidWalletProvider } from './types';

import { BaseProvider } from './base/Provider';

declare module '@zcloak/login-rpc/rpcs' {
interface Rpcs {
credential_import: [{ credential: HexString }, undefined];
}
}

declare global {
interface Window {
zkid?: { request: Request; events: any };
}
}

type HexString = `0x${string}`;

const waitReady: Promise<void> = new Promise<void>((resolve) => {
if (document.readyState === 'complete') {
resolve();
} else {
self.addEventListener('load', () => resolve());
}
});

const request: Request = async (method: RpcMethods, params: RpcRequest<RpcMethods>) => {
await waitReady;
if (!self.zkid) throw new Error('Please install zkID Wallet');

return self.zkid.request(method, params);
};

export class ExtensionProvider extends BaseProvider implements ZkidWalletProvider {
public static async isInstalled(): Promise<boolean> {
await ExtensionProvider.isReady();

return !!self.zkid;
}

public static isReady(): Promise<void> {
return waitReady;
}

constructor() {
super(request);

waitReady.then(() => {
if (!self.zkid) {
console.warn(
'Not zkID Wallet context, please install zkID Wallet https://chrome.google.com/webstore/detail/zkid-wallet/ahkpfejaeoepmfopmbhjgjekibmfcfgo'
);
}
});

self.zkid?.events?.on?.('zkID_Wallet_didLoggedChanged', this.#didChanged);
self.zkid?.events?.on?.('zkID_Wallet_lock', this.#lock);
self.zkid?.events?.on?.('zkID_Wallet_unlock', this.#unlock);
}

#didChanged = (...args: any[]) => {
this.emit('did_changed', ...args);
};

#lock = (...args: any[]) => {
this.emit('lock', ...args);
};

#unlock = (...args: any[]) => {
this.emit('unlock', ...args);
};

public importCredential(data: HexString) {
return this.request('credential_import', { credential: data });
}
}
108 changes: 108 additions & 0 deletions packages/providers/src/PostMessageProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// Copyright 2021-2023 zcloak authors & contributors
// SPDX-License-Identifier: Apache-2.0

import type { ZkidWalletProvider } from './types';

import {
isTransportEventMessage,
isTransportResponseMessage,
Request,
RpcMethods,
RpcRequest,
RpcResponse
} from '@zcloak/login-rpc';

import { BaseProvider } from './base/Provider';

type Handlers = Record<
string,
{
resolve: (data: RpcResponse<RpcMethods>) => void;
reject: (error: Error) => void;
}
>;
type HexString = `0x${string}`;

let increaseId = 0;

const handlers: Handlers = {};

const request: Request = (method: RpcMethods, params: RpcRequest<RpcMethods>) => {
if (self === parent) throw new Error('Please open on zkID Wallet');

return new Promise((resolve, reject) => {
const id = `${Date.now()}.${increaseId++}`;

handlers[id] = { reject, resolve };

const message = {
jsonrpc: '2.0',
id,
method,
params
};

parent.postMessage(message, '*');
});
};

export class PostMessageProvider extends BaseProvider implements ZkidWalletProvider {
private readonly allowedOrigins: RegExp[] | null = null;

constructor(allowedOrigins: RegExp[] | null = null) {
super(request);

this.allowedOrigins = allowedOrigins;
self.addEventListener('message', this.#handleMessage);
}

private isValidMessage = ({ data, origin, source }: MessageEvent): boolean => {
const emptyOrMalformed = !data;
const sentFromParentEl = source === parent;
let validOrigin = true;

if (Array.isArray(this.allowedOrigins)) {
validOrigin = this.allowedOrigins.find((regExp) => regExp.test(origin)) !== undefined;
}

return !emptyOrMalformed && sentFromParentEl && validOrigin;
};

#handleMessage = (message: MessageEvent) => {
if (this.isValidMessage(message)) {
if (isTransportEventMessage(message.data)) {
// event transport message

if (message.data.event === 'zkID_Wallet_didLoggedChanged') {
this.emit('did_changed', message.data.data);
} else if (message.data.event === 'zkID_Wallet_lock') {
this.emit('lock', message.data.data);
} else if (message.data.event === 'zkID_Wallet_unlock') {
this.emit('unlock', message.data.data);
}
} else if (isTransportResponseMessage(message.data)) {
// response transport message

const handler = handlers[message.data.id];

if (!handler) {
console.error(`Unknown response: ${JSON.stringify(message.data)}`);

return;
}

delete handlers[message.data.id];

if (message.data.error) {
handler.reject(new Error(message.data.error.message));
} else {
handler.resolve(message.data.result);
}
}
}
};

public importCredential(data: HexString) {
return this.request('credential_import', { credential: data });
}
}
60 changes: 0 additions & 60 deletions packages/providers/src/ZkidWalletProvider.ts

This file was deleted.

4 changes: 3 additions & 1 deletion packages/providers/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
// Copyright 2021-2023 zcloak authors & contributors
// SPDX-License-Identifier: Apache-2.0

export * from './ZkidWalletProvider';
export * from './ExtensionProvider';
export * from './PostMessageProvider';
export * from './zkidWallet';
7 changes: 7 additions & 0 deletions packages/providers/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
// Copyright 2021-2023 zcloak authors & contributors
// SPDX-License-Identifier: Apache-2.0

import { BaseProvider } from './base/Provider';

export type ProviderEvents = 'did_changed' | 'lock' | 'unlock';
type HexString = `0x${string}`;

export interface ZkidWalletProvider extends BaseProvider {
importCredential(data: HexString): Promise<void>;
}
14 changes: 14 additions & 0 deletions packages/providers/src/zkidWallet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright 2021-2023 zcloak authors & contributors
// SPDX-License-Identifier: Apache-2.0

import { ExtensionProvider } from './ExtensionProvider';
import { PostMessageProvider } from './PostMessageProvider';
import { ZkidWalletProvider } from './types';

export function adaptZkidWallet(allowedOrigins: RegExp[] | null = null): ZkidWalletProvider {
if (self !== parent) {
return new PostMessageProvider(allowedOrigins);
} else {
return new ExtensionProvider();
}
}
6 changes: 6 additions & 0 deletions packages/rpc-defines/src/defineZk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,10 @@ declare module '@zcloak/login-rpc/rpcs' {
did_decrypt: [DidDecryptParams, HexString];
proof_generate: [ZkpGenRequest, ZkpGenResponse];
}

interface RpcEvents {
zkID_Wallet_lock: any;
zkID_Wallet_unlock: any;
zkID_Wallet_didLoggedChanged: DidInfo;
}
}
5 changes: 4 additions & 1 deletion packages/rpc/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,8 @@
"sideEffects": false,
"type": "module",
"version": "1.0.1",
"main": "index.js"
"main": "index.js",
"dependencies": {
"@polkadot/util": "^10.3.1"
}
}
52 changes: 51 additions & 1 deletion packages/rpc/src/errors.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,58 @@
// Copyright 2021-2023 zcloak authors & contributors
// SPDX-License-Identifier: Apache-2.0

export interface RpcError {
export interface RpcErrorInterface {
code: number;
message: string;
meaning: string;
}

export class RpcError extends Error {
public code: number;
public meaning: string;

constructor(code: number, message: string, meaning: string) {
super(message);
this.code = code;
this.meaning = meaning;
}

public toJson(): RpcErrorInterface {
return {
code: this.code,
message: this.message,
meaning: this.meaning
};
}

public static fromCode(code: number): RpcErrorInterface {
switch (code) {
case -32700:
return new RpcError(
code,
'Parse error',
'Invalid JSON was received by the server. An error occurred on the server while parsing the JSON text.'
).toJson();
case -32600:
return new RpcError(code, 'Invalid Request', 'The JSON sent is not a valid Request object.').toJson();
case -32601:
return new RpcError(code, 'Method not found', 'The method does not exist / is not available.').toJson();
case -32602:
return new RpcError(code, 'Invalid params', 'Invalid method parameter(s).').toJson();
case -32603:
return new RpcError(code, 'Internal error', 'Internal JSON-Rpc error.').toJson();
case -32001:
return new RpcError(code, 'User Reject', 'User reject operation').toJson();
default:
return new RpcError(code, 'Unknown Error', 'Unknown Error').toJson();
}
}

public static fromError(error: any): RpcErrorInterface {
return new RpcError(
error?.code ?? -1,
error?.message ?? 'Unknown Error',
error?.reason ?? 'Unknown Error'
).toJson();
}
}
1 change: 1 addition & 0 deletions packages/rpc/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
export * from './errors';
export * from './request';
export * from './rpcs';
export * from './transport';
Loading

0 comments on commit f042638

Please sign in to comment.