Skip to content

Commit

Permalink
Implement a window for authentication
Browse files Browse the repository at this point in the history
  • Loading branch information
tsuwatch committed Dec 24, 2016
1 parent 5a14ca6 commit b9979ef
Show file tree
Hide file tree
Showing 16 changed files with 224 additions and 29 deletions.
1 change: 1 addition & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"presets": [
"es2015",
"stage-0",
"react"
]
}
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"lint": "./node_modules/.bin/eslint ./src",
"electron-rebuild": "./node_modules/.bin/electron-rebuild sqlite3 keytar",
"build": "npm run build:main && npm run build:renderer",
"build:main": "cross-env NODE_ENV=production node -r babel-register ./node_modules/.bin/webpack --config webpack.config.electron.js --progress --profile --colors",
"build:renderer": "cross-env NODE_ENV=production node -r babel-register ./node_modules/.bin/webpack --config webpack.config.production.js --progress --profile --colors",
Expand All @@ -25,6 +26,8 @@
},
"homepage": "https://github.com/tsuwatch/nicomentron#readme",
"dependencies": {
"electron-window-state": "^4.0.0",
"keytar": "^3.0.2",
"nicolive": "0.0.4",
"react": "^15.3.2",
"react-dom": "^15.3.2",
Expand All @@ -37,10 +40,11 @@
"babel-loader": "^6.2.7",
"babel-preset-es2015": "^6.18.0",
"babel-preset-react": "^6.16.0",
"babel-preset-stage-0": "^6.16.0",
"babel-register": "^6.18.0",
"cross-env": "^3.1.3",
"electron-prebuilt": "^1.3.8",
"electron-rebuild": "^1.3.0",
"electron-rebuild": "^1.4.0",
"eslint": "^3.9.1",
"eslint-plugin-react": "^6.5.0",
"html-webpack-plugin": "^2.24.1",
Expand Down
39 changes: 39 additions & 0 deletions src/browser/LoginModal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import {BrowserWindow, ipcMain, session} from 'electron';

export default class LoginModal {
constructor(parent) {
const publicPath = process.env.NODE_ENV === 'development' ? `file://${__dirname}/../renderer/login` : `file://${__dirname}/login`;

this.ipc = ipcMain;

this.window = new BrowserWindow({
parent: parent,
modal: true,
show: false,
width: 600,
height: 400,
resizable: false
});
this.window.loadURL(`${publicPath}/index.html`);
this.window.once('ready-to-show', () => this.window.show());
this.window.on('closed', () => this.window = null);

this.ipc.on('RequestSetCookie', ::this._onRequestSetCookie)
}

_onRequestSetCookie(e, cookieValue) {
const cookie = {
url: 'http://.nicovideo.jp',
name: 'user_session',
value: cookieValue
}

session.defaultSession.cookies.set(cookie, ((err) => {
if (err) {
alert(err);
} else {
this.window.close();
}
}));
}
}
22 changes: 22 additions & 0 deletions src/browser/MainWindow.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import {app, BrowserWindow, ipcMain} from 'electron';
import path from 'path';
import windowStateKeeper from 'electron-window-state';
import LoginModal from './LoginModal';

export default class MainWindow {
constructor() {
const publicPath = process.env.NODE_ENV === 'development' ? `file://${__dirname}/../renderer` : `file://${__dirname}`;
const windowState = windowStateKeeper();

this.window = new BrowserWindow({ ...windowState });

this.window.loadURL(`${publicPath}/index.html`);
this.window.on('closed', () => this.window = null);
windowState.manage(this.window);
this.loginModal = null;
}

createLoginModal() {
this.loginModal = new LoginModal(this.window);
}
}
60 changes: 60 additions & 0 deletions src/libraries/SessionExtractor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import sqlite3 from 'sqlite3';
import path from 'path';
import keytar from 'keytar';
import crypto from 'crypto';

export default class SessionExtractor {
constructor() {
const dbPath = path.resolve(path.join(process.env.HOME, 'Library/Application Support/Google/Chrome/Default/Cookies'));
this.db = new sqlite3.Database(dbPath);
}

extract() {
return new Promise((resolve, reject) => {
this.db.serialize(() => {
Promise.resolve()
.then(() => {
return this.getEncryptedUserSession();
})
.then((value) => {
this.db.close();
resolve(this.decryptUserSession(value));
})
.catch((err) => {
this.db.close();
reject(err);
});
});
});
}

getEncryptedUserSession() {
return new Promise((resolve, reject) => {
this.db.get("SELECT * FROM cookies WHERE host_key = '.nicovideo.jp' AND name = 'user_session'", (err, row) => {
if (err) {
reject(err);
return;
}

resolve(row.encrypted_value);
});
});
}

decryptUserSession(encryptedValue) {
return new Promise((resolve, reject) => {
const password = keytar.getPassword('Chrome Safe Storage', 'Chrome');

crypto.pbkdf2(password, 'saltysalt', 1003, 16, 'sha1', (err, key) => {
if (err) {
reject(err);
return;
}

const iv = new Array(17).join(' ');
const decipher = crypto.createDecipheriv('aes-128-cbc', key, iv);
resolve(decipher.update(encryptedValue.slice(3)) + decipher.final());
});
});
}
}
36 changes: 21 additions & 15 deletions src/main.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,29 @@
import {app, BrowserWindow} from 'electron';
import {app} from 'electron';
import MainWindow from './browser/MainWindow';

let mainWindow = null;

app.on('ready', () => {
if (mainWindow) return;
class Main {
constructor() {
this.mainWindow = null;
}

mainWindow = new BrowserWindow({width: 800, height: 600});
mainWindow.loadURL(`file://${__dirname}/../src/index.html`);
mainWindow.on('closed', () => {
mainWindow = null;
});
onReady() {
this.mainWindow = new MainWindow();
this.mainWindow.createLoginModal();
}

if (process.env.NODE_ENV === 'development') {
mainWindow.openDevTools();
onWindowAllClosed() {
if (process.platform !== 'darwin') {
app.quit();
}
}
}

const main = new Main();

app.on('ready', () => {
main.onReady();
});

app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
main.onWindowAllClosed();
});
File renamed without changes.
File renamed without changes.
File renamed without changes.
30 changes: 30 additions & 0 deletions src/renderer/login/components/App.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from 'react';
import {ipcRenderer} from 'electron';
import SessionExtractor from '../../../libraries/SessionExtractor';

export default class App extends React.Component {
constructor(props) {
super(props);

this.ipc = ipcRenderer;
}

handleLogin() {
const sessionExtractor = new SessionExtractor();
sessionExtractor.extract()
.then((sessionId) => {
console.log(sessionId);
this.ipc.send('RequestSetCookie', sessionId);
})
.catch((err) => {
});
}

render() {
return (
<div>
<p onClick={::this.handleLogin}>ログイン</p>
</div>
);
}
}
19 changes: 19 additions & 0 deletions src/renderer/login/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<head>
<title>Nicomentron - ログイン</title>
<meta charset="utf-8">
</head>
<body>
<div id="root"></div>
<script>
{
const script = document.createElement('script');
const port = process.env.PORT || 3000;
script.src = process.env.NODE_ENV === 'development' ?
'http://localhost:' + port + '/login/index.js' : 'login/index.js';
document.write(script.outerHTML);
}
</script>
</body>
</html>
5 changes: 5 additions & 0 deletions src/renderer/login/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App';

ReactDOM.render(<App />, document.getElementById('root'));
5 changes: 4 additions & 1 deletion webpack.config.base.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,8 @@ export default {
}
]
},
externals: ['sqlite3']
externals: [
'sqlite3',
'keytar'
]
};
13 changes: 5 additions & 8 deletions webpack.config.development.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,18 @@ export default merge(baseConfig, {
debug: true,
devtool: 'cheap-module-eval-source-map',
entry: {
app: ['./src/index']
index: './src/renderer/index',
'login/index': './src/renderer/login/index'
},
output: {
path: path.join(__dirname, 'app'),
filename : 'index.js',
filename : '[name].js',
publicPath: `http://localhost:${port}/`
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'src/index.html',
inject: false
})
new webpack.HotModuleReplacementPlugin()
],
target: 'electron-renderer',
devServer: {
port: port,
inline: true,
Expand Down
3 changes: 2 additions & 1 deletion webpack.config.electron.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ export default merge(baseConfig, {
output: {
path: path.join(__dirname, 'app'),
filename : 'main.js',
}
},
target: 'electron'
});
14 changes: 11 additions & 3 deletions webpack.config.production.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,23 @@ import baseConfig from './webpack.config.base';

export default merge(baseConfig, {
devtool: 'cheap-module-eval-source-map',
entry: ['./src/index'],
entry: [
index: './src/renderer/index',
'login/index': './src/renderer/login/index'
],
output: {
path: path.join(__dirname, 'app'),
filename : 'index.js',
filename : '[name].js',
},
plugins: [
new HtmlWebpackPlugin({
filename: 'login/index.html',
template: 'src/renderer/login/index.html',
inject: false
}),
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'src/index.html',
template: 'src/renderer/index.html',
inject: false
})
]
Expand Down

0 comments on commit b9979ef

Please sign in to comment.