-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #19 from DSorlov/dev
v1.0.0 final
- Loading branch information
Showing
67 changed files
with
2,662 additions
and
3,672 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,214 @@ | ||
const BaseClient = require('../baseclient.js'); | ||
var crypto = require('crypto'); | ||
|
||
class BankID extends BaseClient { | ||
|
||
|
||
constructor(settings) { | ||
super(settings); | ||
this.settings = settings || {}; | ||
|
||
this.clientInfo = { | ||
name: "BankID", | ||
version: "20210406", | ||
author: "Daniel Sörlöv <[email protected]>", | ||
url: "https://github.com/DSorlov/eid-provider", | ||
methods: ['auth','sign'] | ||
}; | ||
|
||
this._customAgent({ | ||
pfx: this.settings.client_cert, | ||
passphrase: this.settings.password, | ||
ca: this.settings.ca_cert | ||
}); | ||
|
||
}; | ||
|
||
async pollRequest(data) { | ||
if (typeof data !== 'object') return this._createErrorMessage('internal_error','Supplied argument is not a class'); | ||
if (!data.id || typeof data.id !== 'string') return this._createErrorMessage('internal_error','Id argument must be string'); | ||
|
||
var postData = { | ||
orderRef: data.id | ||
}; | ||
var result = await this._httpRequest(`${this.settings.endpoint}/collect`,{},JSON.stringify(postData)); | ||
|
||
if (result.statusCode===599) { | ||
return this._createErrorMessage('internal_error',result.statusMessage); | ||
} else if (result.statusCode===200) { | ||
|
||
if (result.json.hintCode) { | ||
switch(result.json.hintCode) { | ||
case "expiredTransaction": | ||
return this._createErrorMessage('expired_transaction'); | ||
case "outstandingTransaction": | ||
return this._createPendingMessage('notdelivered'); | ||
case "userSign": | ||
return this._createPendingMessage('user_in_app'); | ||
case "noClient": | ||
return this._createPendingMessage('delivered'); | ||
case "userCancel": | ||
return this._createErrorMessage('cancelled_by_user'); | ||
case "cancelled": | ||
return this._createErrorMessage('cancelled_by_idp'); | ||
default: | ||
return this._createErrorMessage('api_error', `Unknwon error '${result.json.hintCode}' was received`); | ||
} | ||
} else { | ||
|
||
if (result.json.status==="complete") { | ||
|
||
return this._createCompletionMessage( | ||
result.json.completionData.user.personalNumber, | ||
result.json.completionData.user.givenName, | ||
result.json.completionData.user.surname, | ||
result.json.completionData.user.name, { | ||
signature: result.json.completionData.signature, | ||
ocspResponse: result.json.completionData.ocspResponse | ||
}); | ||
|
||
} else { | ||
return this._createErrorMessage('communication_error', result.data); | ||
} | ||
|
||
} | ||
|
||
} else { | ||
if (result.json.errorCode) { | ||
switch(result.json.errorCode) { | ||
case 'invalidParameters': | ||
return this._createErrorMessage('request_id_invalid'); | ||
default: | ||
return this._createErrorMessage('api_error', `Unknwon error '${result.json.errorCode}' was received`); | ||
} | ||
} else { | ||
return this._createErrorMessage('communication_error',result.statusMessage); | ||
} | ||
} | ||
|
||
} | ||
|
||
async cancelRequest(data) { | ||
if (typeof data !== 'object') return this._createErrorMessage('internal_error','Supplied argument is not a class'); | ||
if (!data.id || typeof data.id !== 'string') return this._createErrorMessage('internal_error','Id argument must be string'); | ||
|
||
var postData = { | ||
orderRef: data.id | ||
}; | ||
var result = await this._httpRequest(`${this.settings.endpoint}/cancel`,{},JSON.stringify(postData)); | ||
|
||
if (result.statusCode===599) { | ||
return this._createErrorMessage('internal_error',result.statusMessage); | ||
} else if (result.statusCode===200) { | ||
return this._createSuccessMessage(); | ||
} else { | ||
return this._createErrorMessage('communication_error',result.statusMessage); | ||
} | ||
|
||
} | ||
|
||
createQRCodeString(data) { | ||
if (typeof data !== 'object') return this._createErrorMessage('internal_error','Supplied argument is not a class'); | ||
if (!data.qrStartSecret||!data.qrStartToken||!data.qrAuthTime) this._createErrorMessage('internal_error','Needed attributes not supplied'); | ||
var initTime = (new Date(data.qrAuthTime)).getTime(); | ||
var currTime = (new Date()).getTime(); | ||
var timeDiff = Math.floor((currTime - initTime) / 1000); | ||
var hash = crypto.createHmac('SHA256', data.qrStartSecret).update(timeDiff.toString()).digest("hex"); | ||
return `bankid.${data.qrStartToken}.${timeDiff}.${hash}`; | ||
} | ||
|
||
async initRequest(data) { | ||
if (typeof data !== 'object') return this._createErrorMessage('internal_error','Supplied argument is not a class'); | ||
if (!data.id) return this._createErrorMessage('internal_error','Id argument must be string'); | ||
var postData = ''; | ||
var endpointUri = ''; | ||
|
||
if (data.text) { | ||
endpointUri = 'sign'; | ||
postData = { | ||
endUserIp: data.endUserIp ? data.endUserUp : '127.0.0.1', | ||
userVisibleData: Buffer.from(data.text).toString('base64'), | ||
requirement: { | ||
allowFingerprint: data.allowFingerprint ? data.allowFingerprint : this.settings.allowFingerprint | ||
}}; | ||
} else { | ||
endpointUri = 'auth'; | ||
postData = { | ||
endUserIp: data.endUserIp ? data.endUserUp : '127.0.0.1', | ||
requirement: { | ||
allowFingerprint: data.allowFingerprint ? data.allowFingerprint : this.settings.allowFingerprint | ||
}}; | ||
} | ||
|
||
var personalId = this._unPack(data.id); | ||
if (personalId) postData.personalNumber = personalId; | ||
|
||
var result = await this._httpRequest(`${this.settings.endpoint}/${endpointUri}`,{},JSON.stringify(postData)); | ||
|
||
if (result.statusCode===599) { | ||
|
||
return this._createErrorMessage('internal_error',result.statusMessage); | ||
|
||
} else if (result.statusCode===200) { | ||
|
||
if (result.json.qrStartSecret) { | ||
return this._createInitializationMessage(result.json.orderRef, { | ||
autostart_token: result.json.autoStartToken, | ||
autostart_url: "bankid:///?autostarttoken="+result.json.autoStartToken+"&redirect=null", | ||
qrStartSecret: result.json.qrStartSecret, | ||
qrStartToken: result.json.qrStartToken, | ||
qrAuthTime: Date(), | ||
qrCodeString: createQRCodeString | ||
}); | ||
} else { | ||
return this._createInitializationMessage(result.json.orderRef, { | ||
autostart_token: result.json.autoStartToken, | ||
autostart_url: "bankid:///?autostarttoken="+result.json.autoStartToken+"&redirect=null" | ||
}); | ||
} | ||
|
||
|
||
} else { | ||
|
||
if (result.json.errorCode) { | ||
switch(result.json.errorCode) { | ||
case 'invalidParameters': | ||
if (result.json.details==='Incorrect personalNumber') { | ||
return this._createErrorMessage('request_ssn_invalid'); | ||
} else if (result.json.details==='Invalid userVisibleData') { | ||
return this._createErrorMessage('request_text_invalid'); | ||
} else { | ||
return this._createErrorMessage('api_error', `Unknwon parameter error '${result.json.details}' was received`); | ||
} | ||
case 'alreadyInProgress': | ||
return this._createErrorMessage('already_in_progress'); | ||
default: | ||
return this._createErrorMessage('api_error', `Unknwon error '${result.json.errorCode}' was received`); | ||
} | ||
} else { | ||
return this._createErrorMessage('communication_error',result.statusMessage); | ||
} | ||
|
||
}; | ||
|
||
} | ||
|
||
// Supporting function to take care of any and all types of id that could be sent into bankid | ||
_unPack(data) { | ||
if (data==="") return ""; | ||
if (data==="INFERRED") return ""; | ||
|
||
if (typeof data === 'string') { | ||
return data; | ||
} else { | ||
if (data.ssn) { | ||
return data.ssn; | ||
} else { | ||
return data.toString(); | ||
} | ||
} | ||
} | ||
|
||
} | ||
|
||
module.exports = BankID; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
## Swedish BankID (BankID) | ||
|
||
Client for direct API communication with BankID (Finansiell ID-Teknik AB). | ||
|
||
|
||
|
||
| Information | | | ||
| --- | --- | | ||
| Version | 20210406 | | ||
| Status | Built-in | | ||
| Author | Daniel Sörlöv <[email protected]> | | ||
| Client URL | https://github.com/DSorlov/eid-provider | | ||
|
||
### Feature Table | ||
|
||
| Feature | Supported | | ||
| --- | --- | | ||
| Authentication | :heavy_check_mark: | | ||
| Signatures | :heavy_check_mark: | | ||
|
||
### Configuration Factory | ||
|
||
Supports configuration factory using attribute `enviroment` to specify either `production` or `testing`. | ||
|
||
```javascript | ||
var config = eid.configFactory({ | ||
clientType: 'bankid', | ||
enviroment: 'testing' | ||
}); | ||
``` | ||
|
||
### Configuration Object | ||
|
||
Use the Configuration Factory to get a pre-populated object | ||
|
||
```javascript | ||
var config = { | ||
// Client type (must be bankid to use this client) | ||
clientType: 'bankid', | ||
// The base URI to call the Rest-API | ||
endpoint: 'https://appapi2.test.bankid.com/rp/v5', | ||
// The PFX file content to use | ||
client_cert: '...', | ||
// The password for the PFX | ||
password: 'test', | ||
// The CA public cert for SSL communications | ||
ca_cert: '...', | ||
// Allow usage of fingerprint to sign in app for end-users | ||
allowFingerprint: true | ||
}; | ||
``` | ||
|
||
### Extension Methods | ||
|
||
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. | ||
|
||
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. | ||
|
||
**Extension methods** | ||
|
||
* `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. |
Oops, something went wrong.