@ave-id/embed 0.2.0 → 0.2.2

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.
Files changed (2) hide show
  1. package/dist/embed.js +537 -0
  2. package/package.json +10 -2
package/dist/embed.js ADDED
@@ -0,0 +1,537 @@
1
+ const DEFAULT_THEME = "dark";
2
+
3
+ /**
4
+ * Mount Ave auth embed as an inline iframe
5
+ */
6
+ export function mountAveEmbed({
7
+ container,
8
+ clientId,
9
+ redirectUri,
10
+ scope = "openid profile email",
11
+ issuer = "https://aveid.net",
12
+ theme = DEFAULT_THEME,
13
+ width = "100%",
14
+ height = 720,
15
+ onSuccess,
16
+ onError,
17
+ onClose,
18
+ }) {
19
+ if (!container) {
20
+ throw new Error("container is required");
21
+ }
22
+
23
+ const iframe = document.createElement("iframe");
24
+ const params = new URLSearchParams({
25
+ client_id: clientId,
26
+ redirect_uri: redirectUri,
27
+ scope,
28
+ embed: "1",
29
+ theme,
30
+ });
31
+
32
+ iframe.src = `${issuer}/signin?${params.toString()}`;
33
+ iframe.style.width = width;
34
+ iframe.style.height = typeof height === "number" ? `${height}px` : height;
35
+ iframe.style.border = "0";
36
+ iframe.style.borderRadius = "24px";
37
+ iframe.style.background = "#090909";
38
+ iframe.allow = "publickey-credentials-get";
39
+
40
+ container.appendChild(iframe);
41
+
42
+ const messageHandler = (event) => {
43
+ if (event.origin !== issuer) return;
44
+ const data = event.data || {};
45
+
46
+ if (data.type === "ave:success") {
47
+ onSuccess?.(data.payload);
48
+ }
49
+
50
+ if (data.type === "ave:error") {
51
+ onError?.(data.payload);
52
+ }
53
+
54
+ if (data.type === "ave:close") {
55
+ onClose?.();
56
+ }
57
+ };
58
+
59
+ window.addEventListener("message", messageHandler);
60
+
61
+ return {
62
+ iframe,
63
+ destroy() {
64
+ window.removeEventListener("message", messageHandler);
65
+ iframe.remove();
66
+ },
67
+ postMessage(payload) {
68
+ iframe.contentWindow?.postMessage(payload, issuer);
69
+ },
70
+ };
71
+ }
72
+
73
+ /**
74
+ * Open Ave auth as a modal sheet overlay
75
+ */
76
+ export function openAveSheet({
77
+ clientId,
78
+ redirectUri,
79
+ scope = "openid profile email",
80
+ issuer = "https://aveid.net",
81
+ theme = DEFAULT_THEME,
82
+ codeChallenge,
83
+ codeChallengeMethod,
84
+ onSuccess,
85
+ onError,
86
+ onClose,
87
+ }) {
88
+ // Create overlay backdrop
89
+ const overlay = document.createElement("div");
90
+ overlay.style.cssText = `
91
+ position: fixed;
92
+ inset: 0;
93
+ background: rgba(0, 0, 0, 0.7);
94
+ backdrop-filter: blur(4px);
95
+ z-index: 999999;
96
+ display: flex;
97
+ align-items: flex-end;
98
+ justify-content: center;
99
+ animation: aveSheetFadeIn 0.2s ease-out;
100
+ `;
101
+
102
+ // Create sheet container
103
+ const sheet = document.createElement("div");
104
+ sheet.style.cssText = `
105
+ width: 100%;
106
+ max-width: 500px;
107
+ max-height: 90vh;
108
+ background: #090909;
109
+ border-radius: 24px 24px 0 0;
110
+ overflow: hidden;
111
+ animation: aveSheetSlideUp 0.3s ease-out;
112
+ position: relative;
113
+ `;
114
+
115
+ // Add drag handle
116
+ const dragHandle = document.createElement("div");
117
+ dragHandle.style.cssText = `
118
+ width: 40px;
119
+ height: 4px;
120
+ background: #333;
121
+ border-radius: 2px;
122
+ margin: 12px auto;
123
+ `;
124
+ sheet.appendChild(dragHandle);
125
+
126
+ // Create close button
127
+ const closeBtn = document.createElement("button");
128
+ closeBtn.innerHTML = `
129
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#888" stroke-width="2">
130
+ <path d="M18 6L6 18M6 6l12 12"/>
131
+ </svg>
132
+ `;
133
+ closeBtn.style.cssText = `
134
+ position: absolute;
135
+ top: 8px;
136
+ right: 12px;
137
+ background: transparent;
138
+ border: none;
139
+ cursor: pointer;
140
+ padding: 8px;
141
+ z-index: 10;
142
+ `;
143
+
144
+ // Create iframe
145
+ const iframe = document.createElement("iframe");
146
+ const params = new URLSearchParams({
147
+ client_id: clientId,
148
+ redirect_uri: redirectUri,
149
+ scope,
150
+ embed: "1",
151
+ theme,
152
+ });
153
+
154
+ if (codeChallenge) {
155
+ params.set("code_challenge", codeChallenge);
156
+ }
157
+ if (codeChallengeMethod) {
158
+ params.set("code_challenge_method", codeChallengeMethod);
159
+ }
160
+
161
+ iframe.src = `${issuer}/signin?${params.toString()}`;
162
+ iframe.style.cssText = `
163
+ width: 100%;
164
+ height: calc(90vh - 50px);
165
+ border: none;
166
+ background: #090909;
167
+ `;
168
+ iframe.allow = "publickey-credentials-get";
169
+
170
+ sheet.appendChild(closeBtn);
171
+ sheet.appendChild(iframe);
172
+ overlay.appendChild(sheet);
173
+
174
+ // Add animations
175
+ const style = document.createElement("style");
176
+ style.textContent = `
177
+ @keyframes aveSheetFadeIn {
178
+ from { opacity: 0; }
179
+ to { opacity: 1; }
180
+ }
181
+ @keyframes aveSheetSlideUp {
182
+ from { transform: translateY(100%); }
183
+ to { transform: translateY(0); }
184
+ }
185
+ @keyframes aveSheetSlideDown {
186
+ from { transform: translateY(0); }
187
+ to { transform: translateY(100%); }
188
+ }
189
+ @keyframes aveSheetFadeOut {
190
+ from { opacity: 1; }
191
+ to { opacity: 0; }
192
+ }
193
+ `;
194
+ document.head.appendChild(style);
195
+
196
+ // Close function
197
+ const close = () => {
198
+ sheet.style.animation = "aveSheetSlideDown 0.2s ease-in forwards";
199
+ overlay.style.animation = "aveSheetFadeOut 0.2s ease-in forwards";
200
+ setTimeout(() => {
201
+ overlay.remove();
202
+ style.remove();
203
+ window.removeEventListener("message", messageHandler);
204
+ onClose?.();
205
+ }, 200);
206
+ };
207
+
208
+ // Event handlers
209
+ closeBtn.onclick = close;
210
+ overlay.onclick = (e) => {
211
+ if (e.target === overlay) close();
212
+ };
213
+
214
+ const messageHandler = (event) => {
215
+ if (event.origin !== issuer) return;
216
+ const data = event.data || {};
217
+
218
+ if (data.type === "ave:success") {
219
+ close();
220
+ onSuccess?.(data.payload);
221
+ }
222
+
223
+ if (data.type === "ave:error") {
224
+ close();
225
+ onError?.(data.payload);
226
+ }
227
+
228
+ if (data.type === "ave:close") {
229
+ close();
230
+ }
231
+ };
232
+
233
+ window.addEventListener("message", messageHandler);
234
+
235
+ // Add to DOM
236
+ document.body.appendChild(overlay);
237
+
238
+ return {
239
+ close,
240
+ iframe,
241
+ };
242
+ }
243
+
244
+ /**
245
+ * Open Ave auth as a popup window (for desktop)
246
+ */
247
+ export function openAvePopup({
248
+ clientId,
249
+ redirectUri,
250
+ scope = "openid profile email",
251
+ issuer = "https://aveid.net",
252
+ codeChallenge,
253
+ codeChallengeMethod,
254
+ width = 450,
255
+ height = 650,
256
+ onSuccess,
257
+ onError,
258
+ onClose,
259
+ }) {
260
+ const params = new URLSearchParams({
261
+ client_id: clientId,
262
+ redirect_uri: redirectUri,
263
+ scope,
264
+ embed: "1",
265
+ });
266
+
267
+ if (codeChallenge) {
268
+ params.set("code_challenge", codeChallenge);
269
+ }
270
+ if (codeChallengeMethod) {
271
+ params.set("code_challenge_method", codeChallengeMethod);
272
+ }
273
+
274
+ const left = (window.innerWidth - width) / 2 + window.screenX;
275
+ const top = (window.innerHeight - height) / 2 + window.screenY;
276
+
277
+ const popup = window.open(
278
+ `${issuer}/signin?${params.toString()}`,
279
+ "ave_auth",
280
+ `width=${width},height=${height},left=${left},top=${top},menubar=no,toolbar=no,location=no,status=no`
281
+ );
282
+
283
+ if (!popup) {
284
+ onError?.({ error: "popup_blocked", message: "Popup was blocked by the browser" });
285
+ return null;
286
+ }
287
+
288
+ const messageHandler = (event) => {
289
+ if (event.origin !== issuer) return;
290
+ const data = event.data || {};
291
+
292
+ if (data.type === "ave:success") {
293
+ popup.close();
294
+ window.removeEventListener("message", messageHandler);
295
+ onSuccess?.(data.payload);
296
+ }
297
+
298
+ if (data.type === "ave:error") {
299
+ popup.close();
300
+ window.removeEventListener("message", messageHandler);
301
+ onError?.(data.payload);
302
+ }
303
+
304
+ if (data.type === "ave:close") {
305
+ popup.close();
306
+ window.removeEventListener("message", messageHandler);
307
+ onClose?.();
308
+ }
309
+ };
310
+
311
+ window.addEventListener("message", messageHandler);
312
+
313
+ // Check if popup was closed
314
+ const pollTimer = setInterval(() => {
315
+ if (popup.closed) {
316
+ clearInterval(pollTimer);
317
+ window.removeEventListener("message", messageHandler);
318
+ onClose?.();
319
+ }
320
+ }, 500);
321
+
322
+ return {
323
+ popup,
324
+ close() {
325
+ clearInterval(pollTimer);
326
+ popup.close();
327
+ window.removeEventListener("message", messageHandler);
328
+ },
329
+ };
330
+ }
331
+
332
+ // ============================================
333
+ // Ave Signing Embeds
334
+ // ============================================
335
+
336
+ /**
337
+ * Open Ave signing as a modal sheet overlay
338
+ */
339
+ export function openAveSigningSheet({
340
+ requestId,
341
+ issuer = "https://aveid.net",
342
+ onSigned,
343
+ onDenied,
344
+ onClose,
345
+ }) {
346
+ // Create overlay backdrop
347
+ const overlay = document.createElement("div");
348
+ overlay.style.cssText = `
349
+ position: fixed;
350
+ inset: 0;
351
+ background: rgba(0, 0, 0, 0.8);
352
+ backdrop-filter: blur(4px);
353
+ z-index: 999999;
354
+ display: flex;
355
+ align-items: flex-end;
356
+ justify-content: center;
357
+ animation: aveSheetFadeIn 0.2s ease-out;
358
+ `;
359
+
360
+ // Create sheet container
361
+ const sheet = document.createElement("div");
362
+ sheet.style.cssText = `
363
+ width: 100%;
364
+ max-width: 600px;
365
+ max-height: 90vh;
366
+ background: #111111;
367
+ border-radius: 32px 32px 0 0;
368
+ overflow: hidden;
369
+ animation: aveSheetSlideUp 0.3s ease-out;
370
+ position: relative;
371
+ `;
372
+
373
+ // Add drag handle
374
+ const dragHandle = document.createElement("div");
375
+ dragHandle.style.cssText = `
376
+ width: 40px;
377
+ height: 4px;
378
+ background: #333;
379
+ border-radius: 2px;
380
+ margin: 12px auto;
381
+ `;
382
+ sheet.appendChild(dragHandle);
383
+
384
+ // Create iframe
385
+ const iframe = document.createElement("iframe");
386
+ const params = new URLSearchParams({
387
+ requestId,
388
+ embed: "1",
389
+ });
390
+
391
+ iframe.src = `${issuer}/sign?${params.toString()}`;
392
+ iframe.style.cssText = `
393
+ width: 100%;
394
+ height: calc(90vh - 30px);
395
+ border: none;
396
+ background: transparent;
397
+ `;
398
+ iframe.allow = "publickey-credentials-get";
399
+
400
+ sheet.appendChild(iframe);
401
+ overlay.appendChild(sheet);
402
+
403
+ // Add animations (reuse if already present)
404
+ if (!document.getElementById("ave-sheet-styles")) {
405
+ const style = document.createElement("style");
406
+ style.id = "ave-sheet-styles";
407
+ style.textContent = `
408
+ @keyframes aveSheetFadeIn {
409
+ from { opacity: 0; }
410
+ to { opacity: 1; }
411
+ }
412
+ @keyframes aveSheetSlideUp {
413
+ from { transform: translateY(100%); }
414
+ to { transform: translateY(0); }
415
+ }
416
+ @keyframes aveSheetSlideDown {
417
+ from { transform: translateY(0); }
418
+ to { transform: translateY(100%); }
419
+ }
420
+ @keyframes aveSheetFadeOut {
421
+ from { opacity: 1; }
422
+ to { opacity: 0; }
423
+ }
424
+ `;
425
+ document.head.appendChild(style);
426
+ }
427
+
428
+ // Close function
429
+ const close = () => {
430
+ sheet.style.animation = "aveSheetSlideDown 0.2s ease-in forwards";
431
+ overlay.style.animation = "aveSheetFadeOut 0.2s ease-in forwards";
432
+ setTimeout(() => {
433
+ overlay.remove();
434
+ window.removeEventListener("message", messageHandler);
435
+ onClose?.();
436
+ }, 200);
437
+ };
438
+
439
+ // Click outside to close
440
+ overlay.onclick = (e) => {
441
+ if (e.target === overlay) close();
442
+ };
443
+
444
+ const messageHandler = (event) => {
445
+ if (event.origin !== issuer) return;
446
+ const data = event.data || {};
447
+
448
+ if (data.type === "ave:signed") {
449
+ close();
450
+ onSigned?.(data.payload);
451
+ }
452
+
453
+ if (data.type === "ave:denied") {
454
+ close();
455
+ onDenied?.(data.payload);
456
+ }
457
+ };
458
+
459
+ window.addEventListener("message", messageHandler);
460
+
461
+ // Add to DOM
462
+ document.body.appendChild(overlay);
463
+
464
+ return {
465
+ close,
466
+ iframe,
467
+ };
468
+ }
469
+
470
+ /**
471
+ * Open Ave signing as a popup window
472
+ */
473
+ export function openAveSigningPopup({
474
+ requestId,
475
+ issuer = "https://aveid.net",
476
+ width = 500,
477
+ height = 600,
478
+ onSigned,
479
+ onDenied,
480
+ onClose,
481
+ }) {
482
+ const params = new URLSearchParams({
483
+ requestId,
484
+ embed: "1",
485
+ });
486
+
487
+ const left = (window.innerWidth - width) / 2 + window.screenX;
488
+ const top = (window.innerHeight - height) / 2 + window.screenY;
489
+
490
+ const popup = window.open(
491
+ `${issuer}/sign?${params.toString()}`,
492
+ "ave_signing",
493
+ `width=${width},height=${height},left=${left},top=${top},menubar=no,toolbar=no,location=no,status=no`
494
+ );
495
+
496
+ if (!popup) {
497
+ onDenied?.({ error: "popup_blocked", message: "Popup was blocked by the browser" });
498
+ return null;
499
+ }
500
+
501
+ const messageHandler = (event) => {
502
+ if (event.origin !== issuer) return;
503
+ const data = event.data || {};
504
+
505
+ if (data.type === "ave:signed") {
506
+ popup.close();
507
+ window.removeEventListener("message", messageHandler);
508
+ onSigned?.(data.payload);
509
+ }
510
+
511
+ if (data.type === "ave:denied") {
512
+ popup.close();
513
+ window.removeEventListener("message", messageHandler);
514
+ onDenied?.(data.payload);
515
+ }
516
+ };
517
+
518
+ window.addEventListener("message", messageHandler);
519
+
520
+ // Check if popup was closed
521
+ const pollTimer = setInterval(() => {
522
+ if (popup.closed) {
523
+ clearInterval(pollTimer);
524
+ window.removeEventListener("message", messageHandler);
525
+ onClose?.();
526
+ }
527
+ }, 500);
528
+
529
+ return {
530
+ popup,
531
+ close() {
532
+ clearInterval(pollTimer);
533
+ popup.close();
534
+ window.removeEventListener("message", messageHandler);
535
+ },
536
+ };
537
+ }
package/package.json CHANGED
@@ -1,13 +1,21 @@
1
1
  {
2
2
  "name": "@ave-id/embed",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "type": "module",
5
5
  "main": "dist/embed.js",
6
+ "module": "dist/embed.js",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./dist/embed.js",
10
+ "default": "./dist/embed.js"
11
+ }
12
+ },
6
13
  "files": ["dist"],
7
14
  "publishConfig": {
8
15
  "access": "public"
9
16
  },
10
17
  "scripts": {
11
- "build": "mkdir -p dist && cp src/embed.js dist/embed.js"
18
+ "build": "mkdir -p dist && cp src/embed.js dist/embed.js",
19
+ "prepublishOnly": "npm run build"
12
20
  }
13
21
  }