Skip to content

Commit 56aceac

Browse files
committed
feat: signature validator library
1 parent b1ebf31 commit 56aceac

File tree

11 files changed

+430
-35
lines changed

11 files changed

+430
-35
lines changed

infrastructure/eid-wallet/src/lib/global/controllers/evault.ts

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import { PUBLIC_REGISTRY_URL, PUBLIC_PROVISIONER_URL, PUBLIC_EID_WALLET_TOKEN } from "$env/static/public";
1+
import {
2+
PUBLIC_REGISTRY_URL,
3+
PUBLIC_PROVISIONER_URL,
4+
PUBLIC_EID_WALLET_TOKEN,
5+
} from "$env/static/public";
26
import type { Store } from "@tauri-apps/plugin-store";
37
import axios from "axios";
48
import { GraphQLClient } from "graphql-request";
@@ -52,7 +56,11 @@ export class VaultController {
5256
#profileCreationStatus: "idle" | "loading" | "success" | "failed" = "idle";
5357
#notificationService: NotificationService;
5458

55-
constructor(store: Store, userController: UserController, keyService?: KeyService) {
59+
constructor(
60+
store: Store,
61+
userController: UserController,
62+
keyService?: KeyService,
63+
) {
5664
this.#store = store;
5765
this.#userController = userController;
5866
this.#keyService = keyService || null;
@@ -86,12 +94,16 @@ export class VaultController {
8694
// Check if we've already saved the public key
8795
const savedKey = localStorage.getItem(`publicKeySaved_${eName}`);
8896
if (savedKey === "true") {
89-
console.log(`Public key already saved for ${eName}, skipping sync`);
97+
console.log(
98+
`Public key already saved for ${eName}, skipping sync`,
99+
);
90100
return;
91101
}
92102

93103
if (!this.#keyService) {
94-
console.warn("KeyService not available, cannot sync public key");
104+
console.warn(
105+
"KeyService not available, cannot sync public key",
106+
);
95107
return;
96108
}
97109

@@ -121,29 +133,39 @@ export class VaultController {
121133
// Get public key using the exact same logic as onboarding/verification flow
122134
// KEY_ID is always "default", context depends on whether user is pre-verification
123135
const KEY_ID = "default";
124-
136+
125137
// Determine context: check if user is pre-verification (fake/demo user)
126138
const isFake = await this.#userController.isFake;
127139
const context = isFake ? "pre-verification" : "onboarding";
128140

129141
// Get public key using the same method as getApplicationPublicKey() in onboarding/verify
130142
let publicKey: string | undefined;
131143
try {
132-
publicKey = await this.#keyService.getPublicKey(KEY_ID, context);
144+
publicKey = await this.#keyService.getPublicKey(
145+
KEY_ID,
146+
context,
147+
);
133148
} catch (error) {
134-
console.error(`Failed to get public key for ${KEY_ID} with context ${context}:`, error);
149+
console.error(
150+
`Failed to get public key for ${KEY_ID} with context ${context}:`,
151+
error,
152+
);
135153
return;
136154
}
137155

138156
if (!publicKey) {
139-
console.warn(`No public key found for ${KEY_ID} with context ${context}, cannot sync`);
157+
console.warn(
158+
`No public key found for ${KEY_ID} with context ${context}, cannot sync`,
159+
);
140160
return;
141161
}
142162

143163
// Get authentication token from environment variable
144164
const authToken = PUBLIC_EID_WALLET_TOKEN || null;
145165
if (!authToken) {
146-
console.warn("PUBLIC_EID_WALLET_TOKEN not set, request may fail authentication");
166+
console.warn(
167+
"PUBLIC_EID_WALLET_TOKEN not set, request may fail authentication",
168+
);
147169
}
148170

149171
// Call PATCH /public-key to save the public key
@@ -157,11 +179,7 @@ export class VaultController {
157179
headers["Authorization"] = `Bearer ${authToken}`;
158180
}
159181

160-
await axios.patch(
161-
patchUrl,
162-
{ publicKey },
163-
{ headers }
164-
);
182+
await axios.patch(patchUrl, { publicKey }, { headers });
165183

166184
// Mark as saved
167185
localStorage.setItem(`publicKeySaved_${eName}`, "true");

infrastructure/eid-wallet/src/lib/global/state.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,11 @@ export class GlobalState {
3535
this.securityController = new SecurityController(store);
3636
this.userController = new UserController(store);
3737
this.keyService = keyService;
38-
this.vaultController = new VaultController(store, this.userController, keyService);
38+
this.vaultController = new VaultController(
39+
store,
40+
this.userController,
41+
keyService,
42+
);
3943
this.notificationService = NotificationService.getInstance();
4044
}
4145

infrastructure/eid-wallet/src/routes/(auth)/login/+page.svelte

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,9 @@ onMount(async () => {
101101
102102
// Sync public key to eVault core
103103
try {
104-
await globalState.vaultController.syncPublicKey(vault.ename);
104+
await globalState.vaultController.syncPublicKey(
105+
vault.ename,
106+
);
105107
} catch (error) {
106108
console.error("Error syncing public key:", error);
107109
// Continue to app even if sync fails - non-blocking
@@ -181,7 +183,9 @@ onMount(async () => {
181183
182184
// Sync public key to eVault core
183185
try {
184-
await globalState.vaultController.syncPublicKey(vault.ename);
186+
await globalState.vaultController.syncPublicKey(
187+
vault.ename,
188+
);
185189
} catch (error) {
186190
console.error("Error syncing public key:", error);
187191
// Continue to app even if sync fails - non-blocking

infrastructure/evault-core/src/core/http/server.ts

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -286,30 +286,50 @@ export async function registerHttpRoutes(
286286
// Helper function to validate JWT token
287287
async function validateToken(authHeader: string | null): Promise<any | null> {
288288
if (!authHeader || !authHeader.startsWith("Bearer ")) {
289+
console.error("Token validation: Missing or invalid Authorization header format");
289290
return null;
290291
}
291292

292293
const token = authHeader.substring(7); // Remove 'Bearer ' prefix
293294

294295
try {
295-
if (!process.env.REGISTRY_URL) {
296-
console.error("REGISTRY_URL is not set");
296+
// Try REGISTRY_URL first, fallback to PUBLIC_REGISTRY_URL
297+
const registryUrl = process.env.REGISTRY_URL || process.env.PUBLIC_REGISTRY_URL;
298+
if (!registryUrl) {
299+
console.error("Token validation: REGISTRY_URL or PUBLIC_REGISTRY_URL is not set");
297300
return null;
298301
}
299302

300-
const jwksResponse = await axios.get(
301-
new URL(
302-
`/.well-known/jwks.json`,
303-
process.env.REGISTRY_URL
304-
).toString()
305-
);
303+
const jwksUrl = new URL(`/.well-known/jwks.json`, registryUrl).toString();
304+
console.log(`Token validation: Fetching JWKS from ${jwksUrl}`);
305+
306+
const jwksResponse = await axios.get(jwksUrl, {
307+
timeout: 5000,
308+
});
306309

310+
console.log(`Token validation: JWKS response keys count: ${jwksResponse.data?.keys?.length || 0}`);
311+
307312
const JWKS = jose.createLocalJWKSet(jwksResponse.data);
313+
314+
// Decode token header to see what kid it's using
315+
const decodedHeader = jose.decodeProtectedHeader(token);
316+
console.log(`Token validation: Token header - alg: ${decodedHeader.alg}, kid: ${decodedHeader.kid}`);
317+
308318
const { payload } = await jose.jwtVerify(token, JWKS);
309-
319+
320+
console.log(`Token validation: Token verified successfully, payload:`, payload);
310321
return payload;
311-
} catch (error) {
312-
console.error("Token validation failed:", error);
322+
} catch (error: any) {
323+
console.error("Token validation failed:", error.message || error);
324+
if (error.code) {
325+
console.error(`Token validation error code: ${error.code}`);
326+
}
327+
if (error.response) {
328+
console.error(`Token validation HTTP error: ${error.response.status} - ${error.response.statusText}`);
329+
}
330+
if (error.cause) {
331+
console.error(`Token validation error cause:`, error.cause);
332+
}
313333
return null;
314334
}
315335
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
node_modules/
2+
dist/
3+
*.log
4+
.DS_Store
5+
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Signature Validator
2+
3+
A TypeScript library for verifying signatures using public keys retrieved from eVault.
4+
5+
## Installation
6+
7+
```bash
8+
npm install
9+
npm run build
10+
```
11+
12+
## Usage
13+
14+
```typescript
15+
import { verifySignature } from "signature-validator";
16+
17+
const result = await verifySignature({
18+
eName: "@user.w3id",
19+
signature: "z...", // multibase encoded signature
20+
payload: "message to verify",
21+
registryBaseUrl: "https://registry.example.com"
22+
});
23+
24+
if (result.valid) {
25+
console.log("Signature is valid!");
26+
} else {
27+
console.error("Signature invalid:", result.error);
28+
}
29+
```
30+
31+
## API
32+
33+
### `verifySignature(options: VerifySignatureOptions): Promise<VerifySignatureResult>`
34+
35+
Verifies a signature by:
36+
1. Resolving the eVault URL from the registry using the eName
37+
2. Fetching the public key from the eVault `/whois` endpoint
38+
3. Decoding the multibase-encoded public key
39+
4. Verifying the signature using Web Crypto API
40+
41+
#### Parameters
42+
43+
- `eName`: The eName (W3ID) of the user
44+
- `signature`: The signature to verify (multibase encoded string, supports 'z' prefix for base58btc or base64)
45+
- `payload`: The payload that was signed (string)
46+
- `registryBaseUrl`: Base URL of the registry service
47+
48+
#### Returns
49+
50+
- `valid`: Boolean indicating if the signature is valid
51+
- `error`: Error message if verification failed
52+
- `publicKey`: The public key that was used for verification
53+
54+
## Public Key Format
55+
56+
Public keys are expected to be in multibase format starting with 'z':
57+
- `z` prefix indicates multibase encoding
58+
- Supports base58btc (standard) or hex encoding
59+
60+
Example: `z3059301306072a8648ce3d020106082a8648ce3d03010703420004a16b063e785d25945c44ae2e7a4cbd94c3316533427261244f696609d6afb848155b9016ad8d5c9ec59053b3b2cf2511af0c2414fc53d2abf96323bb1a031902`
61+
62+
## Signature Format
63+
64+
Signatures can be:
65+
- Multibase encoded (starting with 'z' for base58btc)
66+
- Base64 encoded (for software keys)
67+
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"name": "signature-validator",
3+
"version": "1.0.0",
4+
"description": "Library for verifying signatures using public keys from eVault",
5+
"main": "dist/index.js",
6+
"types": "dist/index.d.ts",
7+
"scripts": {
8+
"build": "tsc",
9+
"test": "vitest",
10+
"dev": "tsc --watch"
11+
},
12+
"dependencies": {
13+
"axios": "^1.6.7",
14+
"multiformats": "^13.3.2"
15+
},
16+
"devDependencies": {
17+
"@types/node": "^20.11.24",
18+
"typescript": "^5.3.3",
19+
"vitest": "^1.6.1"
20+
}
21+
}

0 commit comments

Comments
 (0)