@flink-app/bankid-plugin 0.12.1-alpha.25 → 0.12.1-alpha.33

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/README.md +777 -0
  2. package/package.json +2 -2
package/README.md ADDED
@@ -0,0 +1,777 @@
1
+ # BankID Plugin
2
+
3
+ A Flink plugin for Swedish BankID authentication and document signing. This plugin provides seamless integration with the Swedish BankID service for secure authentication and electronic signatures.
4
+
5
+ ## Features
6
+
7
+ - BankID authentication (Mobile BankID and QR code)
8
+ - Document signing with BankID
9
+ - Automatic QR code generation and refresh
10
+ - Session management with MongoDB
11
+ - Test and production environments
12
+ - Built-in HTTP endpoints (optional)
13
+ - TypeScript support with full type safety
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install @flink-app/bankid-plugin
19
+ ```
20
+
21
+ ## Prerequisites
22
+
23
+ ### 1. BankID Certificate
24
+
25
+ You need a BankID certificate (PFX file) from your bank or the Swedish BankID organization:
26
+
27
+ - **Test Environment**: Use test certificates from [BankID Test Environment](https://www.bankid.com/utvecklare/test)
28
+ - **Production Environment**: Request a production certificate from your bank
29
+
30
+ ### 2. MongoDB Connection
31
+
32
+ The plugin requires MongoDB to store BankID sessions.
33
+
34
+ ## Setup
35
+
36
+ ### Step 1: Prepare Certificate
37
+
38
+ Convert your PFX certificate to base64:
39
+
40
+ ```bash
41
+ # Convert PFX to base64
42
+ cat certificate.pfx | base64 > certificate.txt
43
+ ```
44
+
45
+ Store the base64 string in your environment variables:
46
+
47
+ ```bash
48
+ BANKID_CERT_BASE64="MIIKZgIBAzCCCiw..."
49
+ BANKID_PASSPHRASE="your-certificate-passphrase"
50
+ ```
51
+
52
+ ### Step 2: Configure Plugin
53
+
54
+ **`index.ts`:**
55
+ ```typescript
56
+ import { FlinkApp } from "@flink-app/flink";
57
+ import { bankIdPlugin } from "@flink-app/bankid-plugin";
58
+ import { jwtAuthPlugin } from "@flink-app/jwt-auth-plugin";
59
+ import { genericAuthPlugin, User } from "@flink-app/generic-auth-plugin";
60
+ import { Ctx } from "./Ctx";
61
+
62
+ function start() {
63
+ const app = new FlinkApp<Ctx>({
64
+ name: "My App",
65
+ auth: jwtAuthPlugin({
66
+ secret: process.env.JWT_SECRET!,
67
+ getUser: async (tokenData) => {
68
+ const user = await app.ctx.repos.userRepo.findById(tokenData.userId);
69
+ if (!user) throw new Error("User not found");
70
+ return {
71
+ id: user._id,
72
+ username: user.username,
73
+ roles: user.roles,
74
+ };
75
+ },
76
+ rolePermissions: {
77
+ user: ["read", "write"],
78
+ },
79
+ }),
80
+ db: {
81
+ uri: process.env.MONGODB_URI!,
82
+ },
83
+ plugins: [
84
+ genericAuthPlugin({
85
+ repoName: "userRepo",
86
+ }),
87
+ bankIdPlugin({
88
+ pfxBase64: process.env.BANKID_CERT_BASE64!,
89
+ passphrase: process.env.BANKID_PASSPHRASE!,
90
+ production: false, // Set to true for production
91
+ onGetEndUserIp: async (req) => {
92
+ // Get IP from X-Forwarded-For header if behind proxy
93
+ const forwardedFor = req.headers["x-forwarded-for"];
94
+ if (forwardedFor) {
95
+ return Array.isArray(forwardedFor)
96
+ ? forwardedFor[0]
97
+ : forwardedFor.split(",")[0];
98
+ }
99
+ return req.ip || "127.0.0.1";
100
+ },
101
+ onAuthSuccess: async (userData, ip, payload) => {
102
+ // Find or create user based on personal number
103
+ let user = await app.ctx.repos.userRepo.findOne({
104
+ personalNumber: userData.user.personalNumber,
105
+ });
106
+
107
+ if (!user) {
108
+ // Create new user
109
+ const createResult = await app.ctx.plugins.genericAuthPlugin.createUser(
110
+ app.ctx.repos.userRepo,
111
+ app.ctx.auth,
112
+ userData.user.personalNumber,
113
+ "", // No password for BankID users
114
+ "bankid",
115
+ ["user"],
116
+ {
117
+ name: userData.user.name,
118
+ givenName: userData.user.givenName,
119
+ surname: userData.user.surname,
120
+ },
121
+ undefined,
122
+ undefined,
123
+ userData.user.personalNumber
124
+ );
125
+
126
+ if (createResult.status !== "success") {
127
+ throw new Error("Failed to create user");
128
+ }
129
+
130
+ user = await app.ctx.repos.userRepo.findById(createResult.user!._id);
131
+ }
132
+
133
+ // Create JWT token
134
+ const token = await app.ctx.auth.createToken(
135
+ { userId: user!._id, username: user!.username },
136
+ user!.roles
137
+ );
138
+
139
+ return {
140
+ user: {
141
+ _id: user!._id,
142
+ username: user!.username,
143
+ profile: user!.profile,
144
+ roles: user!.roles,
145
+ },
146
+ token,
147
+ };
148
+ },
149
+ onSignSuccess: async (userData, signature, payload) => {
150
+ // Handle signed document
151
+ console.log("Document signed by:", userData.user.name);
152
+ console.log("Signature:", signature.signature);
153
+
154
+ // Store signature in database or process document
155
+ // Implementation depends on your use case
156
+ },
157
+ }),
158
+ ],
159
+ });
160
+
161
+ app.start();
162
+ }
163
+
164
+ start();
165
+ ```
166
+
167
+ ## Configuration Options
168
+
169
+ ### `BankIdPluginOptions`
170
+
171
+ | Option | Type | Required | Default | Description |
172
+ |--------|------|----------|---------|-------------|
173
+ | `pfxBase64` | `string` | Yes | - | BankID certificate in base64 format |
174
+ | `passphrase` | `string` | Yes | - | Certificate passphrase |
175
+ | `production` | `boolean` | No | `false` | Use production BankID environment |
176
+ | `allowNoIp` | `boolean` | No | `false` | Allow requests without IP (uses 127.0.0.1) |
177
+ | `onGetEndUserIp` | `Function` | Yes | - | Function to extract client IP address |
178
+ | `onAuthSuccess` | `Function` | Yes | - | Callback when authentication succeeds |
179
+ | `onSignSuccess` | `Function` | No | - | Callback when document signing succeeds |
180
+ | `keepSessionsSec` | `number` | No | `86400` (24h) | How long to keep sessions in database |
181
+ | `bankIdSessionsCollectionName` | `string` | No | `"bankid_sessions"` | MongoDB collection name for sessions |
182
+ | `registerRoutes` | `boolean` | No | `true` | Register built-in HTTP endpoints |
183
+
184
+ ### Callback Functions
185
+
186
+ #### `onGetEndUserIp(req: FlinkRequest): Promise<string>`
187
+
188
+ Extract the end user's IP address from the request. Required by BankID for security.
189
+
190
+ ```typescript
191
+ onGetEndUserIp: async (req) => {
192
+ // Behind a proxy
193
+ const forwardedFor = req.headers["x-forwarded-for"];
194
+ if (forwardedFor) {
195
+ return Array.isArray(forwardedFor)
196
+ ? forwardedFor[0]
197
+ : forwardedFor.split(",")[0];
198
+ }
199
+
200
+ // Direct connection
201
+ return req.ip || "127.0.0.1";
202
+ }
203
+ ```
204
+
205
+ #### `onAuthSuccess(userData, ip?, payload?): Promise<AuthSuccessCallbackResponse>`
206
+
207
+ Called when BankID authentication is successful. Must return user object and JWT token.
208
+
209
+ ```typescript
210
+ interface AuthSuccessCallbackResponse {
211
+ user: any;
212
+ token: string;
213
+ }
214
+ ```
215
+
216
+ **Parameters:**
217
+ - `userData`: BankID user data including personal number and name
218
+ - `ip`: Client IP address (optional)
219
+ - `payload`: Custom payload passed during auth initiation (optional)
220
+
221
+ #### `onSignSuccess(userData, signature, payload?): Promise<void>`
222
+
223
+ Called when document signing is successful.
224
+
225
+ **Parameters:**
226
+ - `userData`: BankID user data
227
+ - `signature`: Signature data including signature string and OCSP response
228
+ - `payload`: Custom payload passed during sign initiation (optional)
229
+
230
+ ## Context API
231
+
232
+ The plugin exposes the following functions via `ctx.plugins.bankId`:
233
+
234
+ ### `auth(options?): Promise<AuthResponse>`
235
+
236
+ Initiate BankID authentication.
237
+
238
+ ```typescript
239
+ const result = await ctx.plugins.bankId.auth({
240
+ endUserIp: "192.168.1.1",
241
+ payload: { returnUrl: "/dashboard" }, // Optional custom data
242
+ });
243
+ ```
244
+
245
+ **Returns:**
246
+ ```typescript
247
+ interface AuthResponse {
248
+ orderRef: string; // BankID order reference
249
+ autoStartToken: string; // Token for Mobile BankID app
250
+ qr: string; // QR code data URL for QR code authentication
251
+ }
252
+ ```
253
+
254
+ ### `sign(options): Promise<SignResponse>`
255
+
256
+ Initiate document signing.
257
+
258
+ ```typescript
259
+ const result = await ctx.plugins.bankId.sign({
260
+ endUserIp: "192.168.1.1",
261
+ userVisibleData: "I approve this document",
262
+ userNonVisibleData: "Document ID: 12345", // Optional
263
+ payload: { documentId: "12345" },
264
+ });
265
+ ```
266
+
267
+ **Returns:**
268
+ ```typescript
269
+ interface SignResponse {
270
+ orderRef: string;
271
+ autoStartToken: string;
272
+ qr: string;
273
+ }
274
+ ```
275
+
276
+ ### `getAuthStatus(options): Promise<AuthStatusResponse>`
277
+
278
+ Check authentication status and get result when complete.
279
+
280
+ ```typescript
281
+ const result = await ctx.plugins.bankId.getAuthStatus({
282
+ orderRef: "abc123...",
283
+ });
284
+ ```
285
+
286
+ **Returns:**
287
+ ```typescript
288
+ interface AuthStatusResponse {
289
+ status: "pending" | "complete" | "failed";
290
+ hintCode?: string;
291
+ qr?: string; // Updated QR code if still pending
292
+ user?: any; // User data when complete
293
+ token?: string; // JWT token when complete
294
+ }
295
+ ```
296
+
297
+ ### `getSignStatus(options): Promise<SignStatusResponse>`
298
+
299
+ Check signing status and get result when complete.
300
+
301
+ ```typescript
302
+ const result = await ctx.plugins.bankId.getSignStatus({
303
+ orderRef: "abc123...",
304
+ });
305
+ ```
306
+
307
+ ### `cancelSession(options): Promise<CancelSessionResponse>`
308
+
309
+ Cancel an ongoing BankID session.
310
+
311
+ ```typescript
312
+ const result = await ctx.plugins.bankId.cancelSession({
313
+ orderRef: "abc123...",
314
+ });
315
+ ```
316
+
317
+ ## Built-in API Endpoints
318
+
319
+ When `registerRoutes: true` (default), the following endpoints are automatically available:
320
+
321
+ ### POST /bankid/auth
322
+
323
+ Initiate BankID authentication.
324
+
325
+ **Request:**
326
+ ```json
327
+ {
328
+ "payload": {
329
+ "returnUrl": "/dashboard"
330
+ }
331
+ }
332
+ ```
333
+
334
+ **Response:**
335
+ ```json
336
+ {
337
+ "data": {
338
+ "orderRef": "131daac9-16c6-4618-beb0-365768f37288",
339
+ "autoStartToken": "7c40b5c9-fa74-49cf-b98c-bfaf...",
340
+ "qr": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..."
341
+ }
342
+ }
343
+ ```
344
+
345
+ ### GET /bankid/auth?orderRef=xxx
346
+
347
+ Get authentication status.
348
+
349
+ **Query Parameters:**
350
+ - `orderRef`: The order reference from initiation
351
+
352
+ **Response (Pending):**
353
+ ```json
354
+ {
355
+ "data": {
356
+ "status": "pending",
357
+ "hintCode": "outstandingTransaction",
358
+ "qr": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..."
359
+ }
360
+ }
361
+ ```
362
+
363
+ **Response (Complete):**
364
+ ```json
365
+ {
366
+ "data": {
367
+ "status": "complete",
368
+ "user": {
369
+ "_id": "507f1f77bcf86cd799439011",
370
+ "username": "198001011234",
371
+ "profile": {
372
+ "name": "John Doe",
373
+ "givenName": "John",
374
+ "surname": "Doe"
375
+ },
376
+ "roles": ["user"]
377
+ },
378
+ "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
379
+ }
380
+ }
381
+ ```
382
+
383
+ **Response (Failed):**
384
+ ```json
385
+ {
386
+ "data": {
387
+ "status": "failed",
388
+ "hintCode": "userCancel"
389
+ }
390
+ }
391
+ ```
392
+
393
+ ### GET /bankid/sign?orderRef=xxx
394
+
395
+ Get signing status. Similar response structure to auth status.
396
+
397
+ ### DELETE /bankid/session/:orderRef
398
+
399
+ Cancel a BankID session.
400
+
401
+ **Response:**
402
+ ```json
403
+ {
404
+ "data": {
405
+ "status": "success"
406
+ }
407
+ }
408
+ ```
409
+
410
+ ## BankID Status Codes
411
+
412
+ ### Hint Codes (Pending)
413
+
414
+ - `outstandingTransaction` - User hasn't opened BankID app yet
415
+ - `noClient` - BankID app not installed
416
+ - `started` - BankID app has been started
417
+ - `userSign` - User is signing in BankID app
418
+
419
+ ### Hint Codes (Failed)
420
+
421
+ - `userCancel` - User cancelled the operation
422
+ - `expiredTransaction` - Session timed out
423
+ - `certificateErr` - Certificate error
424
+ - `startFailed` - Failed to start BankID app
425
+
426
+ ## Client Integration Examples
427
+
428
+ ### React Example with QR Code
429
+
430
+ ```typescript
431
+ import React, { useState, useEffect } from "react";
432
+
433
+ function BankIDLogin() {
434
+ const [orderRef, setOrderRef] = useState<string>();
435
+ const [qrCode, setQrCode] = useState<string>();
436
+ const [status, setStatus] = useState<string>("idle");
437
+
438
+ const initAuth = async () => {
439
+ const response = await fetch("/bankid/auth", {
440
+ method: "POST",
441
+ headers: { "Content-Type": "application/json" },
442
+ body: JSON.stringify({
443
+ payload: { returnUrl: "/dashboard" },
444
+ }),
445
+ });
446
+
447
+ const data = await response.json();
448
+ setOrderRef(data.data.orderRef);
449
+ setQrCode(data.data.qr);
450
+ setStatus("pending");
451
+ };
452
+
453
+ useEffect(() => {
454
+ if (!orderRef) return;
455
+
456
+ const interval = setInterval(async () => {
457
+ const response = await fetch(`/bankid/auth?orderRef=${orderRef}`);
458
+ const data = await response.json();
459
+
460
+ if (data.data.status === "complete") {
461
+ clearInterval(interval);
462
+ setStatus("complete");
463
+ // Store token and redirect
464
+ localStorage.setItem("token", data.data.token);
465
+ window.location.href = "/dashboard";
466
+ } else if (data.data.status === "failed") {
467
+ clearInterval(interval);
468
+ setStatus("failed");
469
+ } else {
470
+ // Update QR code if provided
471
+ if (data.data.qr) {
472
+ setQrCode(data.data.qr);
473
+ }
474
+ }
475
+ }, 2000); // Poll every 2 seconds
476
+
477
+ return () => clearInterval(interval);
478
+ }, [orderRef]);
479
+
480
+ return (
481
+ <div>
482
+ {status === "idle" && (
483
+ <button onClick={initAuth}>Login with BankID</button>
484
+ )}
485
+
486
+ {status === "pending" && (
487
+ <div>
488
+ <p>Scan QR code with BankID app:</p>
489
+ {qrCode && <img src={qrCode} alt="BankID QR Code" />}
490
+ <p>Or open BankID app on this device</p>
491
+ </div>
492
+ )}
493
+
494
+ {status === "failed" && (
495
+ <div>
496
+ <p>Authentication failed. Please try again.</p>
497
+ <button onClick={initAuth}>Retry</button>
498
+ </div>
499
+ )}
500
+ </div>
501
+ );
502
+ }
503
+ ```
504
+
505
+ ### Mobile BankID Deep Link
506
+
507
+ For mobile devices, use the `autoStartToken` to open the BankID app:
508
+
509
+ ```typescript
510
+ const response = await fetch("/bankid/auth", { method: "POST" });
511
+ const data = await response.json();
512
+
513
+ // iOS and Android
514
+ const bankIdUrl = `bankid:///?autostarttoken=${data.data.autoStartToken}&redirect=null`;
515
+ window.location.href = bankIdUrl;
516
+
517
+ // Then poll for status
518
+ ```
519
+
520
+ ## Document Signing
521
+
522
+ ### Sign a Document
523
+
524
+ ```typescript
525
+ // In your handler
526
+ const handler: Handler<Ctx, SignDocumentReq, SignDocumentRes> = async ({ ctx, req }) => {
527
+ const documentText = "I agree to the terms and conditions...";
528
+
529
+ const result = await ctx.plugins.bankId.sign({
530
+ endUserIp: await getClientIp(req),
531
+ userVisibleData: documentText,
532
+ userNonVisibleData: `Document ID: ${documentId}`,
533
+ payload: {
534
+ documentId,
535
+ userId: req.user.id,
536
+ },
537
+ });
538
+
539
+ return { data: result };
540
+ };
541
+ ```
542
+
543
+ ### Handle Signed Document
544
+
545
+ In your `onSignSuccess` callback:
546
+
547
+ ```typescript
548
+ onSignSuccess: async (userData, signature, payload) => {
549
+ const { documentId, userId } = payload;
550
+
551
+ // Store signature
552
+ await ctx.repos.signatureRepo.create({
553
+ documentId,
554
+ userId,
555
+ personalNumber: userData.user.personalNumber,
556
+ name: userData.user.name,
557
+ signature: signature.signature,
558
+ ocspResponse: signature.ocspResponse,
559
+ signedAt: new Date(),
560
+ });
561
+
562
+ // Update document status
563
+ await ctx.repos.documentRepo.update(documentId, {
564
+ status: "signed",
565
+ signedBy: userData.user.personalNumber,
566
+ });
567
+ }
568
+ ```
569
+
570
+ ## Session Management
571
+
572
+ The plugin automatically manages BankID sessions in MongoDB:
573
+
574
+ - Sessions are stored with automatic expiration (default: 24 hours)
575
+ - QR codes are automatically regenerated every second while pending
576
+ - Failed or completed sessions are cleaned up automatically
577
+
578
+ ### Custom Session Retention
579
+
580
+ ```typescript
581
+ bankIdPlugin({
582
+ // ...other options
583
+ keepSessionsSec: 3600, // Keep sessions for 1 hour
584
+ bankIdSessionsCollectionName: "my_bankid_sessions",
585
+ })
586
+ ```
587
+
588
+ ## Testing
589
+
590
+ ### Test Environment
591
+
592
+ Use BankID test certificates and test personal numbers:
593
+
594
+ ```typescript
595
+ bankIdPlugin({
596
+ production: false, // Test environment
597
+ pfxBase64: process.env.BANKID_TEST_CERT_BASE64!,
598
+ passphrase: process.env.BANKID_TEST_PASSPHRASE!,
599
+ // ...
600
+ })
601
+ ```
602
+
603
+ ### Test Personal Numbers
604
+
605
+ BankID provides test personal numbers for development:
606
+
607
+ - `198001011234` - Standard test user
608
+ - `198001021234` - Test user with multiple BankIDs
609
+
610
+ See [BankID Test Documentation](https://www.bankid.com/utvecklare/test) for complete list.
611
+
612
+ ## Security Best Practices
613
+
614
+ ### 1. Certificate Security
615
+
616
+ - Never commit certificates to version control
617
+ - Store certificates in secure environment variables or secrets management
618
+ - Use different certificates for test and production
619
+
620
+ ```bash
621
+ # .env
622
+ BANKID_CERT_BASE64=xxx
623
+ BANKID_PASSPHRASE=yyy
624
+ ```
625
+
626
+ ### 2. IP Address Validation
627
+
628
+ Always verify and log IP addresses:
629
+
630
+ ```typescript
631
+ onGetEndUserIp: async (req) => {
632
+ const ip = extractIp(req);
633
+
634
+ // Log for audit
635
+ logger.info(`BankID request from IP: ${ip}`);
636
+
637
+ // Validate IP format
638
+ if (!isValidIp(ip)) {
639
+ throw new Error("Invalid IP address");
640
+ }
641
+
642
+ return ip;
643
+ }
644
+ ```
645
+
646
+ ### 3. Rate Limiting
647
+
648
+ Implement rate limiting on BankID endpoints to prevent abuse:
649
+
650
+ ```typescript
651
+ import rateLimit from "express-rate-limit";
652
+
653
+ app.use("/bankid", rateLimit({
654
+ windowMs: 15 * 60 * 1000, // 15 minutes
655
+ max: 10, // 10 requests per window
656
+ }));
657
+ ```
658
+
659
+ ### 4. Session Validation
660
+
661
+ Validate session state before completing authentication:
662
+
663
+ ```typescript
664
+ onAuthSuccess: async (userData, ip, payload) => {
665
+ // Verify personal number format
666
+ if (!/^\d{12}$/.test(userData.user.personalNumber)) {
667
+ throw new Error("Invalid personal number");
668
+ }
669
+
670
+ // Additional validation...
671
+
672
+ return { user, token };
673
+ }
674
+ ```
675
+
676
+ ### 5. HTTPS Only
677
+
678
+ BankID requires HTTPS in production. Never use HTTP for BankID in production.
679
+
680
+ ## TypeScript Types
681
+
682
+ ```typescript
683
+ import {
684
+ BankIdPluginOptions,
685
+ BankIdUserInfo,
686
+ BankIdSignature,
687
+ AuthSuccessCallbackResponse,
688
+ AuthResponse,
689
+ SignResponse,
690
+ AuthStatusResponse,
691
+ SignStatusResponse,
692
+ BankIdSession,
693
+ } from "@flink-app/bankid-plugin";
694
+
695
+ // User info from BankID
696
+ interface BankIdUserInfo {
697
+ personalNumber: string;
698
+ name: string;
699
+ givenName: string;
700
+ surname: string;
701
+ }
702
+
703
+ // Signature data
704
+ interface BankIdSignature {
705
+ signature: string;
706
+ ocspResponse: string;
707
+ }
708
+
709
+ // Auth success response
710
+ interface AuthSuccessCallbackResponse {
711
+ user: any;
712
+ token: string;
713
+ }
714
+ ```
715
+
716
+ ## Troubleshooting
717
+
718
+ ### QR Code Not Updating
719
+
720
+ **Issue:** QR code becomes stale after 1 second
721
+
722
+ **Solution:** The plugin automatically generates new QR codes. Ensure you're polling the status endpoint regularly (every 1-2 seconds).
723
+
724
+ ### Certificate Error
725
+
726
+ **Issue:** `Error: unable to get local issuer certificate`
727
+
728
+ **Solution:**
729
+ - Verify certificate format is correct
730
+ - Check passphrase is correct
731
+ - Ensure certificate is valid for the environment (test/prod)
732
+
733
+ ### IP Address Issues
734
+
735
+ **Issue:** `Failed to obtain endUserIp`
736
+
737
+ **Solution:**
738
+ ```typescript
739
+ // For development only
740
+ bankIdPlugin({
741
+ allowNoIp: true, // Only for local development
742
+ onGetEndUserIp: async (req) => {
743
+ return req.ip || "127.0.0.1";
744
+ },
745
+ })
746
+ ```
747
+
748
+ ### User Cancel
749
+
750
+ **Issue:** Users frequently cancel
751
+
752
+ **Solution:** Improve UX:
753
+ - Show clear instructions
754
+ - Display QR code prominently
755
+ - Provide mobile deep link option
756
+ - Show timeout countdown
757
+
758
+ ## Production Checklist
759
+
760
+ - [ ] Use production BankID certificate
761
+ - [ ] Set `production: true`
762
+ - [ ] Configure HTTPS
763
+ - [ ] Implement rate limiting
764
+ - [ ] Set up monitoring and logging
765
+ - [ ] Configure proper IP extraction behind proxy
766
+ - [ ] Set appropriate session retention
767
+ - [ ] Test certificate expiration handling
768
+ - [ ] Configure error alerting
769
+ - [ ] Validate personal number format
770
+
771
+ ## Complete Example
772
+
773
+ See the configuration example at the beginning of this document for a complete working example integrating BankID with the Generic Auth Plugin.
774
+
775
+ ## License
776
+
777
+ MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flink-app/bankid-plugin",
3
- "version": "0.12.1-alpha.25",
3
+ "version": "0.12.1-alpha.33",
4
4
  "description": "Flink plugin for Swedish BankID authentication and document signing",
5
5
  "scripts": {
6
6
  "test": "node --preserve-symlinks -r ts-node/register -- node_modules/jasmine/bin/jasmine --config=./spec/support/jasmine.json",
@@ -32,5 +32,5 @@
32
32
  "tsc-watch": "^4.2.9",
33
33
  "typescript": "5.4.5"
34
34
  },
35
- "gitHead": "44a1bf5bb2b2c7d18e4cecc06da626700639f82a"
35
+ "gitHead": "eec3a22c21db0e7fec84190bf7a74c1e430e5ec4"
36
36
  }