@worldcoin/idkit-core 1.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +7 -0
- package/build/chunk-25ALATTL.js +54 -0
- package/build/config-5feac22f.d.ts +28 -0
- package/build/index.d.ts +52 -0
- package/build/index.js +154 -0
- package/build/lib/hashing.d.ts +20 -0
- package/build/lib/hashing.js +14 -0
- package/package.json +68 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright (c) 2022 Worldcoin Foundation
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// src/lib/hashing.ts
|
|
2
|
+
import { Buffer } from "buffer/index";
|
|
3
|
+
import { encodePacked, isBytes, isHex, keccak256 } from "viem";
|
|
4
|
+
function hashToField(input) {
|
|
5
|
+
if (isBytes(input) || isHex(input))
|
|
6
|
+
return hashEncodedBytes(input);
|
|
7
|
+
return hashString(input);
|
|
8
|
+
}
|
|
9
|
+
function packAndEncode(input) {
|
|
10
|
+
const [types, values] = input.reduce(
|
|
11
|
+
([types2, values2], [type, value]) => {
|
|
12
|
+
types2.push(type);
|
|
13
|
+
values2.push(value);
|
|
14
|
+
return [types2, values2];
|
|
15
|
+
},
|
|
16
|
+
[[], []]
|
|
17
|
+
);
|
|
18
|
+
return hashEncodedBytes(encodePacked(types, values));
|
|
19
|
+
}
|
|
20
|
+
function hashString(input) {
|
|
21
|
+
const bytesInput = Buffer.from(input);
|
|
22
|
+
return hashEncodedBytes(bytesInput);
|
|
23
|
+
}
|
|
24
|
+
function hashEncodedBytes(input) {
|
|
25
|
+
const hash = BigInt(keccak256(input)) >> 8n;
|
|
26
|
+
const rawDigest = hash.toString(16);
|
|
27
|
+
return { hash, digest: `0x${rawDigest.padStart(64, "0")}` };
|
|
28
|
+
}
|
|
29
|
+
var solidityEncode = (types, values) => {
|
|
30
|
+
if (types.length !== values.length) {
|
|
31
|
+
throw new Error("Types and values arrays must have the same length.");
|
|
32
|
+
}
|
|
33
|
+
return { types, values };
|
|
34
|
+
};
|
|
35
|
+
var generateSignal = (signal) => {
|
|
36
|
+
if (!signal || typeof signal === "string")
|
|
37
|
+
return hashToField(signal ?? "");
|
|
38
|
+
return packAndEncode(signal.types.map((type, index) => [type, signal.values[index]]));
|
|
39
|
+
};
|
|
40
|
+
var encodeAction = (action) => {
|
|
41
|
+
if (!action)
|
|
42
|
+
return "";
|
|
43
|
+
if (typeof action === "string")
|
|
44
|
+
return action;
|
|
45
|
+
return action.types.map((type, index) => `${type}(${action.values[index]})`).join(",");
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export {
|
|
49
|
+
hashToField,
|
|
50
|
+
packAndEncode,
|
|
51
|
+
solidityEncode,
|
|
52
|
+
generateSignal,
|
|
53
|
+
encodeAction
|
|
54
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
declare const brand: unique symbol;
|
|
2
|
+
type Brand<T, TBrand extends string> = T & {
|
|
3
|
+
[brand]: TBrand;
|
|
4
|
+
};
|
|
5
|
+
type AbiEncodedValue = Brand<{
|
|
6
|
+
types: string[];
|
|
7
|
+
values: unknown[];
|
|
8
|
+
}, 'AbiEncodedValue'>;
|
|
9
|
+
declare enum CredentialType {
|
|
10
|
+
Orb = "orb",
|
|
11
|
+
Device = "device"
|
|
12
|
+
}
|
|
13
|
+
type IDKitConfig = {
|
|
14
|
+
/** Unique identifier for the app verifying the action. This should be the app ID obtained from the Developer Portal. */
|
|
15
|
+
app_id: string;
|
|
16
|
+
/** The description of the specific action (shown to users in World App). Only recommended for actions created on-the-fly. */
|
|
17
|
+
action_description?: string;
|
|
18
|
+
/** Encodes data into a proof that must match when validating. Read more on the [On-chain section](https://docs.worldcoin.org/advanced/on-chain). */
|
|
19
|
+
signal?: AbiEncodedValue | string;
|
|
20
|
+
/** Identifier for the action the user is performing. Should be left blank for [Sign in with Worldcoin](https://docs.worldcoin.org/id/sign-in). */
|
|
21
|
+
action?: AbiEncodedValue | string;
|
|
22
|
+
/** URL to a third-party bridge to use when connecting to the World App. Optional. */
|
|
23
|
+
bridge_url?: string;
|
|
24
|
+
/** An array of credential types to allow for verification. Will accept any combination of "orb" & "device". Defaults to orb. TypeScript apps can use the `CredentialType` enum. */
|
|
25
|
+
credential_types?: CredentialType[];
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export { AbiEncodedValue as A, CredentialType as C, IDKitConfig as I };
|
package/build/index.d.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { C as CredentialType, I as IDKitConfig } from './config-5feac22f.js';
|
|
2
|
+
export { A as AbiEncodedValue } from './config-5feac22f.js';
|
|
3
|
+
import * as zustand from 'zustand';
|
|
4
|
+
|
|
5
|
+
declare enum AppErrorCodes {
|
|
6
|
+
ConnectionFailed = "connection_failed",
|
|
7
|
+
VerificationRejected = "verification_rejected",
|
|
8
|
+
MaxVerificationsReached = "max_verifications_reached",
|
|
9
|
+
CredentialUnavailable = "credential_unavailable",
|
|
10
|
+
MalformedRequest = "malformed_request",
|
|
11
|
+
InvalidNetwork = "invalid_network",
|
|
12
|
+
InclusionProofFailed = "inclusion_proof_failed",
|
|
13
|
+
InclusionProofPending = "inclusion_proof_pending",
|
|
14
|
+
UnexpectedResponse = "unexpected_response",
|
|
15
|
+
FailedByHostApp = "failed_by_host_app",
|
|
16
|
+
GenericError = "generic_error"
|
|
17
|
+
}
|
|
18
|
+
declare enum VerificationState {
|
|
19
|
+
PreparingClient = "loading_widget",
|
|
20
|
+
WaitingForConnection = "awaiting_connection",
|
|
21
|
+
WaitingForApp = "awaiting_app",
|
|
22
|
+
Confirmed = "confirmed",
|
|
23
|
+
Failed = "failed"
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface ISuccessResult {
|
|
27
|
+
proof: string;
|
|
28
|
+
merkle_root: string;
|
|
29
|
+
nullifier_hash: string;
|
|
30
|
+
credential_type: CredentialType;
|
|
31
|
+
}
|
|
32
|
+
interface IErrorState {
|
|
33
|
+
code: AppErrorCodes;
|
|
34
|
+
message?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
type WorldBridgeStore = {
|
|
38
|
+
bridge_url: string;
|
|
39
|
+
iv: Uint8Array | null;
|
|
40
|
+
key: CryptoKey | null;
|
|
41
|
+
requestId: string | null;
|
|
42
|
+
connectorURI: string | null;
|
|
43
|
+
result: ISuccessResult | null;
|
|
44
|
+
errorCode: AppErrorCodes | null;
|
|
45
|
+
verificationState: VerificationState;
|
|
46
|
+
createClient: (app_id: IDKitConfig['app_id'], action: IDKitConfig['action'], signal?: IDKitConfig['signal'], bridge_url?: IDKitConfig['bridge_url'], credential_types?: IDKitConfig['credential_types'], action_description?: IDKitConfig['action_description']) => Promise<void>;
|
|
47
|
+
pollForUpdates: () => Promise<void>;
|
|
48
|
+
reset: () => void;
|
|
49
|
+
};
|
|
50
|
+
declare const useWorldBridgeStore: zustand.UseBoundStore<zustand.StoreApi<WorldBridgeStore>>;
|
|
51
|
+
|
|
52
|
+
export { AppErrorCodes, CredentialType, IDKitConfig, IErrorState, ISuccessResult, VerificationState, WorldBridgeStore, useWorldBridgeStore };
|
package/build/index.js
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import {
|
|
2
|
+
encodeAction,
|
|
3
|
+
generateSignal
|
|
4
|
+
} from "./chunk-25ALATTL.js";
|
|
5
|
+
|
|
6
|
+
// src/types/bridge.ts
|
|
7
|
+
var AppErrorCodes = /* @__PURE__ */ ((AppErrorCodes2) => {
|
|
8
|
+
AppErrorCodes2["ConnectionFailed"] = "connection_failed";
|
|
9
|
+
AppErrorCodes2["VerificationRejected"] = "verification_rejected";
|
|
10
|
+
AppErrorCodes2["MaxVerificationsReached"] = "max_verifications_reached";
|
|
11
|
+
AppErrorCodes2["CredentialUnavailable"] = "credential_unavailable";
|
|
12
|
+
AppErrorCodes2["MalformedRequest"] = "malformed_request";
|
|
13
|
+
AppErrorCodes2["InvalidNetwork"] = "invalid_network";
|
|
14
|
+
AppErrorCodes2["InclusionProofFailed"] = "inclusion_proof_failed";
|
|
15
|
+
AppErrorCodes2["InclusionProofPending"] = "inclusion_proof_pending";
|
|
16
|
+
AppErrorCodes2["UnexpectedResponse"] = "unexpected_response";
|
|
17
|
+
AppErrorCodes2["FailedByHostApp"] = "failed_by_host_app";
|
|
18
|
+
AppErrorCodes2["GenericError"] = "generic_error";
|
|
19
|
+
return AppErrorCodes2;
|
|
20
|
+
})(AppErrorCodes || {});
|
|
21
|
+
var VerificationState = /* @__PURE__ */ ((VerificationState2) => {
|
|
22
|
+
VerificationState2["PreparingClient"] = "loading_widget";
|
|
23
|
+
VerificationState2["WaitingForConnection"] = "awaiting_connection";
|
|
24
|
+
VerificationState2["WaitingForApp"] = "awaiting_app";
|
|
25
|
+
VerificationState2["Confirmed"] = "confirmed";
|
|
26
|
+
VerificationState2["Failed"] = "failed";
|
|
27
|
+
return VerificationState2;
|
|
28
|
+
})(VerificationState || {});
|
|
29
|
+
|
|
30
|
+
// src/types/config.ts
|
|
31
|
+
var CredentialType = /* @__PURE__ */ ((CredentialType2) => {
|
|
32
|
+
CredentialType2["Orb"] = "orb";
|
|
33
|
+
CredentialType2["Device"] = "device";
|
|
34
|
+
return CredentialType2;
|
|
35
|
+
})(CredentialType || {});
|
|
36
|
+
|
|
37
|
+
// src/bridge.ts
|
|
38
|
+
import { create } from "zustand";
|
|
39
|
+
|
|
40
|
+
// src/lib/utils.ts
|
|
41
|
+
import { Buffer } from "buffer/index";
|
|
42
|
+
var buffer_encode = (buffer) => {
|
|
43
|
+
return Buffer.from(buffer).toString("base64");
|
|
44
|
+
};
|
|
45
|
+
var buffer_decode = (encoded) => {
|
|
46
|
+
return Buffer.from(encoded, "base64");
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// src/lib/crypto.ts
|
|
50
|
+
var encoder = new TextEncoder();
|
|
51
|
+
var decoder = new TextDecoder();
|
|
52
|
+
var generateKey = async () => {
|
|
53
|
+
return {
|
|
54
|
+
iv: window.crypto.getRandomValues(new Uint8Array(12)),
|
|
55
|
+
key: await window.crypto.subtle.generateKey({ name: "AES-GCM", length: 256 }, true, ["encrypt", "decrypt"])
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
var exportKey = async (key) => {
|
|
59
|
+
return buffer_encode(await window.crypto.subtle.exportKey("raw", key));
|
|
60
|
+
};
|
|
61
|
+
var encryptRequest = async (key, iv, request) => {
|
|
62
|
+
return {
|
|
63
|
+
iv: buffer_encode(iv),
|
|
64
|
+
payload: buffer_encode(
|
|
65
|
+
await window.crypto.subtle.encrypt({ name: "AES-GCM", iv }, key, encoder.encode(request))
|
|
66
|
+
)
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
var decryptResponse = async (key, iv, payload) => {
|
|
70
|
+
return decoder.decode(await window.crypto.subtle.decrypt({ name: "AES-GCM", iv }, key, buffer_decode(payload)));
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// src/bridge.ts
|
|
74
|
+
var DEFAULT_BRIDGE_URL = "https://bridge.worldcoin.org";
|
|
75
|
+
var useWorldBridgeStore = create((set, get) => ({
|
|
76
|
+
iv: null,
|
|
77
|
+
key: null,
|
|
78
|
+
result: null,
|
|
79
|
+
errorCode: null,
|
|
80
|
+
requestId: null,
|
|
81
|
+
connectorURI: null,
|
|
82
|
+
bridge_url: DEFAULT_BRIDGE_URL,
|
|
83
|
+
verificationState: "loading_widget" /* PreparingClient */,
|
|
84
|
+
createClient: async (app_id, action, signal, bridge_url, credential_types, action_description) => {
|
|
85
|
+
const { key, iv } = await generateKey();
|
|
86
|
+
const res = await fetch(`${bridge_url ?? DEFAULT_BRIDGE_URL}/request`, {
|
|
87
|
+
method: "POST",
|
|
88
|
+
headers: { "Content-Type": "application/json" },
|
|
89
|
+
body: JSON.stringify(
|
|
90
|
+
await encryptRequest(
|
|
91
|
+
key,
|
|
92
|
+
iv,
|
|
93
|
+
JSON.stringify({
|
|
94
|
+
app_id,
|
|
95
|
+
credential_types,
|
|
96
|
+
action_description,
|
|
97
|
+
action: encodeAction(action),
|
|
98
|
+
signal: generateSignal(signal).digest
|
|
99
|
+
})
|
|
100
|
+
)
|
|
101
|
+
)
|
|
102
|
+
});
|
|
103
|
+
if (!res.ok) {
|
|
104
|
+
set({ verificationState: "failed" /* Failed */ });
|
|
105
|
+
throw new Error("Failed to create client");
|
|
106
|
+
}
|
|
107
|
+
const { request_id } = await res.json();
|
|
108
|
+
set({
|
|
109
|
+
iv,
|
|
110
|
+
key,
|
|
111
|
+
requestId: request_id,
|
|
112
|
+
bridge_url: bridge_url ?? DEFAULT_BRIDGE_URL,
|
|
113
|
+
verificationState: "awaiting_connection" /* WaitingForConnection */,
|
|
114
|
+
connectorURI: `https://worldcoin.org/verify?t=wld&i=${request_id}&k=${encodeURIComponent(
|
|
115
|
+
await exportKey(key)
|
|
116
|
+
)}${bridge_url && bridge_url !== DEFAULT_BRIDGE_URL ? `&b=${encodeURIComponent(bridge_url)}` : ""}`
|
|
117
|
+
});
|
|
118
|
+
},
|
|
119
|
+
pollForUpdates: async () => {
|
|
120
|
+
const key = get().key;
|
|
121
|
+
if (!key)
|
|
122
|
+
throw new Error("No keypair found. Please call `createClient` first.");
|
|
123
|
+
const res = await fetch(`${get().bridge_url}/response/${get().requestId}`);
|
|
124
|
+
const { response, status } = await res.json();
|
|
125
|
+
if (status != "completed" /* Completed */) {
|
|
126
|
+
return set({
|
|
127
|
+
verificationState: status == "retrieved" /* Retrieved */ ? "awaiting_app" /* WaitingForApp */ : "awaiting_connection" /* WaitingForConnection */
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
const result = JSON.parse(await decryptResponse(key, buffer_decode(response.iv), response.payload));
|
|
131
|
+
if ("error_code" in result) {
|
|
132
|
+
return set({
|
|
133
|
+
errorCode: result.error_code,
|
|
134
|
+
verificationState: "failed" /* Failed */
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
set({ result, verificationState: "confirmed" /* Confirmed */, key: null, connectorURI: null, requestId: null });
|
|
138
|
+
},
|
|
139
|
+
reset: () => {
|
|
140
|
+
set({
|
|
141
|
+
iv: null,
|
|
142
|
+
key: null,
|
|
143
|
+
requestId: null,
|
|
144
|
+
connectorURI: null,
|
|
145
|
+
verificationState: "loading_widget" /* PreparingClient */
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
}));
|
|
149
|
+
export {
|
|
150
|
+
AppErrorCodes,
|
|
151
|
+
CredentialType,
|
|
152
|
+
VerificationState,
|
|
153
|
+
useWorldBridgeStore
|
|
154
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { A as AbiEncodedValue, I as IDKitConfig } from '../config-5feac22f.js';
|
|
2
|
+
|
|
3
|
+
interface HashFunctionOutput {
|
|
4
|
+
hash: bigint;
|
|
5
|
+
digest: `0x${string}`;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Hashes an input using the `keccak256` hashing function used across the World ID protocol, to be used as
|
|
9
|
+
* a ZKP input. The function will try to determine the best hashing mechanism, if the string already looks like hex-encoded
|
|
10
|
+
* bytes (e.g. `0x0000000000000000000000000000000000000000`), it will be hashed directly.
|
|
11
|
+
* @param input Any string, hex-like string, bytes represented as a hex string.
|
|
12
|
+
* @returns
|
|
13
|
+
*/
|
|
14
|
+
declare function hashToField(input: Uint8Array | string): HashFunctionOutput;
|
|
15
|
+
declare function packAndEncode(input: [string, unknown][]): HashFunctionOutput;
|
|
16
|
+
declare const solidityEncode: (types: string[], values: unknown[]) => AbiEncodedValue;
|
|
17
|
+
declare const generateSignal: (signal: IDKitConfig['signal']) => HashFunctionOutput;
|
|
18
|
+
declare const encodeAction: (action: IDKitConfig['action']) => string;
|
|
19
|
+
|
|
20
|
+
export { HashFunctionOutput, encodeAction, generateSignal, hashToField, packAndEncode, solidityEncode };
|
package/package.json
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@worldcoin/idkit-core",
|
|
3
|
+
"version": "1.0.0-alpha.0",
|
|
4
|
+
"homepage": "https://docs.worldcoin.org/id/idkit",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"private": false,
|
|
7
|
+
"description": "The identity SDK. Privacy-preserving identity and proof of personhood with World ID.",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/worldcoin/idkit-js",
|
|
11
|
+
"directory": "packages/core"
|
|
12
|
+
},
|
|
13
|
+
"type": "module",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"import": "./build/index.js",
|
|
17
|
+
"types": "./build/index.d.ts"
|
|
18
|
+
},
|
|
19
|
+
"./hashing": {
|
|
20
|
+
"import": "./build/lib/hashing.js",
|
|
21
|
+
"types": "./build/lib/hashing.d.ts"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"typesVersions": {
|
|
25
|
+
"*": {
|
|
26
|
+
"hashing": [
|
|
27
|
+
"./build/lib/hashing.d.ts"
|
|
28
|
+
],
|
|
29
|
+
"*": [
|
|
30
|
+
"./build/*/index.d.ts",
|
|
31
|
+
"./build/index.d.ts"
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=12.4"
|
|
37
|
+
},
|
|
38
|
+
"files": [
|
|
39
|
+
"build/**",
|
|
40
|
+
"README.md"
|
|
41
|
+
],
|
|
42
|
+
"keywords": [
|
|
43
|
+
"identity",
|
|
44
|
+
"ID",
|
|
45
|
+
"web3",
|
|
46
|
+
"proof-of-personhood",
|
|
47
|
+
"sybil resistance"
|
|
48
|
+
],
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"buffer": "^6.0.3",
|
|
51
|
+
"viem": "^1.14.0",
|
|
52
|
+
"zustand": "^4.3.3"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@typescript-eslint/eslint-plugin": "6.7.4",
|
|
56
|
+
"@typescript-eslint/parser": "6.7.4",
|
|
57
|
+
"esbuild": "0.14.43",
|
|
58
|
+
"prettier": "^2.7.1",
|
|
59
|
+
"prettier-plugin-sort-imports-desc": "^1.0.0",
|
|
60
|
+
"tsup": "^7.2.0",
|
|
61
|
+
"typescript": "5.2.2"
|
|
62
|
+
},
|
|
63
|
+
"scripts": {
|
|
64
|
+
"lint": "eslint src --ext .ts",
|
|
65
|
+
"build": "tsup",
|
|
66
|
+
"dev": "tsup --watch"
|
|
67
|
+
}
|
|
68
|
+
}
|