Skip to content
This repository has been archived by the owner on Feb 5, 2023. It is now read-only.

Commit

Permalink
Version 3.0
Browse files Browse the repository at this point in the history
All change. Again.
  • Loading branch information
russss committed Sep 24, 2018
1 parent 3e0be1f commit 3977e6b
Show file tree
Hide file tree
Showing 13 changed files with 1,366 additions and 887 deletions.
5 changes: 2 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
barclays-pinsentry
config.js
*.ofx
*.png
.DS_Store
export/
export/
node_modules
899 changes: 617 additions & 282 deletions LICENSE

Large diffs are not rendered by default.

102 changes: 34 additions & 68 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,85 +1,59 @@
Barclayscrape
=============
Code to programmatically manipulate Barclays online banking.
At the moment it supports:
Barclayscrape v3.0
==================
Code to programmatically manipulate Barclays online banking using
[Puppeteer](https://github.com/GoogleChrome/puppeteer).

* Logging in
* Fetching a list of accounts
* `get_ofx.js` uses the export function to fetch an .ofx
file for each of your bank accounts (assuming there are
transactions available) into the `export` directory
* `get_combined_ofx.js` fetches a combined .ofx file of all
available transactions for all accounts into `all.ofx` in the `export`
directory.
* `export_csv.js` parses HTML statements into a CSV
file for each of your bank accounts and saves them into the `export`
directory.
Installation
------------

Prerequisites
-------------
Barclayscrape requires node.js version 10 or above which can be
installed through your OS's package manager or Homebrew. Once Node
is installed, barclayscrape can be installed on your system using:

* [Phantomjs](http://phantomjs.org/) 1.9.8
* [Casperjs](http://casperjs.readthedocs.org/) 1.1.4
$ npm install -g barclayscrape

Configuration
-------------
Copy `config.js.example` to `config.js`, and fill in:

* `surname`
* `membership_number`

If you use the Mobile PINSentry app, that's it, otherwise you'll
be using the PINSentry card reader and will also need:

* `card_digits` (last digits of your authentication card)

If you know your account numbers, want to limit the exported files
and give the exported files aliases for readability, you can set:

* `accounts` an object e.g:

```
accounts: {
'gbp': '12345678901234',
'eur': '12345678901234',
'usd': '12345678901234'
}
```

Otherwise, you can leave it null and all accounts will be exported
The `barclayscrape` executable will be installed in your path.

Usage
-----
```
Options:
If you use the Mobile PINSentry app, no arguments are needed
-V, --version output the version number
--otp [pin] Pinsentry PIN code
--motp [pin] Mobile Pinsentry PIN code
--no-headless Show browser window when interacting
-h, --help output usage information
$ ./get_ofx.js # or export_csv.js
Commands:
To log in via the PINSentry card reader, usage is like so:
list List all available accounts
get_ofx <out_path> Fetch .ofx files for all accounts into out_path
config Set up login details
```

$ ./get_ofx.js --otp=<otp>
To start, `barclayscrape config` will ask you for your basic login
details. You can test that the login works by running:

Where `otp` is your PINSentry one-time password.
$ barclayscrape --otp <pin> list

Finally, you can log non-interactively, by also passing a pre-configured
memorable password and 5 digit passcode.
Where `<pin>` is the eight-digit code generated by your PINSentry device.
If you're using the mobile PINSentry facility then use `--motp <pin>`
instead of `--otp <pin>`.

This is supported if your Barclays account was set up to allow
non-PINSentry access, but it is unclear whether Barclays allow existing
account holders to opt into this any more.
To download bank statements in OFX format, you can run:

Logging in this way will still require PINSentry to transfer funds, but
you should take measures to secure the script if you choose to use this method.
$ barclayscrape --otp <pin> get_ofx ./output_dir/

$ ./get_ofx.js --mcode=<memorable password> --pcode=<5 digit passcode>
This will download one file per account and place them in `./output_dir/`.

Automating PINSentry Generation
-------------------------------

Typing in your OTP every time is a pain, but there are ways of
automating the process entirely using a USB smartcard reader.

**NOTE:** This somewhat defeats the purpose of two-factor
**SECURITY NOTE:** This somewhat defeats the purpose of two-factor
authentication, so please do not implement this unless you are confident
in your ability to adequately secure the machine running it. It is your
money at risk.
Expand All @@ -88,16 +62,8 @@ The [python-emv](https://github.com/russss/python-emv) package contains
a tool to generate a one-time password on the command line. It can be
hooked up to barclayscrape like so:

$ ./get_ofx.js --otp=`emvtool -p <PIN> cap`
$ barclayscrape --otp=`emvtool -p <PIN> cap` get_ofx ./output/

Please be aware that if you're putting this command into cron, any error
emails will include your PIN in the subject line. It's worth using a small
shell script to prevent this.

Security Notes
--------------

* Do not be tempted to set the PhantomJS option to ignore certificate errors.
This leaves **your banking data** subject to man-in-the-middle attacks.
* If you're changing this code please don't leave debugHTML() calls in it
when submitting pull requests. This can leak privileged data into cron emails.
50 changes: 50 additions & 0 deletions account.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
const u = require('./utils.js');

// Class for dealing with the Barclays account page.
module.exports = class Account {
constructor(session, number, idx) {
this.session = session;
this.page = session.page;
this.number = number;
this.idx = idx;
}

async select() {
// Switch the page to this account.
// Call `await this.session.home()` to reset state when you're done.
console.log('Selecting account ' + this.number);
await this.page.$eval('#a' + this.idx + ' #showStatements', el => el.click());
// waitForNavigation seems to stall indefinitely here (?!) so we don't use u.click
await u.wait(this.page, '.transaction-list-container-header');
}

async statementOFX() {
// Return an OFX-formatted string of the most recent account statement.
await this.select();
if (!(await this.page.$('a.export'))) {
console.log(
'No export option (probably no transactions) for account ' +
this.number,
);
return null;
}

// Locate the download links
const dl_el = await this.page.$x("//a[text()[contains(., ' Money 2001')]]");

// Fetch the href of this link in the context of the page.
const ofx = await this.page.evaluate(el => {
return fetch(el.href, {method: 'GET', credentials: 'include'}).then(r =>
r.text(),
);
}, dl_el[0]);
console.log('Fetched OFX for account ' + this.number);

await this.session.home();
return ofx;
}

toString() {
return '[Account ' + this.number + ']';
}
};
Loading

0 comments on commit 3977e6b

Please sign in to comment.