Skip to content

Commit 224df23

Browse files
committed
tistory oauth2 ExternalOAuth2로 전환
1 parent 5e9c6ba commit 224df23

File tree

8 files changed

+157
-36
lines changed

8 files changed

+157
-36
lines changed

docs/callback.html

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,13 @@ <h1 class="title-text">Editor</h1>
8181
</div>
8282

8383
<div class="loading">로딩중</div>
84-
85-
<div>
86-
<a href="tistory-editor://tistory-editor/callback?state=app">열기</a>
87-
</div>
8884
</div>
85+
86+
<script>
87+
if (navigator.userAgent.indexOf("TistoryEditor") < 0) {
88+
const url = new URL(location.href)
89+
location.href = "tistory-editor://tistory-editor/callback" + url.search
90+
}
91+
</script>
8992
</body>
9093
</html>

src/main/apis/tistory-api.js

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
const oauth2 = require('../oauth/ElectronOauth2');
21
const fs = require('fs')
32
const fetch = require('isomorphic-fetch')
43
const querystring = require('querystring')
@@ -7,6 +6,7 @@ const {session} = require('electron')
76
const stream = require('stream')
87
const Oauth2infoReader = require('../oauth/OauthInfoReader')
98
const appInfo = require('../appInfo')
9+
const ExternalOAuth2 = require('../oauth/ExternalOAuth2');
1010

1111
const errorHandler = (res) => {
1212
if (!res.ok) {
@@ -20,19 +20,17 @@ const errorHandler = (res) => {
2020

2121
const BASE_URL = 'https://www.tistory.com/apis'
2222

23-
const getAccessToken = () => {
23+
const requestAuth = () => {
2424
const oauth2infoReader = new Oauth2infoReader()
25-
const tistoryOAuth = oauth2(oauth2infoReader.getTistory(), {
26-
alwaysOnTop: true,
27-
autoHideMenuBar: true,
28-
webPreferences: {
29-
nodeIntegration: false,
30-
contextIsolation: true,
31-
session: session.fromPartition("tistory:oauth2:" + new Date())
32-
}
33-
}, 'GET')
25+
const tistoryOAuth = new ExternalOAuth2(oauth2infoReader.getTistory())
26+
tistoryOAuth.requestAuth({})
27+
return tistoryOAuth.getState()
28+
}
3429

35-
return tistoryOAuth.getAccessToken({})
30+
const requestToken = (code) => {
31+
const oauth2infoReader = new Oauth2infoReader()
32+
const tistoryOAuth = new ExternalOAuth2(oauth2infoReader.getTistory())
33+
return tistoryOAuth.requestToken(code, 'GET')
3634
}
3735

3836
const fetchBlogInfo = (auth) => {
@@ -253,7 +251,8 @@ const fetchAccount = async (auth) => {
253251

254252

255253
module.exports = {
256-
getAccessToken: getAccessToken,
254+
requestAuth: requestAuth,
255+
requestToken: requestToken,
257256
fetchBlogInfo: fetchBlogInfo,
258257
fetchUser: fetchUser,
259258
fetchPosts: fetchPosts,

src/main/events/auth.js

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ const uuid = require('uuid').v4
22
const { ipcMain } = require('electron')
33
const AuthenticationManager = require('../lib/AuthenticationManager')
44
const ProviderApiManager = require('../lib/ProviderApiManager')
5+
const OAuthRequestManager = require('../oauth/OAuthRequestManager');
56

67
async function fetchAccount(auth) {
78
const api = ProviderApiManager.getApi(auth.provider)
@@ -16,6 +17,7 @@ function fetchAccounts(authList) {
1617

1718
function saveAuth(auth) {
1819
AuthenticationManager.add(auth)
20+
console.log("savedAuth", auth)
1921
return auth
2022
}
2123

@@ -35,19 +37,33 @@ module.exports = () => {
3537
ipcMain.on("request-auth", (evt, provider) => {
3638
console.log('Main.receive: request-auth', provider)
3739
let providerApi = ProviderApiManager.getApi(provider)
38-
providerApi.getAccessToken()
39-
.then(authInfo => ({
40-
uuid: uuid(),
41-
provider: provider,
42-
authInfo: authInfo
43-
}))
44-
.then(auth => saveAuth(auth))
45-
.then(auth => {
46-
fetchAccount(auth)
47-
.then(account => {
48-
evt.sender.send('receive-account', account)
49-
})
50-
})
40+
let state = providerApi.requestAuth()
41+
OAuthRequestManager.saveRequestInfo(state, (code) => {
42+
const providerApi = ProviderApiManager.getApi(provider)
43+
providerApi.requestToken(code)
44+
.then(data => {
45+
if (data.error) {
46+
throw new Error(`${data.error}: ${data.error_description}`)
47+
}
48+
49+
return {
50+
uuid: uuid(),
51+
provider: provider,
52+
authInfo: data
53+
}
54+
})
55+
.then(saveAuth)
56+
.then(auth => {
57+
providerApi.fetchAccount(auth)
58+
.then(account => {
59+
evt.sender.send('receive-account', account)
60+
})
61+
})
62+
.catch(e => {
63+
console.error(e)
64+
evt.sender.send('receive-message', `오류가 발생했습니다. (${e.message})`)
65+
})
66+
})
5167
})
5268

5369
ipcMain.on("disconnect-auth", (evt, uuid) => {

src/main/lib/AuthUtils.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
2+
class AuthUtils {
3+
static generateRandomString(length) {
4+
var text = '';
5+
var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
6+
7+
for (var i = 0; i < length; i++) {
8+
text += possible.charAt(Math.floor(Math.random() * possible.length));
9+
}
10+
11+
return text;
12+
}
13+
}
14+
15+
module.exports = AuthUtils

src/main/main.js

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
const { app, protocol } = require('electron')
2-
const url = require('url')
32
const ipc = require('./ipc-event')
43
const settings = require('electron-settings')
54
const { initWindow } = require('./window')
5+
const OAuthRequestManager = require('./oauth/OAuthRequestManager')
6+
const ProviderApiManager = require('./lib/ProviderApiManager')
67

78
const PROTOCOL = "tistory-editor"
89

@@ -24,10 +25,13 @@ app.on('window-all-closed', () => {
2425
app.quit()
2526
})
2627

27-
app.on("open-url", (e, data) => {
28-
e.preventDefault()
29-
// tistory-editor://tistory-editor/callback?state=app
30-
console.log("open url", url.parse(data))
28+
app.on("open-url", (e, urlString) => {
29+
const url = new URL(urlString)
30+
console.log("OPEN_URL", urlString, url, url.pathname, url.searchParams.get('code'), url.searchParams.get('state'))
31+
const requestHandler = OAuthRequestManager.loadRequestInfo(url.searchParams.get('state'))
32+
if (requestHandler) {
33+
requestHandler(url.searchParams.get('code'))
34+
}
3135
})
3236

3337
app.setAsDefaultProtocolClient(PROTOCOL)

src/main/oauth/ElectronOauth2.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const objectAssign = require('object-assign');
77
const nodeUrl = require('url');
88
const electron = require('electron');
99
const BrowserWindow = electron.BrowserWindow || electron.remote.BrowserWindow;
10+
const AuthUtils = require('../lib/AuthUtils')
1011

1112
var generateRandomString = function (length) {
1213
var text = '';
@@ -31,7 +32,7 @@ module.exports = function (oauthInfo, windowParams, tokenMethod = 'POST') {
3132
response_type: 'code',
3233
redirect_uri: oauthInfo.redirectUri,
3334
client_id: oauthInfo.clientId,
34-
state: generateRandomString(16)
35+
state: AuthUtils.generateRandomString(16)
3536
};
3637

3738
if (opts.scope) {

src/main/oauth/ExternalOAuth2.js

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
const Promise = require('pinkie-promise');
2+
const queryString = require('querystring');
3+
const fetch = require('node-fetch');
4+
const electron = require('electron');
5+
const AuthUtils = require('../lib/AuthUtils')
6+
7+
class ExternalOAuth2 {
8+
#oauthInfo
9+
#state
10+
11+
constructor(oauthInfo) {
12+
this.#oauthInfo = oauthInfo
13+
this.#state = AuthUtils.generateRandomString(16)
14+
}
15+
16+
getState() {
17+
return this.#state
18+
}
19+
20+
requestAuth(opts) {
21+
opts = opts || {};
22+
23+
var urlParams = {
24+
response_type: opts.scope || 'code',
25+
redirect_uri: this.#oauthInfo.redirectUri,
26+
client_id: this.#oauthInfo.clientId,
27+
state: this.#state
28+
};
29+
30+
if (opts.accessType) {
31+
urlParams.access_type = opts.accessType;
32+
}
33+
34+
var url = this.#oauthInfo.authorizationUrl + '?' + queryString.stringify(urlParams);
35+
electron.shell.openExternal(url)
36+
}
37+
38+
requestToken(authorizationCode, tokenMethod = 'POST') {
39+
var tokenRequestData = {
40+
code: authorizationCode,
41+
grant_type: 'authorization_code',
42+
redirect_uri: this.#oauthInfo.redirectUri,
43+
client_id: this.#oauthInfo.clientId,
44+
client_secret: this.#oauthInfo.clientSecret
45+
};
46+
47+
let url = this.#oauthInfo.tokenUrl;
48+
let fetchOptions = {
49+
method: tokenMethod,
50+
headers: {
51+
'Accept': 'application/json'
52+
}
53+
};
54+
55+
if (tokenMethod == 'GET') {
56+
url = `${this.#oauthInfo.tokenUrl}?${queryString.stringify(tokenRequestData)}`
57+
} else {
58+
fetchOptions['headers']['Content-Type'] = 'application/x-www-form-urlencoded';
59+
fetchOptions['body'] = queryString.stringify(tokenRequestData);
60+
}
61+
62+
console.log("requestToken", tokenMethod, url, fetchOptions)
63+
64+
return fetch(url, fetchOptions)
65+
.then(res => res.json())
66+
}
67+
}
68+
69+
module.exports = ExternalOAuth2
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
2+
const requestInfo = []
3+
4+
class OAuthRequestManager {
5+
static saveRequestInfo(state, info) {
6+
requestInfo[state] = info
7+
}
8+
9+
static loadRequestInfo(state) {
10+
return requestInfo[state]
11+
}
12+
}
13+
14+
module.exports = OAuthRequestManager

0 commit comments

Comments
 (0)