From 50d3c0e1fee295b3cf2d8ea00c4c851614351be9 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 23 Nov 2017 13:25:33 +0900 Subject: [PATCH] #939 --- package.json | 70 +++++++++-------- src/api/common/signin.ts | 19 +++++ src/api/private/signin.ts | 14 +--- src/api/service/twitter.ts | 78 ++++++++++++++++--- src/web/app/desktop/tags/pages/entrance.tag | 1 + .../app/mobile/tags/page/entrance/signin.tag | 1 + 6 files changed, 128 insertions(+), 55 deletions(-) create mode 100644 src/api/common/signin.ts diff --git a/package.json b/package.json index 73c7b198d..bfe4fd28c 100644 --- a/package.json +++ b/package.json @@ -22,11 +22,14 @@ "format": "gulp format" }, "dependencies": { + "@prezzemolo/rap": "0.1.2", + "@prezzemolo/zip": "0.0.3", "@types/bcryptjs": "2.4.1", "@types/body-parser": "1.16.7", "@types/chai": "4.0.4", "@types/chai-http": "3.0.3", "@types/compression": "0.0.34", + "@types/cookie": "^0.3.1", "@types/cors": "2.8.1", "@types/debug": "0.0.30", "@types/deep-equal": "1.0.1", @@ -67,11 +70,33 @@ "@types/webpack": "3.8.0", "@types/webpack-stream": "3.2.8", "@types/websocket": "0.0.34", + "accesses": "2.5.0", + "animejs": "2.2.0", + "autwh": "0.0.1", "awesome-typescript-loader": "3.4.0", + "bcryptjs": "2.4.3", + "body-parser": "1.18.2", + "cafy": "3.2.0", "chai": "4.1.2", "chai-http": "3.0.0", + "chalk": "2.3.0", + "compression": "1.7.1", + "cookie": "^0.3.1", + "cors": "2.8.4", + "cropperjs": "1.1.3", "css-loader": "0.28.7", + "debug": "3.1.0", + "deep-equal": "1.0.1", + "deepcopy": "0.6.3", + "diskusage": "0.2.2", + "elasticsearch": "13.3.1", + "escape-regexp": "0.0.1", "event-stream": "3.3.4", + "eventemitter3": "^2.0.3", + "express": "4.15.4", + "file-type": "7.2.0", + "fuckadblock": "3.2.1", + "gm": "1.23.0", "gulp": "3.9.1", "gulp-cssnano": "2.1.2", "gulp-htmlmin": "3.0.0", @@ -84,45 +109,12 @@ "gulp-typescript": "3.2.3", "gulp-uglify": "3.0.0", "gulp-util": "3.0.8", - "mocha": "4.0.1", - "riot-tag-loader": "1.0.0", - "string-replace-webpack-plugin": "0.1.3", - "style-loader": "0.19.0", - "stylus": "0.54.5", - "stylus-loader": "3.0.1", - "swagger-jsdoc": "1.9.7", - "tslint": "5.8.0", - "uglify-es": "3.1.8", - "uglifyjs-webpack-plugin": "1.1.0", - "webpack": "3.8.1", - "@prezzemolo/rap": "0.1.2", - "@prezzemolo/zip": "0.0.3", - "accesses": "2.5.0", - "animejs": "2.2.0", - "autwh": "0.0.1", - "bcryptjs": "2.4.3", - "body-parser": "1.18.2", - "cafy": "3.2.0", - "chalk": "2.3.0", - "compression": "1.7.1", - "cors": "2.8.4", - "cropperjs": "1.1.3", - "debug": "3.1.0", - "deep-equal": "1.0.1", - "deepcopy": "0.6.3", - "diskusage": "0.2.2", - "elasticsearch": "13.3.1", - "escape-regexp": "0.0.1", - "eventemitter3": "^2.0.3", - "express": "4.15.4", - "file-type": "7.2.0", - "fuckadblock": "3.2.1", - "gm": "1.23.0", "inquirer": "3.3.0", "is-root": "1.0.0", "is-url": "1.2.2", "js-yaml": "3.10.0", "mecab-async": "^0.1.0", + "mocha": "4.0.1", "moji": "^0.5.1", "mongodb": "2.2.33", "monk": "6.0.5", @@ -143,21 +135,31 @@ "request": "^2.83.0", "rimraf": "2.6.2", "riot": "3.7.4", + "riot-tag-loader": "1.0.0", "rndstr": "1.0.0", "s-age": "1.1.0", "seedrandom": "^2.4.3", "serve-favicon": "2.4.5", "sortablejs": "1.7.0", + "string-replace-webpack-plugin": "0.1.3", + "style-loader": "0.19.0", + "stylus": "0.54.5", + "stylus-loader": "3.0.1", "summaly": "2.0.3", + "swagger-jsdoc": "1.9.7", "syuilo-password-strength": "0.0.1", "tcp-port-used": "0.1.2", "textarea-caret": "3.0.2", "tmp": "0.0.33", "ts-node": "3.3.0", + "tslint": "5.8.0", "typescript": "2.6.1", + "uglify-es": "3.1.8", + "uglifyjs-webpack-plugin": "1.1.0", "uuid": "3.1.0", "vhost": "3.0.2", "web-push": "^3.2.4", + "webpack": "3.8.1", "websocket": "1.0.25", "xev": "2.0.0" } diff --git a/src/api/common/signin.ts b/src/api/common/signin.ts new file mode 100644 index 000000000..693e62f39 --- /dev/null +++ b/src/api/common/signin.ts @@ -0,0 +1,19 @@ +import config from '../../conf'; + +export default function(res, user, redirect: boolean) { + const expires = 1000 * 60 * 60 * 24 * 365; // One Year + res.cookie('i', user.token, { + path: '/', + domain: `.${config.host}`, + secure: config.url.substr(0, 5) === 'https', + httpOnly: false, + expires: new Date(Date.now() + expires), + maxAge: expires + }); + + if (redirect) { + res.redirect(config.url); + } else { + res.sendStatus(204); + } +} diff --git a/src/api/private/signin.ts b/src/api/private/signin.ts index 068988792..0ebf8d6aa 100644 --- a/src/api/private/signin.ts +++ b/src/api/private/signin.ts @@ -4,7 +4,7 @@ import { default as User, IUser } from '../models/user'; import Signin from '../models/signin'; import serialize from '../serializers/signin'; import event from '../event'; -import config from '../../conf'; +import signin from '../common/signin'; export default async (req: express.Request, res: express.Response) => { res.header('Access-Control-Allow-Credentials', 'true'); @@ -43,17 +43,7 @@ export default async (req: express.Request, res: express.Response) => { const same = await bcrypt.compare(password, user.password); if (same) { - const expires = 1000 * 60 * 60 * 24 * 365; // One Year - res.cookie('i', user.token, { - path: '/', - domain: `.${config.host}`, - secure: config.url.substr(0, 5) === 'https', - httpOnly: false, - expires: new Date(Date.now() + expires), - maxAge: expires - }); - - res.sendStatus(204); + signin(res, user, false); } else { res.status(400).send({ error: 'incorrect password' diff --git a/src/api/service/twitter.ts b/src/api/service/twitter.ts index 9fb274aac..b5dfc2c99 100644 --- a/src/api/service/twitter.ts +++ b/src/api/service/twitter.ts @@ -1,4 +1,6 @@ import * as express from 'express'; +import * as cookie from 'cookie'; +import * as uuid from 'uuid'; // import * as Twitter from 'twitter'; // const Twitter = require('twitter'); import autwh from 'autwh'; @@ -7,6 +9,7 @@ import User from '../models/user'; import serialize from '../serializers/user'; import event from '../event'; import config from '../../conf'; +import signin from '../common/signin'; module.exports = (app: express.Application) => { app.get('/disconnect/twitter', async (req, res): Promise => { @@ -30,8 +33,13 @@ module.exports = (app: express.Application) => { if (config.twitter == null) { app.get('/connect/twitter', (req, res) => { - res.send('現在Twitterへ接続できません'); + res.send('現在Twitterへ接続できません (このインスタンスではTwitterはサポートされていません)'); }); + + app.get('/signin/twitter', (req, res) => { + res.send('現在Twitterへ接続できません (このインスタンスではTwitterはサポートされていません)'); + }); + return; } @@ -41,6 +49,12 @@ module.exports = (app: express.Application) => { callbackUrl: `${config.api_url}/tw/cb` }); + const twAuthSignin = autwh({ + consumerKey: config.twitter.consumer_key, + consumerSecret: config.twitter.consumer_secret, + callbackUrl: `${config.api_url}/signin/twitter/cb` + }); + app.get('/connect/twitter', async (req, res): Promise => { if (res.locals.user == null) return res.send('plz signin'); const ctx = await twAuth.begin(); @@ -56,15 +70,15 @@ module.exports = (app: express.Application) => { const user = await User.findOneAndUpdate({ token: res.locals.user }, { - $set: { - twitter: { - access_token: result.accessToken, - access_token_secret: result.accessTokenSecret, - user_id: result.userId, - screen_name: result.screenName - } + $set: { + twitter: { + access_token: result.accessToken, + access_token_secret: result.accessTokenSecret, + user_id: result.userId, + screen_name: result.screenName } - }); + } + }); res.send(`Twitter: @${result.screenName} を、Misskey: @${user.username} に接続しました!`); @@ -75,4 +89,50 @@ module.exports = (app: express.Application) => { })); }); }); + + app.get('/signin/twitter', async (req, res): Promise => { + const ctx = await twAuthSignin.begin(); + + const sessid = uuid(); + + redis.set(sessid, JSON.stringify(ctx)); + + const expires = 1000 * 60 * 60; // 1h + res.cookie('signin_with_twitter_session_id', sessid, { + path: '/', + domain: `.${config.host}`, + secure: config.url.substr(0, 5) === 'https', + httpOnly: true, + expires: new Date(Date.now() + expires), + maxAge: expires + }); + + res.redirect(ctx.url); + }); + + app.get('/signin/twitter/cb', (req, res): any => { + // req.headers['cookie'] は常に string ですが、型定義の都合上 + // string | string[] になっているので string を明示しています + const cookies = cookie.parse((req.headers['cookie'] as string || '')); + + const sessid = cookies['signin_with_twitter_session_id']; + + if (sessid == undefined) { + res.status(400).send('invalid session'); + } + + redis.get(sessid, async (_, ctx) => { + const result = await twAuthSignin.done(JSON.parse(ctx), req.query.oauth_verifier); + + const user = await User.findOne({ + 'twitter.user_id': result.userId + }); + + if (user == null) { + res.status(404).send(`@${result.screenName}と連携しているMisskeyアカウントはありませんでした...`); + } + + signin(res, user, true); + }); + }); }; diff --git a/src/web/app/desktop/tags/pages/entrance.tag b/src/web/app/desktop/tags/pages/entrance.tag index 824847f51..02aeb922f 100644 --- a/src/web/app/desktop/tags/pages/entrance.tag +++ b/src/web/app/desktop/tags/pages/entrance.tag @@ -157,6 +157,7 @@ + Twitterでサインイン
or
Misskeyについて