antaeus.keycloak.react 1.0.6

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.
@@ -0,0 +1,601 @@
1
+ /**
2
+ * @your-org/keycloak-react
3
+ * Default CSS styles for Keycloak React components
4
+ *
5
+ * Import this file in your app:
6
+ * import '@your-org/keycloak-react/styles.css';
7
+ */
8
+
9
+ /* === Base Variables === */
10
+ :root {
11
+ --kc-primary: #0066cc;
12
+ --kc-primary-hover: #0052a3;
13
+ --kc-secondary: #6c757d;
14
+ --kc-secondary-hover: #5a6268;
15
+ --kc-danger: #dc3545;
16
+ --kc-danger-hover: #c82333;
17
+ --kc-warning: #ffc107;
18
+ --kc-critical: #dc3545;
19
+ --kc-success: #28a745;
20
+ --kc-text: #212529;
21
+ --kc-text-light: #6c757d;
22
+ --kc-bg: #ffffff;
23
+ --kc-border: #dee2e6;
24
+ --kc-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
25
+ --kc-radius: 4px;
26
+ --kc-transition: all 0.2s ease;
27
+ }
28
+
29
+ /* === Button Base === */
30
+ .kc-button {
31
+ display: inline-flex;
32
+ align-items: center;
33
+ justify-content: center;
34
+ gap: 8px;
35
+ padding: 8px 16px;
36
+ font-family: inherit;
37
+ font-size: 14px;
38
+ font-weight: 500;
39
+ line-height: 1.5;
40
+ text-align: center;
41
+ text-decoration: none;
42
+ white-space: nowrap;
43
+ vertical-align: middle;
44
+ cursor: pointer;
45
+ border: 1px solid transparent;
46
+ border-radius: var(--kc-radius);
47
+ background-color: transparent;
48
+ color: var(--kc-text);
49
+ transition: var(--kc-transition);
50
+ user-select: none;
51
+ }
52
+
53
+ .kc-button:hover {
54
+ text-decoration: none;
55
+ }
56
+
57
+ .kc-button:focus {
58
+ outline: 2px solid var(--kc-primary);
59
+ outline-offset: 2px;
60
+ }
61
+
62
+ .kc-button:disabled {
63
+ opacity: 0.6;
64
+ cursor: not-allowed;
65
+ pointer-events: none;
66
+ }
67
+
68
+ /* === Button Variants === */
69
+ .kc-button--primary {
70
+ background-color: var(--kc-primary);
71
+ border-color: var(--kc-primary);
72
+ color: white;
73
+ }
74
+
75
+ .kc-button--primary:hover {
76
+ background-color: var(--kc-primary-hover);
77
+ border-color: var(--kc-primary-hover);
78
+ }
79
+
80
+ .kc-button--secondary {
81
+ background-color: var(--kc-secondary);
82
+ border-color: var(--kc-secondary);
83
+ color: white;
84
+ }
85
+
86
+ .kc-button--secondary:hover {
87
+ background-color: var(--kc-secondary-hover);
88
+ border-color: var(--kc-secondary-hover);
89
+ }
90
+
91
+ .kc-button--outline {
92
+ background-color: transparent;
93
+ border-color: var(--kc-primary);
94
+ color: var(--kc-primary);
95
+ }
96
+
97
+ .kc-button--outline:hover {
98
+ background-color: var(--kc-primary);
99
+ color: white;
100
+ }
101
+
102
+ .kc-button--danger {
103
+ background-color: var(--kc-danger);
104
+ border-color: var(--kc-danger);
105
+ color: white;
106
+ }
107
+
108
+ .kc-button--danger:hover {
109
+ background-color: var(--kc-danger-hover);
110
+ border-color: var(--kc-danger-hover);
111
+ }
112
+
113
+ /* === Button Sizes === */
114
+ .kc-button--small {
115
+ padding: 4px 12px;
116
+ font-size: 12px;
117
+ }
118
+
119
+ .kc-button--medium {
120
+ padding: 8px 16px;
121
+ font-size: 14px;
122
+ }
123
+
124
+ .kc-button--large {
125
+ padding: 12px 24px;
126
+ font-size: 16px;
127
+ }
128
+
129
+ /* === Button Icon === */
130
+ .kc-button__icon {
131
+ display: inline-flex;
132
+ font-size: 1.2em;
133
+ }
134
+
135
+ .kc-button__text {
136
+ display: inline-flex;
137
+ }
138
+
139
+ /* === User Profile === */
140
+ .kc-user-profile {
141
+ display: flex;
142
+ align-items: center;
143
+ gap: 12px;
144
+ padding: 8px;
145
+ }
146
+
147
+ .kc-user-profile__avatar {
148
+ display: flex;
149
+ align-items: center;
150
+ justify-content: center;
151
+ flex-shrink: 0;
152
+ width: 40px;
153
+ height: 40px;
154
+ border-radius: 50%;
155
+ background: linear-gradient(135deg, var(--kc-primary), var(--kc-primary-hover));
156
+ color: white;
157
+ font-weight: 600;
158
+ font-size: 16px;
159
+ text-transform: uppercase;
160
+ }
161
+
162
+ .kc-user-profile__info {
163
+ display: flex;
164
+ flex-direction: column;
165
+ gap: 2px;
166
+ min-width: 0;
167
+ }
168
+
169
+ .kc-user-profile__name {
170
+ font-size: 14px;
171
+ font-weight: 600;
172
+ color: var(--kc-text);
173
+ white-space: nowrap;
174
+ overflow: hidden;
175
+ text-overflow: ellipsis;
176
+ }
177
+
178
+ .kc-user-profile__email {
179
+ font-size: 12px;
180
+ color: var(--kc-text-light);
181
+ white-space: nowrap;
182
+ overflow: hidden;
183
+ text-overflow: ellipsis;
184
+ }
185
+
186
+ .kc-user-profile__roles {
187
+ display: flex;
188
+ flex-wrap: wrap;
189
+ gap: 4px;
190
+ margin-top: 4px;
191
+ }
192
+
193
+ .kc-user-profile__role-badge {
194
+ display: inline-flex;
195
+ align-items: center;
196
+ padding: 2px 8px;
197
+ font-size: 11px;
198
+ font-weight: 500;
199
+ color: var(--kc-primary);
200
+ background-color: rgba(0, 102, 204, 0.1);
201
+ border: 1px solid rgba(0, 102, 204, 0.2);
202
+ border-radius: 12px;
203
+ white-space: nowrap;
204
+ }
205
+
206
+ /* === Session Monitor === */
207
+ .kc-session-monitor {
208
+ position: fixed;
209
+ z-index: 9999;
210
+ padding: 12px 16px;
211
+ background-color: var(--kc-bg);
212
+ border: 1px solid var(--kc-border);
213
+ border-radius: var(--kc-radius);
214
+ box-shadow: var(--kc-shadow);
215
+ min-width: 200px;
216
+ transition: var(--kc-transition);
217
+ }
218
+
219
+ /* === Session Monitor Positions === */
220
+ .kc-session-monitor--top-left {
221
+ top: 16px;
222
+ left: 16px;
223
+ }
224
+
225
+ .kc-session-monitor--top-right {
226
+ top: 16px;
227
+ right: 16px;
228
+ }
229
+
230
+ .kc-session-monitor--bottom-left {
231
+ bottom: 16px;
232
+ left: 16px;
233
+ }
234
+
235
+ .kc-session-monitor--bottom-right {
236
+ bottom: 16px;
237
+ right: 16px;
238
+ }
239
+
240
+ /* === Session Monitor States === */
241
+ .kc-session-monitor--normal {
242
+ border-color: var(--kc-success);
243
+ }
244
+
245
+ .kc-session-monitor--warning {
246
+ border-color: var(--kc-warning);
247
+ background-color: #fff9e6;
248
+ }
249
+
250
+ .kc-session-monitor--critical {
251
+ border-color: var(--kc-critical);
252
+ background-color: #ffe6e6;
253
+ animation: kc-pulse 1s infinite;
254
+ }
255
+
256
+ @keyframes kc-pulse {
257
+ 0%, 100% {
258
+ opacity: 1;
259
+ }
260
+ 50% {
261
+ opacity: 0.8;
262
+ }
263
+ }
264
+
265
+ .kc-session-monitor--minimized {
266
+ padding: 8px 12px;
267
+ min-width: auto;
268
+ }
269
+
270
+ /* === Session Monitor Content === */
271
+ .kc-session-monitor__toggle {
272
+ position: absolute;
273
+ top: 4px;
274
+ right: 4px;
275
+ padding: 4px 8px;
276
+ font-size: 10px;
277
+ background: transparent;
278
+ border: none;
279
+ cursor: pointer;
280
+ opacity: 0.6;
281
+ transition: opacity 0.2s;
282
+ }
283
+
284
+ .kc-session-monitor__toggle:hover {
285
+ opacity: 1;
286
+ }
287
+
288
+ .kc-session-monitor__content {
289
+ display: flex;
290
+ flex-direction: column;
291
+ gap: 4px;
292
+ }
293
+
294
+ .kc-session-monitor__label {
295
+ font-size: 12px;
296
+ color: var(--kc-text-light);
297
+ }
298
+
299
+ .kc-session-monitor__time {
300
+ font-size: 20px;
301
+ font-weight: 700;
302
+ color: var(--kc-text);
303
+ font-variant-numeric: tabular-nums;
304
+ }
305
+
306
+ .kc-session-monitor__progress-bar {
307
+ height: 4px;
308
+ background-color: rgba(0, 0, 0, 0.1);
309
+ border-radius: 2px;
310
+ overflow: hidden;
311
+ margin-top: 8px;
312
+ }
313
+
314
+ .kc-session-monitor__progress-fill {
315
+ height: 100%;
316
+ background-color: var(--kc-success);
317
+ transition: width 0.3s ease, background-color 0.3s ease;
318
+ }
319
+
320
+ .kc-session-monitor--warning .kc-session-monitor__progress-fill {
321
+ background-color: var(--kc-warning);
322
+ }
323
+
324
+ .kc-session-monitor--critical .kc-session-monitor__progress-fill {
325
+ background-color: var(--kc-critical);
326
+ }
327
+
328
+ .kc-session-monitor__status {
329
+ display: flex;
330
+ align-items: center;
331
+ gap: 8px;
332
+ margin-top: 8px;
333
+ padding-top: 8px;
334
+ border-top: 1px solid var(--kc-border);
335
+ font-size: 12px;
336
+ color: var(--kc-text-light);
337
+ }
338
+
339
+ /* === Device Login === */
340
+ .kc-device-login {
341
+ display: flex;
342
+ flex-direction: column;
343
+ align-items: center;
344
+ gap: 24px;
345
+ padding: 32px;
346
+ max-width: 400px;
347
+ margin: 0 auto;
348
+ text-align: center;
349
+ }
350
+
351
+ .kc-device-login--loading,
352
+ .kc-device-login--error {
353
+ padding: 48px;
354
+ }
355
+
356
+ .kc-device-login__title {
357
+ margin: 0;
358
+ font-size: 24px;
359
+ font-weight: 700;
360
+ color: var(--kc-text);
361
+ }
362
+
363
+ .kc-device-login__qr {
364
+ padding: 16px;
365
+ background-color: white;
366
+ border: 2px solid var(--kc-border);
367
+ border-radius: var(--kc-radius);
368
+ box-shadow: var(--kc-shadow);
369
+ }
370
+
371
+ .kc-device-login__instructions {
372
+ display: flex;
373
+ flex-direction: column;
374
+ gap: 12px;
375
+ }
376
+
377
+ .kc-device-login__instructions p {
378
+ margin: 0;
379
+ font-size: 14px;
380
+ color: var(--kc-text-light);
381
+ }
382
+
383
+ .kc-device-login__url {
384
+ display: inline-block;
385
+ padding: 8px 16px;
386
+ font-family: 'Courier New', monospace;
387
+ font-size: 14px;
388
+ color: var(--kc-primary);
389
+ background-color: rgba(0, 102, 204, 0.05);
390
+ border: 1px solid rgba(0, 102, 204, 0.2);
391
+ border-radius: var(--kc-radius);
392
+ word-break: break-all;
393
+ }
394
+
395
+ .kc-device-login__user-code {
396
+ display: inline-block;
397
+ padding: 12px 24px;
398
+ font-size: 28px;
399
+ font-weight: 700;
400
+ font-family: 'Courier New', monospace;
401
+ color: var(--kc-primary);
402
+ background-color: rgba(0, 102, 204, 0.1);
403
+ border: 2px solid var(--kc-primary);
404
+ border-radius: var(--kc-radius);
405
+ letter-spacing: 4px;
406
+ }
407
+
408
+ .kc-device-login__expiry {
409
+ font-size: 14px;
410
+ color: var(--kc-text-light);
411
+ font-variant-numeric: tabular-nums;
412
+ }
413
+
414
+ .kc-device-login__status {
415
+ display: flex;
416
+ align-items: center;
417
+ justify-content: center;
418
+ gap: 12px;
419
+ padding: 16px;
420
+ background-color: rgba(0, 102, 204, 0.05);
421
+ border-radius: var(--kc-radius);
422
+ width: 100%;
423
+ }
424
+
425
+ .kc-device-login__status p {
426
+ margin: 0;
427
+ font-size: 14px;
428
+ color: var(--kc-text-light);
429
+ }
430
+
431
+ /* === Device Login Error State === */
432
+ .kc-device-login__error-icon {
433
+ font-size: 48px;
434
+ }
435
+
436
+ .kc-device-login--error h2 {
437
+ margin: 0;
438
+ font-size: 20px;
439
+ color: var(--kc-danger);
440
+ }
441
+
442
+ .kc-device-login--error p {
443
+ margin: 8px 0 0;
444
+ font-size: 14px;
445
+ color: var(--kc-text-light);
446
+ }
447
+
448
+ .kc-device-login--error button {
449
+ margin-top: 16px;
450
+ }
451
+
452
+ /* === Spinner === */
453
+ .kc-device-login__spinner {
454
+ width: 40px;
455
+ height: 40px;
456
+ border: 4px solid rgba(0, 102, 204, 0.1);
457
+ border-top-color: var(--kc-primary);
458
+ border-radius: 50%;
459
+ animation: kc-spin 0.8s linear infinite;
460
+ }
461
+
462
+ .kc-device-login__spinner--small,
463
+ .kc-session-monitor__spinner--small {
464
+ width: 16px;
465
+ height: 16px;
466
+ border-width: 2px;
467
+ }
468
+
469
+ @keyframes kc-spin {
470
+ to {
471
+ transform: rotate(360deg);
472
+ }
473
+ }
474
+
475
+ /* === Loading State === */
476
+ .kc-loading {
477
+ display: flex;
478
+ flex-direction: column;
479
+ align-items: center;
480
+ justify-content: center;
481
+ gap: 16px;
482
+ padding: 48px;
483
+ text-align: center;
484
+ }
485
+
486
+ .kc-loading__spinner {
487
+ width: 40px;
488
+ height: 40px;
489
+ border: 4px solid rgba(0, 102, 204, 0.1);
490
+ border-top-color: var(--kc-primary);
491
+ border-radius: 50%;
492
+ animation: kc-spin 0.8s linear infinite;
493
+ }
494
+
495
+ .kc-loading__text {
496
+ font-size: 14px;
497
+ color: var(--kc-text-light);
498
+ }
499
+
500
+ /* === Unauthorized State === */
501
+ .kc-unauthorized {
502
+ display: flex;
503
+ flex-direction: column;
504
+ align-items: center;
505
+ justify-content: center;
506
+ gap: 16px;
507
+ padding: 48px;
508
+ text-align: center;
509
+ }
510
+
511
+ .kc-unauthorized__icon {
512
+ font-size: 48px;
513
+ }
514
+
515
+ .kc-unauthorized__title {
516
+ margin: 0;
517
+ font-size: 20px;
518
+ font-weight: 700;
519
+ color: var(--kc-text);
520
+ }
521
+
522
+ .kc-unauthorized__message {
523
+ margin: 0;
524
+ font-size: 14px;
525
+ color: var(--kc-text-light);
526
+ }
527
+
528
+ /* === Responsive Design === */
529
+ @media (max-width: 640px) {
530
+ .kc-device-login {
531
+ padding: 16px;
532
+ }
533
+
534
+ .kc-device-login__qr {
535
+ padding: 12px;
536
+ }
537
+
538
+ .kc-device-login__user-code {
539
+ font-size: 24px;
540
+ padding: 10px 20px;
541
+ letter-spacing: 2px;
542
+ }
543
+
544
+ .kc-session-monitor {
545
+ min-width: 160px;
546
+ font-size: 12px;
547
+ }
548
+
549
+ .kc-session-monitor--top-left,
550
+ .kc-session-monitor--top-right {
551
+ top: 8px;
552
+ }
553
+
554
+ .kc-session-monitor--bottom-left,
555
+ .kc-session-monitor--bottom-right {
556
+ bottom: 8px;
557
+ }
558
+
559
+ .kc-session-monitor--top-left,
560
+ .kc-session-monitor--bottom-left {
561
+ left: 8px;
562
+ }
563
+
564
+ .kc-session-monitor--top-right,
565
+ .kc-session-monitor--bottom-right {
566
+ right: 8px;
567
+ }
568
+
569
+ .kc-user-profile {
570
+ gap: 8px;
571
+ padding: 4px;
572
+ }
573
+
574
+ .kc-user-profile__avatar {
575
+ width: 32px;
576
+ height: 32px;
577
+ font-size: 14px;
578
+ }
579
+ }
580
+
581
+ /* === Dark Mode Support === */
582
+ @media (prefers-color-scheme: dark) {
583
+ :root {
584
+ --kc-text: #f8f9fa;
585
+ --kc-text-light: #adb5bd;
586
+ --kc-bg: #212529;
587
+ --kc-border: #495057;
588
+ }
589
+
590
+ .kc-session-monitor--warning {
591
+ background-color: rgba(255, 193, 7, 0.15);
592
+ }
593
+
594
+ .kc-session-monitor--critical {
595
+ background-color: rgba(220, 53, 69, 0.15);
596
+ }
597
+
598
+ .kc-device-login__qr {
599
+ background-color: white;
600
+ }
601
+ }
package/package.json ADDED
@@ -0,0 +1,72 @@
1
+ {
2
+ "name": "antaeus.keycloak.react",
3
+ "version": "1.0.6",
4
+ "description": "Complete Keycloak integration for React with device flow, M2M, and session management",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ },
14
+ "./styles.css": "./dist/styles.css"
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "README.md",
19
+ "LICENSE"
20
+ ],
21
+ "scripts": {
22
+ "build": "tsup",
23
+ "dev": "tsup --watch",
24
+ "test": "vitest",
25
+ "test:ui": "vitest --ui",
26
+ "lint": "eslint src --ext .ts,.tsx",
27
+ "lint:fix": "eslint src --ext .ts,.tsx --fix",
28
+ "format": "prettier --write \"src/**/*.{ts,tsx}\"",
29
+ "type-check": "tsc --noEmit",
30
+ "prepublishOnly": "npm run type-check && npm run build"
31
+ },
32
+ "keywords": [
33
+ "keycloak",
34
+ "react",
35
+ "authentication",
36
+ "oidc",
37
+ "oauth2",
38
+ "device-flow",
39
+ "sso",
40
+ "pkce"
41
+ ],
42
+ "author": "Saeed Aghdam",
43
+ "license": "MIT",
44
+ "peerDependencies": {
45
+ "react": "^18.0.0",
46
+ "react-dom": "^18.0.0",
47
+ "react-router-dom": "^6.0.0"
48
+ },
49
+ "dependencies": {
50
+ "oidc-client-ts": "^3.0.1",
51
+ "qrcode.react": "^3.1.0",
52
+ "react-oidc-context": "^3.1.0"
53
+ },
54
+ "devDependencies": {
55
+ "@types/react": "^18.2.0",
56
+ "@types/react-dom": "^18.2.0",
57
+ "@typescript-eslint/eslint-plugin": "^6.0.0",
58
+ "@typescript-eslint/parser": "^6.0.0",
59
+ "@testing-library/react": "^14.0.0",
60
+ "@testing-library/jest-dom": "^6.0.0",
61
+ "@testing-library/user-event": "^14.0.0",
62
+ "eslint": "^8.0.0",
63
+ "prettier": "^3.0.0",
64
+ "typescript": "^5.3.0",
65
+ "tsup": "^8.0.0",
66
+ "vitest": "^1.0.0"
67
+ },
68
+ "repository": {
69
+ "type": "git",
70
+ "url": "https://github.com/saeedmaghdam/sso"
71
+ }
72
+ }