Skip to content

Commit 0510774

Browse files
authored
Merge pull request #19 from DSorlov/dev
v1.0.0 final
2 parents fc143b7 + ff409d3 commit 0510774

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+2662
-3672
lines changed

Diff for: LICENSE

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2020 Daniel Sörlöv
3+
Copyright (c) 2020-2021 Daniel Sörlöv
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

Diff for: certs/frejaeid_prod_onjnxVgI3oUzWQMLciD7sQZ4mqM.jwt

-28
This file was deleted.

Diff for: certs/frejaeid_test_HwMHK_gb3_iuNF1advMtlG0-fUs.jwt

-24
This file was deleted.

Diff for: changelog.md

+16-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,23 @@
1-
# Changelog for eid-provider
1+
# Changelog for eid
22

33
The format is based on [Keep a Changelog][keep-a-changelog]
44
<!-- and this project adheres to [Semantic Versioning][semantic-versioning]. -->
55

66
## [Unreleased]
7-
- Nothing right now
7+
8+
## [1.0.0] (2021-06-05)
9+
10+
### Breaking changes
11+
- Package name changed from eid-provider to eid
12+
- Major rehauling of architechture and internal file locations
13+
- Completely new interface for all operations
14+
- Compability calls implemented to make transition easier to eid
15+
- frejaeid and frejaorgid have been merged into one client frejaeid
16+
- ftbankid and ftfrejaeid have been merged into new client grp2
17+
- gbankid, bfrejaeid and ghsaid have been merged into new client grandid
18+
- Implemented new client signicat
19+
- Reduced external dependencies, now only jsonwebtoken is required.
20+
- Implemented support for QR-codes (API v5.1) in bankid
821

922
## [0.2.1] (2021-04-08)
1023

@@ -104,6 +117,7 @@ The format is based on [Keep a Changelog][keep-a-changelog]
104117

105118
[keep-a-changelog]: http://keepachangelog.com/en/1.0.0/
106119
[Unreleased]: https://github.com/DSorlov/eid-provider/compare/master...dev
120+
[1.0.0]: https://github.com/DSorlov/eid-provider/releases/tag/v1.0.0
107121
[0.2.1]: https://github.com/DSorlov/eid-provider/releases/tag/v0.2.1
108122
[0.2.0]: https://github.com/DSorlov/eid-provider/releases/tag/v0.2.0
109123
[0.1.9]: https://github.com/DSorlov/eid-provider/releases/tag/v0.1.9
File renamed without changes.
File renamed without changes.
File renamed without changes.

Diff for: clients/bankid/client.js

+214
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
const BaseClient = require('../baseclient.js');
2+
var crypto = require('crypto');
3+
4+
class BankID extends BaseClient {
5+
6+
7+
constructor(settings) {
8+
super(settings);
9+
this.settings = settings || {};
10+
11+
this.clientInfo = {
12+
name: "BankID",
13+
version: "20210406",
14+
author: "Daniel Sörlöv <[email protected]>",
15+
url: "https://github.com/DSorlov/eid-provider",
16+
methods: ['auth','sign']
17+
};
18+
19+
this._customAgent({
20+
pfx: this.settings.client_cert,
21+
passphrase: this.settings.password,
22+
ca: this.settings.ca_cert
23+
});
24+
25+
};
26+
27+
async pollRequest(data) {
28+
if (typeof data !== 'object') return this._createErrorMessage('internal_error','Supplied argument is not a class');
29+
if (!data.id || typeof data.id !== 'string') return this._createErrorMessage('internal_error','Id argument must be string');
30+
31+
var postData = {
32+
orderRef: data.id
33+
};
34+
var result = await this._httpRequest(`${this.settings.endpoint}/collect`,{},JSON.stringify(postData));
35+
36+
if (result.statusCode===599) {
37+
return this._createErrorMessage('internal_error',result.statusMessage);
38+
} else if (result.statusCode===200) {
39+
40+
if (result.json.hintCode) {
41+
switch(result.json.hintCode) {
42+
case "expiredTransaction":
43+
return this._createErrorMessage('expired_transaction');
44+
case "outstandingTransaction":
45+
return this._createPendingMessage('notdelivered');
46+
case "userSign":
47+
return this._createPendingMessage('user_in_app');
48+
case "noClient":
49+
return this._createPendingMessage('delivered');
50+
case "userCancel":
51+
return this._createErrorMessage('cancelled_by_user');
52+
case "cancelled":
53+
return this._createErrorMessage('cancelled_by_idp');
54+
default:
55+
return this._createErrorMessage('api_error', `Unknwon error '${result.json.hintCode}' was received`);
56+
}
57+
} else {
58+
59+
if (result.json.status==="complete") {
60+
61+
return this._createCompletionMessage(
62+
result.json.completionData.user.personalNumber,
63+
result.json.completionData.user.givenName,
64+
result.json.completionData.user.surname,
65+
result.json.completionData.user.name, {
66+
signature: result.json.completionData.signature,
67+
ocspResponse: result.json.completionData.ocspResponse
68+
});
69+
70+
} else {
71+
return this._createErrorMessage('communication_error', result.data);
72+
}
73+
74+
}
75+
76+
} else {
77+
if (result.json.errorCode) {
78+
switch(result.json.errorCode) {
79+
case 'invalidParameters':
80+
return this._createErrorMessage('request_id_invalid');
81+
default:
82+
return this._createErrorMessage('api_error', `Unknwon error '${result.json.errorCode}' was received`);
83+
}
84+
} else {
85+
return this._createErrorMessage('communication_error',result.statusMessage);
86+
}
87+
}
88+
89+
}
90+
91+
async cancelRequest(data) {
92+
if (typeof data !== 'object') return this._createErrorMessage('internal_error','Supplied argument is not a class');
93+
if (!data.id || typeof data.id !== 'string') return this._createErrorMessage('internal_error','Id argument must be string');
94+
95+
var postData = {
96+
orderRef: data.id
97+
};
98+
var result = await this._httpRequest(`${this.settings.endpoint}/cancel`,{},JSON.stringify(postData));
99+
100+
if (result.statusCode===599) {
101+
return this._createErrorMessage('internal_error',result.statusMessage);
102+
} else if (result.statusCode===200) {
103+
return this._createSuccessMessage();
104+
} else {
105+
return this._createErrorMessage('communication_error',result.statusMessage);
106+
}
107+
108+
}
109+
110+
createQRCodeString(data) {
111+
if (typeof data !== 'object') return this._createErrorMessage('internal_error','Supplied argument is not a class');
112+
if (!data.qrStartSecret||!data.qrStartToken||!data.qrAuthTime) this._createErrorMessage('internal_error','Needed attributes not supplied');
113+
var initTime = (new Date(data.qrAuthTime)).getTime();
114+
var currTime = (new Date()).getTime();
115+
var timeDiff = Math.floor((currTime - initTime) / 1000);
116+
var hash = crypto.createHmac('SHA256', data.qrStartSecret).update(timeDiff.toString()).digest("hex");
117+
return `bankid.${data.qrStartToken}.${timeDiff}.${hash}`;
118+
}
119+
120+
async initRequest(data) {
121+
if (typeof data !== 'object') return this._createErrorMessage('internal_error','Supplied argument is not a class');
122+
if (!data.id) return this._createErrorMessage('internal_error','Id argument must be string');
123+
var postData = '';
124+
var endpointUri = '';
125+
126+
if (data.text) {
127+
endpointUri = 'sign';
128+
postData = {
129+
endUserIp: data.endUserIp ? data.endUserUp : '127.0.0.1',
130+
userVisibleData: Buffer.from(data.text).toString('base64'),
131+
requirement: {
132+
allowFingerprint: data.allowFingerprint ? data.allowFingerprint : this.settings.allowFingerprint
133+
}};
134+
} else {
135+
endpointUri = 'auth';
136+
postData = {
137+
endUserIp: data.endUserIp ? data.endUserUp : '127.0.0.1',
138+
requirement: {
139+
allowFingerprint: data.allowFingerprint ? data.allowFingerprint : this.settings.allowFingerprint
140+
}};
141+
}
142+
143+
var personalId = this._unPack(data.id);
144+
if (personalId) postData.personalNumber = personalId;
145+
146+
var result = await this._httpRequest(`${this.settings.endpoint}/${endpointUri}`,{},JSON.stringify(postData));
147+
148+
if (result.statusCode===599) {
149+
150+
return this._createErrorMessage('internal_error',result.statusMessage);
151+
152+
} else if (result.statusCode===200) {
153+
154+
if (result.json.qrStartSecret) {
155+
return this._createInitializationMessage(result.json.orderRef, {
156+
autostart_token: result.json.autoStartToken,
157+
autostart_url: "bankid:///?autostarttoken="+result.json.autoStartToken+"&redirect=null",
158+
qrStartSecret: result.json.qrStartSecret,
159+
qrStartToken: result.json.qrStartToken,
160+
qrAuthTime: Date(),
161+
qrCodeString: createQRCodeString
162+
});
163+
} else {
164+
return this._createInitializationMessage(result.json.orderRef, {
165+
autostart_token: result.json.autoStartToken,
166+
autostart_url: "bankid:///?autostarttoken="+result.json.autoStartToken+"&redirect=null"
167+
});
168+
}
169+
170+
171+
} else {
172+
173+
if (result.json.errorCode) {
174+
switch(result.json.errorCode) {
175+
case 'invalidParameters':
176+
if (result.json.details==='Incorrect personalNumber') {
177+
return this._createErrorMessage('request_ssn_invalid');
178+
} else if (result.json.details==='Invalid userVisibleData') {
179+
return this._createErrorMessage('request_text_invalid');
180+
} else {
181+
return this._createErrorMessage('api_error', `Unknwon parameter error '${result.json.details}' was received`);
182+
}
183+
case 'alreadyInProgress':
184+
return this._createErrorMessage('already_in_progress');
185+
default:
186+
return this._createErrorMessage('api_error', `Unknwon error '${result.json.errorCode}' was received`);
187+
}
188+
} else {
189+
return this._createErrorMessage('communication_error',result.statusMessage);
190+
}
191+
192+
};
193+
194+
}
195+
196+
// Supporting function to take care of any and all types of id that could be sent into bankid
197+
_unPack(data) {
198+
if (data==="") return "";
199+
if (data==="INFERRED") return "";
200+
201+
if (typeof data === 'string') {
202+
return data;
203+
} else {
204+
if (data.ssn) {
205+
return data.ssn;
206+
} else {
207+
return data.toString();
208+
}
209+
}
210+
}
211+
212+
}
213+
214+
module.exports = BankID;

Diff for: clients/bankid/readme.md

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
## Swedish BankID (BankID)
2+
3+
Client for direct API communication with BankID (Finansiell ID-Teknik AB).
4+
5+
6+
7+
| Information | |
8+
| --- | --- |
9+
| Version | 20210406 |
10+
| Status | Built-in |
11+
| Author | Daniel Sörlöv <[email protected]> |
12+
| Client URL | https://github.com/DSorlov/eid-provider |
13+
14+
### Feature Table
15+
16+
| Feature | Supported |
17+
| --- | --- |
18+
| Authentication | :heavy_check_mark: |
19+
| Signatures | :heavy_check_mark: |
20+
21+
### Configuration Factory
22+
23+
Supports configuration factory using attribute `enviroment` to specify either `production` or `testing`.
24+
25+
```javascript
26+
var config = eid.configFactory({
27+
clientType: 'bankid',
28+
enviroment: 'testing'
29+
});
30+
```
31+
32+
### Configuration Object
33+
34+
Use the Configuration Factory to get a pre-populated object
35+
36+
```javascript
37+
var config = {
38+
// Client type (must be bankid to use this client)
39+
clientType: 'bankid',
40+
// The base URI to call the Rest-API
41+
endpoint: 'https://appapi2.test.bankid.com/rp/v5',
42+
// The PFX file content to use
43+
client_cert: '...',
44+
// The password for the PFX
45+
password: 'test',
46+
// The CA public cert for SSL communications
47+
ca_cert: '...',
48+
// Allow usage of fingerprint to sign in app for end-users
49+
allowFingerprint: true
50+
};
51+
```
52+
53+
### Extension Methods
54+
55+
The `doRequest` and `initRequest` accepts additional parameter `endUserIP` which can be set to the end user ip / remote requester ip. If not supplied it will be replaced by '127.0.0.1' as in earlier versions. Also accept `allowFingerprint` as boolean to specify if fingerprint auth is allowed in the app or not, if not specified default value from config will be used.
56+
57+
If `id` is not supplied to `doRequest` and `initRequest` the request will start and the properties `qrStartSecret`,`qrStartToken`,`qrAuthTime` will be returned as extra attributes for use with QR-code logins. Also the `qrCodeString` is populated with an initial calculation for the request.
58+
59+
**Extension methods**
60+
61+
* `createQRCodeString({qrStartSecret,qrStartToken,qrAuthTime})`<br/>Returns a correctly formatted QR-code for starting BankID app. The paramets are obtained when starting a authentication request without a id. It then returns `qrStartSecret`,`qrStartToken`,`qrAuthTime` as extra attributes. This method must be polled every 5 seconds at the most to obtain a new code when using QR-login.

0 commit comments

Comments
 (0)