Heeyeon

Add kakao Login function

Showing 48 changed files with 1863 additions and 111 deletions
...@@ -8,6 +8,7 @@ const fileStore = require('session-file-store')(session); ...@@ -8,6 +8,7 @@ const fileStore = require('session-file-store')(session);
8 const app = express(); 8 const app = express();
9 var flash = require('connect-flash'); 9 var flash = require('connect-flash');
10 var NaverStrategy = require('passport-naver').Strategy; 10 var NaverStrategy = require('passport-naver').Strategy;
11 +var KakaoStrategy = require('passport-kakao').Strategy;
11 12
12 13
13 14
...@@ -51,11 +52,12 @@ app.get('/main',(req,res)=>{ ...@@ -51,11 +52,12 @@ app.get('/main',(req,res)=>{
51 }) 52 })
52 53
53 54
55 +/*--------------------로그인 처리---------------------- */
56 +
54 //로그인 페이지 57 //로그인 페이지
55 app.get('/login',(req,res)=>{ 58 app.get('/login',(req,res)=>{
56 let page = getLoginButton(`<a href="/">뒤로가기</a>`); 59 let page = getLoginButton(`<a href="/">뒤로가기</a>`);
57 res.send(page); 60 res.send(page);
58 -
59 }); 61 });
60 62
61 63
...@@ -96,25 +98,65 @@ passport.authenticate('local', { ...@@ -96,25 +98,65 @@ passport.authenticate('local', {
96 failureFlash: true 98 failureFlash: true
97 })); 99 }));
98 100
101 +//로그 아웃 처리
102 +app.get('/logout',(req,res)=>{
103 +
104 + //passport 정보 삭제
105 + req.logout();
106 + //서버측 세션 삭제
107 + req.session.destroy(()=>{
108 + //클라이언트 측 세션 암호화 쿠키 삭제
109 + res.cookie('connect.sid','',{maxAge:0});
110 + res.redirect('/');
111 + });
112 +});
113 +
114 +
115 +//로그인 로그아웃 여부
116 +const authInfo = (req)=>{
117 + if(req.user) return `${user.name} | <a href="/logout">로그아웃</a>`;
118 + return `<a href="/login">로그인</a>`;
119 +}
120 +
121 +// naver 로그인
122 +app.get('/naverlogin', passport.authenticate('naver'));
123 +passport.use('naver',new NaverStrategy({
124 + clientID: 'CGVVomc0bhMhzfzbytK2',
125 + clientSecret: 'XHylcjnZxG',
126 + callbackURL: "http://localhost:3000/",
127 + svcType: 0,
128 + authType: 'reauthenticate' // enable re-authentication
129 + },
130 +
131 + function(accessToken, refreshToken, profile, done) {
132 + var _profile = profile._json;
133 + console.log(_profile.id);
134 + console.log(_profile.properties.nickname);
135 + }
136 + ));
137 +
138 +
139 +// kakao 로그인
140 +app.get('/kakaologin', passport.authenticate('kakao-login'));
141 +passport.use('kakao-login', new KakaoStrategy({
142 + clientID: '8a854307a99092b4eeeff5e4a79c0ac0',
143 + callbackURL: 'http://localhost:3000/'
144 +},
145 +function (accessToken, refreshToken, profile, done) {
146 + var _profile = profile._json;
147 + console.log(_profile.id);
148 + console.log(_profile.properties.nickname);
149 +
150 +}
151 +));
152 +
153 +
154 +/*--------------------회원가입 처리---------------------- */
155 +
99 156
100 //회원가입 페이지 Get 157 //회원가입 페이지 Get
101 app.get('/join',(req,res)=>{ 158 app.get('/join',(req,res)=>{
102 -<<<<<<< HEAD
103 let page = getPage('회원가입',` 159 let page = getPage('회원가입',`
104 - <script> function congratulation()
105 - {
106 - alert("새로운 회원이 되신걸 축하합니다!:D 레시피 찾을 준비 되셨나요?");
107 - } </script>
108 - <form action="/join" method="post">
109 - <input type="email" name="email" placeholder="email"><br>
110 - <input type="password" name="password" placeholder="****"><br>
111 - <input type="name" name="name" placeholder="이름"><br>
112 - <input type="submit" value="회원가입" onClick="javascript:congratulation()"><br>
113 - </form>
114 - `,'<a href="/login">뒤로가기</a>');
115 - res.send(page);
116 -=======
117 - let page = getPage('',`
118 <html> 160 <html>
119 <head> 161 <head>
120 <style> 162 <style>
...@@ -195,42 +237,14 @@ app.get('/join',(req,res)=>{ ...@@ -195,42 +237,14 @@ app.get('/join',(req,res)=>{
195 </html> 237 </html>
196 `,'<a href="/login">뒤로가기</a>'); 238 `,'<a href="/login">뒤로가기</a>');
197 res.send(page); 239 res.send(page);
198 ->>>>>>> 04a7922847fd162bc1b11e832ee65246c056146d
199 -});
200 -
201 -//회원가입 처리 Post : 예제를 위해 간단 저장 방식으로 구현
202 -var user = {};
203 -app.post('/join',(req,res)=>{
204 - user.email = req.body.email;
205 - user.password = req.body.password;
206 - user.name=req.body.name;
207 - //로그인 페이지로 이동
208 - res.redirect('/login');
209 }); 240 });
210 241
211 -//로그 아웃 처리
212 -app.get('/logout',(req,res)=>{
213 -
214 - //passport 정보 삭제
215 - req.logout();
216 - //서버측 세션 삭제
217 - req.session.destroy(()=>{
218 - //클라이언트 측 세션 암호화 쿠키 삭제
219 - res.cookie('connect.sid','',{maxAge:0});
220 - res.redirect('/');
221 - });
222 -});
223 242
224 243
225 //포트 연결 244 //포트 연결
226 app.listen(3000,()=>console.log(`http://localhost:3000`)); 245 app.listen(3000,()=>console.log(`http://localhost:3000`));
227 246
228 247
229 -//로그인 로그아웃 여부
230 -const authInfo = (req)=>{
231 - if(req.user) return `${user.name} | <a href="/logout">로그아웃</a>`;
232 - return `<a href="/login">로그인</a>`;
233 -}
234 248
235 //페이지 템플릿 249 //페이지 템플릿
236 const getPage = (title, content, auth) =>{ 250 const getPage = (title, content, auth) =>{
...@@ -341,22 +355,19 @@ const getLoginButton = (auth) =>{ ...@@ -341,22 +355,19 @@ const getLoginButton = (auth) =>{
341 </div> 355 </div>
342 </div> 356 </div>
343 </div> 357 </div>
344 - <!-- 네이버 로그인 버튼 노출 영역 --> 358 +
345 - <div id="naver_id_login"></div> 359 + <div>
346 - <!-- //네이버 로그인 버튼 노출 영역 --> 360 + <a href="/naverlogin" class="btn btn-block btn-lg btn-success btn_login">Naver</a>
347 - <script type="text/javascript"> 361 + <a href="/kakaologin" class="btn btn-block btn-lg btn-warning btn_login">KaKao</a>
348 - var naver_id_login = new naver_id_login("CGVVomc0bhMhzfzbytK2", "http://localhost:3000/naverlogin"); 362 + </div>
349 - var state = naver_id_login.getUniqState(); 363 +
350 - naver_id_login.setButton("white", 2,40); 364 +
351 - naver_id_login.setState(state);
352 - naver_id_login.setPopup();
353 - naver_id_login.init_naver_id_login();
354 - </script>
355 </body> 365 </body>
356 </html> 366 </html>
357 `; 367 `;
358 } 368 }
359 369
370 +
360 //첫 페이지 화면 371 //첫 페이지 화면
361 const getFirstPage =(title, content, auth) =>{ 372 const getFirstPage =(title, content, auth) =>{
362 return ` 373 return `
...@@ -385,36 +396,3 @@ const getFirstPage =(title, content, auth) =>{ ...@@ -385,36 +396,3 @@ const getFirstPage =(title, content, auth) =>{
385 396
386 } 397 }
387 398
388 -
389 -var client_id = 'CGVVomc0bhMhzfzbytK2';
390 -var client_secret = 'XHylcjnZxG';
391 -var state = "RAMDOM_STATE";
392 -var redirectURI = encodeURI("http://localhost:3000/");
393 -var api_url = "";
394 -
395 -
396 -app.get('/naverlogin', function (req, res) {
397 - api_url = 'https://nid.naver.com/oauth2.0/authorize?response_type=code&client_id=' + client_id + '&redirect_uri=' + redirectURI + '&state=' + state;
398 - res.writeHead(200, {'Content-Type': 'text/html;charset=utf-8'});
399 - res.end("<a href='"+ api_url + "'><img height='50' src='http://static.nid.naver.com/oauth/small_g_in.PNG'/></a>");
400 - });
401 - app.get('/callback', function (req, res) {
402 - code = req.query.code;
403 - state = req.query.state;
404 - api_url = 'https://nid.naver.com/oauth2.0/token?grant_type=authorization_code&client_id='
405 - + client_id + '&client_secret=' + client_secret + '&redirect_uri=' + redirectURI + '&code=' + code + '&state=' + state;
406 - var request = require('request');
407 - var options = {
408 - url: api_url,
409 - headers: {'X-Naver-Client-Id':client_id, 'X-Naver-Client-Secret': client_secret}
410 - };
411 - request.get(options, function (error, response, body) {
412 - if (!error && response.statusCode == 200) {
413 - res.writeHead(200, {'Content-Type': 'text/json;charset=utf-8'});
414 - res.end(body);
415 - } else {
416 - res.status(response.statusCode).end();
417 - console.log('error = ' + response.statusCode);
418 - }
419 - });
420 - });
...\ No newline at end of file ...\ No newline at end of file
......
1 -<!doctype html>
2 -<html lang="ko">
3 -<head>
4 - <meta charset="utf-8">
5 - <title>네이버 로그인</title>
6 - <script type="text/javascript" src="https://static.nid.naver.com/js/naverLogin_implicit-1.0.3.js" charset="utf-8"></script>
7 - <script type="text/javascript" src="http://code.jquery.com/jquery-1.11.3.min.js"></script>
8 -</head>
9 -<body>
10 - <!-- 네이버 로그인 버튼 노출 영역 -->
11 - <div id="naver_id_login"></div>
12 - <!-- //네이버 로그인 버튼 노출 영역 -->
13 - <script type="text/javascript">
14 - var naver_id_login = new naver_id_login("CGVVomc0bhMhzfzbytK2", "http://localhost:3000/");
15 - var state = naver_id_login.getUniqState();
16 - naver_id_login.setButton("white", 2,40);
17 - naver_id_login.setDomain("http://localhost:3000/");
18 - naver_id_login.setState(state);
19 - naver_id_login.setPopup();
20 - naver_id_login.init_naver_id_login();
21 - </script>
22 -</html>
...\ No newline at end of file ...\ No newline at end of file
...@@ -1713,6 +1713,28 @@ ...@@ -1713,6 +1713,28 @@
1713 "node": ">= 0.4.0" 1713 "node": ">= 0.4.0"
1714 } 1714 }
1715 }, 1715 },
1716 + "node_modules/passport-kakao": {
1717 + "version": "1.0.1",
1718 + "resolved": "https://registry.npmjs.org/passport-kakao/-/passport-kakao-1.0.1.tgz",
1719 + "integrity": "sha512-uItaYRVrTHL6iGPMnMZvPa/O1GrAdh/V6EMjOHcFlQcVroZ9wgG7BZ5PonMNJCxfHQ3L2QVNRnzhKWUzSsumbw==",
1720 + "dependencies": {
1721 + "passport-oauth2": "~1.1.2",
1722 + "pkginfo": "~0.3.0"
1723 + }
1724 + },
1725 + "node_modules/passport-kakao/node_modules/passport-oauth2": {
1726 + "version": "1.1.2",
1727 + "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.1.2.tgz",
1728 + "integrity": "sha1-vXFjsbYJA3GGjcTvb58uHkzEuUg=",
1729 + "dependencies": {
1730 + "oauth": "0.9.x",
1731 + "passport-strategy": "1.x.x",
1732 + "uid2": "0.0.x"
1733 + },
1734 + "engines": {
1735 + "node": ">= 0.4.0"
1736 + }
1737 + },
1716 "node_modules/passport-local": { 1738 "node_modules/passport-local": {
1717 "version": "1.0.0", 1739 "version": "1.0.0",
1718 "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz", 1740 "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz",
...@@ -1827,6 +1849,14 @@ ...@@ -1827,6 +1849,14 @@
1827 "url": "https://github.com/sponsors/jonschlinkert" 1849 "url": "https://github.com/sponsors/jonschlinkert"
1828 } 1850 }
1829 }, 1851 },
1852 + "node_modules/pkginfo": {
1853 + "version": "0.3.1",
1854 + "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz",
1855 + "integrity": "sha1-Wyn2qB9wcXFC4J52W76rl7T4HiE=",
1856 + "engines": {
1857 + "node": ">= 0.4.0"
1858 + }
1859 + },
1830 "node_modules/prepend-http": { 1860 "node_modules/prepend-http": {
1831 "version": "2.0.0", 1861 "version": "2.0.0",
1832 "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", 1862 "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz",
......
1 +module.exports = {
2 + env: {
3 + es6: true,
4 + },
5 + extends: [
6 + 'eslint:recommended',
7 + 'plugin:@typescript-eslint/eslint-recommended',
8 + 'plugin:@typescript-eslint/recommended',
9 + ],
10 + globals: {
11 + Atomics: 'readonly',
12 + SharedArrayBuffer: 'readonly',
13 + },
14 + parser: '@typescript-eslint/parser',
15 + parserOptions: {
16 + ecmaFeatures: {
17 + jsx: true,
18 + },
19 + ecmaVersion: 2018,
20 + sourceType: 'module',
21 + },
22 + plugins: ['@typescript-eslint'],
23 + settings: {
24 + react: {
25 + version: 'detect',
26 + },
27 + },
28 + rules: {
29 + semi: ['warn', 'never'],
30 + 'sort-imports': ['off'],
31 + quotes: ['warn', 'single'],
32 + 'no-extra-boolean-cast': ['off'],
33 + '@typescript-eslint/member-delimiter-style': [
34 + 'warn',
35 + {
36 + multiline: {
37 + delimiter: 'none',
38 + requireLast: false,
39 + },
40 + singleline: {
41 + requireLast: false,
42 + },
43 + },
44 + ],
45 + '@typescript-eslint/no-empty-interface': ['off'],
46 + '@typescript-eslint/explicit-function-return-type': ['off'],
47 + '@typescript-eslint/no-explicit-any': ['off'],
48 + '@typescript-eslint/no-non-null-assertion': ['off'],
49 + '@typescript-eslint/interface-name-prefix': ['warn'],
50 + },
51 +}
1 +{
2 + "trailingComma": "es5",
3 + "semi": false,
4 + "singleQuote": true,
5 + "arrowParens": "avoid",
6 + "bracketSpacing": true,
7 + "endOfLine": "auto",
8 + "htmlWhitespaceSensitivity": "css",
9 + "proseWrap": "preserve",
10 + "quoteProps": "as-needed",
11 + "tabWidth": 2
12 +}
1 +language: node_js
2 +node_js:
3 + - '0.10'
4 +before_script:
5 + - npm install -g jasmine-node
6 +
1 +# passport-kakao
2 +
3 +kakao oauth2 로그인과 passport 모듈 연결체.
4 +
5 +## install
6 +
7 +```sh
8 +npm install passport-kakao
9 +```
10 +
11 +## how to use
12 +
13 +- https://developers.kakao.com/ 에서 애플리케이션을 등록한다.
14 +- 방금 추가한 애플리케이션의 설정 - 사용자 관리에 들어가서 사용을 ON으로 한 뒤 저장한다.
15 +- 설정 - 일반에서, 플랫폼 추가를 누른 후 웹 플랫폼을 추가한다.
16 +- 웹 플랫폼 설정의 사이트 도메인에 자신의 사이트 도메인을 추가한다. (ex : http://localhost:3000)
17 +- 프로그램 상에서는 아래와 같이 사용한다.
18 +
19 +> clientSecret을 활성화 한 경우 해당 파라메터를 같이 넘겨줘야한다.
20 +
21 +```javascript
22 +const passport = require('passport')
23 +const KakaoStrategy = require('passport-kakao').Strategy
24 +
25 +passport.use(new KakaoStrategy({
26 + clientID : clientID,
27 + clientSecret: clientSecret, // clientSecret을 사용하지 않는다면 넘기지 말거나 빈 스트링을 넘길 것
28 + callbackURL : callbackURL
29 + },
30 + (accessToken, refreshToken, profile, done) => {
31 + // 사용자의 정보는 profile에 들어있다.
32 + User.findOrCreate(..., (err, user) => {
33 + if (err) { return done(err) }
34 + return done(null, user)
35 + })
36 + }
37 +))
38 +```
39 +
40 +> 기본 callbackPath는 `/oauth` 이고 https://developers.kakao.com 에서 수정할 수 있다. 하지만 callbackURL은 `사이트 도메인/oauth` 로 설정하는 것을 권장함. (ex : http://myhomepage.com:3000/oauth )
41 +
42 +##
43 +
44 +## profile property
45 +
46 +profile에는 아래의 property들이 설정되어 넘겨진다.
47 +
48 +| key | value | 비고 |
49 +| -------- | ------ | ------------------------------------------ |
50 +| provider | String | kakao 고정 |
51 +| id | Number | 사용자의 kakao id |
52 +| \_raw | String | 사용자 정보 조회로 얻어진 json string |
53 +| \_json | Object | 사용자 정보 조회로 얻어진 json 원본 데이터 |
54 +
55 +## simple sample
56 +
57 +### 설치 & 실행
58 +
59 +1. `./sample/sample.js``appKey` 를 https://developers.kakao.com 에서 발급받은 JS appKey 값으로 셋팅.
60 +2. command line 에서 아래의 커맨드 실행
61 +3. 브라우져를 열고 `127.0.0.1:3000/login` 을 입력 후 이후 과정을 진행한다.
62 +
63 +```
64 +cd ./sample
65 +npm install
66 +node app
67 +```
68 +
69 +## mean.io 와 쉽게 연동하기
70 +
71 +수정해야하는 파일들은 아래와 같다.
72 +
73 +| file path | 설명 |
74 +| -------------------------------- | ------------------------------ |
75 +| server/config/env/development.js | 개발환경 설정파일 |
76 +| server/config/env/production.js | 운영환경 설정파일 |
77 +| server/config/models/user.js | 사용자 모델 |
78 +| server/config/passport.js | passport script |
79 +| server/routes/users.js | 사용자 로그인 관련 routes file |
80 +| public/auth/views/index.html | 로그인 화면 |
81 +
82 +(1) **mean.io app을 생성** 한다. (ex : mean init kakaoTest)
83 +
84 +(2) 해당 모듈을 연동할 mean.io app에 설치한다.(npm install passport-kakao --save)
85 +
86 +(3) **server/config/env/development.js****production.js** 에 kakao 관련 설정을 아래와 같이 추가한다.
87 +
88 +```javascript
89 +'use strict'
90 +
91 +module.exports = {
92 + db: 'mongodb',
93 + app: {
94 + name: 'passport-kakao',
95 + },
96 + // 그외 설정들....,
97 + kakao: {
98 + clientID: 'kakao app rest api key',
99 + callbackURL: 'http://localhost:3000/oauth',
100 + },
101 +}
102 +```
103 +
104 +(4) **server/config/models/users.js** 의 사용자 스키마 정의에 **kakao: {}** 를 추가한다.
105 +
106 +(5) **server/config/passport.js** 파일에 아래 구문을 추가한다.
107 +
108 +```javascript
109 +// 최상단 require되는 구문에 추가
110 +var KakaoStrategy = require('passport-kakao').Strategy
111 +
112 +passport.use(
113 + new KakaoStrategy(
114 + {
115 + clientID: config.kakao.clientID,
116 + callbackURL: config.kakao.callbackURL,
117 + },
118 + function(accessToken, refreshToken, profile, done) {
119 + User.findOne(
120 + {
121 + 'kakao.id': profile.id,
122 + },
123 + function(err, user) {
124 + if (err) {
125 + return done(err)
126 + }
127 + if (!user) {
128 + user = new User({
129 + name: profile.username,
130 + username: profile.id,
131 + roles: ['authenticated'],
132 + provider: 'kakao',
133 + kakao: profile._json,
134 + })
135 +
136 + user.save(function(err) {
137 + if (err) {
138 + console.log(err)
139 + }
140 + return done(err, user)
141 + })
142 + } else {
143 + return done(err, user)
144 + }
145 + }
146 + )
147 + }
148 + )
149 +)
150 +```
151 +
152 +(6) **server/routes/users.js** 에 아래와 같은 구문을 추가한다.
153 +
154 +```javascript
155 +app.get(
156 + '/auth/kakao',
157 + passport.authenticate('kakao', {
158 + failureRedirect: '#!/login',
159 + }),
160 + users.signin
161 +)
162 +
163 +app.get(
164 + '/oauth',
165 + passport.authenticate('kakao', {
166 + failureRedirect: '#!/login',
167 + }),
168 + users.authCallback
169 +)
170 +```
171 +
172 +(7) **public/auth/views/index.html** 에 kakao login을 연결한다.
173 +
174 +```html
175 +<!-- 아래는 예시 -->
176 +<div>
177 + <div class="row">
178 + <div class="col-md-offset-1 col-md-5">
179 + <a href="/auth/facebook">
180 + <img src="/public/auth/assets/img/icons/facebook.png" />
181 + </a>
182 + <a href="/auth/twitter">
183 + <img src="/public/auth/assets/img/icons/twitter.png" />
184 + </a>
185 +
186 + <!-- kakao login -->
187 + <a href="/auth/kakao">
188 + <img
189 + src="https://developers.kakao.com/assets/img/about/logos/kakaolink/kakaolink_btn_medium.png"
190 + />
191 + </a>
192 + </div>
193 + </div>
194 + <div class="col-md-6">
195 + <div ui-view></div>
196 + </div>
197 +</div>
198 +```
199 +
200 +(8) grunt로 mean.io app 실행 후, 실제 로그인 연동 테스트를 해본다.
201 +
202 +## 기타
203 +
204 +passport-oauth 모듈과 passport-facebook 모듈을 참고함.
1 +"use strict";
2 +var __importDefault = (this && this.__importDefault) || function (mod) {
3 + return (mod && mod.__esModule) ? mod : { "default": mod };
4 +};
5 +Object.defineProperty(exports, "__esModule", { value: true });
6 +var util_1 = require("util");
7 +var passport_oauth2_1 = __importDefault(require("passport-oauth2"));
8 +var DEFAULT_CLIENT_SECRET = 'kakao';
9 +var OAUTH_HOST = 'https://kauth.kakao.com';
10 +var USER_PROFILE_URL = 'https://kapi.kakao.com/v2/user/me';
11 +exports.buildOptions = function (options) {
12 + options.authorizationURL = OAUTH_HOST + "/oauth/authorize";
13 + options.tokenURL = OAUTH_HOST + "/oauth/token";
14 + if (!options.clientSecret) {
15 + options.clientSecret = DEFAULT_CLIENT_SECRET;
16 + }
17 + options.scopeSeparator = options.scopeSeparator || ',';
18 + options.customHeaders = options.customHeaders || {};
19 + if (!options.customHeaders['User-Agent']) {
20 + options.customHeaders['User-Agent'] = options.userAgent || 'passport-kakao';
21 + }
22 + return options;
23 +};
24 +/**
25 + * KaKaoStrategy 생성자 함수.<br/>
26 + * @param options.clientID 필수. kakao rest app key.
27 + * @param options.callbackURL 필수. 로그인 처리 후 호출할 URL
28 + * @param verify
29 + * @constructor
30 + */
31 +function Strategy(options, verify) {
32 + if (options === void 0) { options = {}; }
33 + passport_oauth2_1.default.call(this, exports.buildOptions(options), verify);
34 + this.name = 'kakao';
35 + this._userProfileURL = USER_PROFILE_URL;
36 +}
37 +/**
38 + * `OAuth2Stragegy`를 상속 받는다.
39 + */
40 +util_1.inherits(Strategy, passport_oauth2_1.default);
41 +/**
42 + * kakao 사용자 정보를 얻는다.<br/>
43 + * 사용자 정보를 성공적으로 조회하면 아래의 object가 done 콜백함수 호출과 함꼐 넘어간다.
44 + *
45 + * - `provider` kakao 고정
46 + * - `id` kakao user id number
47 + * - `username` 사용자의 kakao nickname
48 + * - `_raw` json string 원문
49 + * _ `_json` json 원 데이터
50 + *
51 + * @param {String} accessToken
52 + * @param {Function} done
53 + */
54 +Strategy.prototype.userProfile = function (accessToken, done) {
55 + this._oauth2.get(this._userProfileURL, accessToken, function (err, body) {
56 + if (err) {
57 + return done(err);
58 + }
59 + try {
60 + var json = JSON.parse(body);
61 + // 카카오톡이나 카카오스토리에 연동한 적이 없는 계정의 경우
62 + // properties가 비어있다고 한다. 없을 경우의 처리
63 + var properties = json.properties || {
64 + nickname: '미연동 계정',
65 + };
66 + var profile = {
67 + provider: 'kakao',
68 + id: json.id,
69 + username: properties.nickname,
70 + displayName: properties.nickname,
71 + _raw: body,
72 + _json: json,
73 + };
74 + return done(null, profile);
75 + }
76 + catch (e) {
77 + return done(e);
78 + }
79 + });
80 +};
81 +exports.default = Strategy;
1 +"use strict";
2 +var __importDefault = (this && this.__importDefault) || function (mod) {
3 + return (mod && mod.__esModule) ? mod : { "default": mod };
4 +};
5 +Object.defineProperty(exports, "__esModule", { value: true });
6 +var Strategy_1 = __importDefault(require("./Strategy"));
7 +exports.Strategy = Strategy_1.default;
8 +exports.default = Strategy_1.default;
1 +"use strict";
2 +Object.defineProperty(exports, "__esModule", { value: true });
1 +(The MIT License)
2 +
3 +Copyright (c) 2011-2014 Jared Hanson
4 +
5 +Permission is hereby granted, free of charge, to any person obtaining a copy of
6 +this software and associated documentation files (the "Software"), to deal in
7 +the Software without restriction, including without limitation the rights to
8 +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 +the Software, and to permit persons to whom the Software is furnished to do so,
10 +subject to the following conditions:
11 +
12 +The above copyright notice and this permission notice shall be included in all
13 +copies or substantial portions of the Software.
14 +
15 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1 +# passport-oauth2
2 +
3 +[![Build](https://travis-ci.org/jaredhanson/passport-oauth2.png)](https://travis-ci.org/jaredhanson/passport-oauth2)
4 +[![Coverage](https://coveralls.io/repos/jaredhanson/passport-oauth2/badge.png)](https://coveralls.io/r/jaredhanson/passport-oauth2)
5 +[![Quality](https://codeclimate.com/github/jaredhanson/passport-oauth2.png)](https://codeclimate.com/github/jaredhanson/passport-oauth2)
6 +[![Dependencies](https://david-dm.org/jaredhanson/passport-oauth2.png)](https://david-dm.org/jaredhanson/passport-oauth2)
7 +[![Tips](http://img.shields.io/gittip/jaredhanson.png)](https://www.gittip.com/jaredhanson/)
8 +
9 +General-purpose OAuth 2.0 authentication strategy for [Passport](http://passportjs.org/).
10 +
11 +This module lets you authenticate using OAuth 2.0 in your Node.js applications.
12 +By plugging into Passport, OAuth 2.0 authentication can be easily and
13 +unobtrusively integrated into any application or framework that supports
14 +[Connect](http://www.senchalabs.org/connect/)-style middleware, including
15 +[Express](http://expressjs.com/).
16 +
17 +Note that this strategy provides generic OAuth 2.0 support. In many cases, a
18 +provider-specific strategy can be used instead, which cuts down on unnecessary
19 +configuration, and accommodates any provider-specific quirks. See the
20 +[list](https://github.com/jaredhanson/passport/wiki/Strategies) for supported
21 +providers.
22 +
23 +Developers who need to implement authentication against an OAuth 2.0 provider
24 +that is not already supported are encouraged to sub-class this strategy. If you
25 +choose to open source the new provider-specific strategy, please add it to the
26 +list so other people can find it.
27 +
28 +## Install
29 +
30 + $ npm install passport-oauth2
31 +
32 +## Usage
33 +
34 +#### Configure Strategy
35 +
36 +The OAuth 2.0 authentication strategy authenticates users using a third-party
37 +account and OAuth 2.0 tokens. The provider's OAuth 2.0 endpoints, as well as
38 +the client identifer and secret, are specified as options. The strategy
39 +requires a `verify` callback, which receives an access token and profile,
40 +and calls `done` providing a user.
41 +
42 + passport.use(new OAuth2Strategy({
43 + authorizationURL: 'https://www.example.com/oauth2/authorize',
44 + tokenURL: 'https://www.example.com/oauth2/token',
45 + clientID: EXAMPLE_CLIENT_ID,
46 + clientSecret: EXAMPLE_CLIENT_SECRET,
47 + callbackURL: "http://localhost:3000/auth/example/callback"
48 + },
49 + function(accessToken, refreshToken, profile, done) {
50 + User.findOrCreate({ exampleId: profile.id }, function (err, user) {
51 + return done(err, user);
52 + });
53 + }
54 + ));
55 +
56 +#### Authenticate Requests
57 +
58 +Use `passport.authenticate()`, specifying the `'oauth2'` strategy, to
59 +authenticate requests.
60 +
61 +For example, as route middleware in an [Express](http://expressjs.com/)
62 +application:
63 +
64 + app.get('/auth/example',
65 + passport.authenticate('oauth2'));
66 +
67 + app.get('/auth/example/callback',
68 + passport.authenticate('oauth2', { failureRedirect: '/login' }),
69 + function(req, res) {
70 + // Successful authentication, redirect home.
71 + res.redirect('/');
72 + });
73 +
74 +## Related Modules
75 +
76 +- [passport-oauth1](https://github.com/jaredhanson/passport-oauth1) — OAuth 1.0 authentication strategy
77 +- [passport-http-bearer](https://github.com/jaredhanson/passport-http-bearer) — Bearer token authentication strategy for APIs
78 +- [OAuth2orize](https://github.com/jaredhanson/oauth2orize) — OAuth 2.0 authorization server toolkit
79 +
80 +## Tests
81 +
82 + $ npm install
83 + $ npm test
84 +
85 +## Credits
86 +
87 + - [Jared Hanson](http://github.com/jaredhanson)
88 +
89 +## License
90 +
91 +[The MIT License](http://opensource.org/licenses/MIT)
92 +
93 +Copyright (c) 2011-2014 Jared Hanson <[http://jaredhanson.net/](http://jaredhanson.net/)>
1 +/**
2 + * `AuthorizationError` error.
3 + *
4 + * AuthorizationError represents an error in response to an authorization
5 + * request. For details, refer to RFC 6749, section 4.1.2.1.
6 + *
7 + * References:
8 + * - [The OAuth 2.0 Authorization Framework](http://tools.ietf.org/html/rfc6749)
9 + *
10 + * @constructor
11 + * @param {String} [message]
12 + * @param {String} [code]
13 + * @param {String} [uri]
14 + * @param {Number} [status]
15 + * @api public
16 + */
17 +function AuthorizationError(message, code, uri, status) {
18 + if (!status) {
19 + switch (code) {
20 + case 'access_denied': status = 403; break;
21 + case 'server_error': status = 502; break;
22 + case 'temporarily_unavailable': status = 503; break;
23 + }
24 + }
25 +
26 + Error.call(this);
27 + Error.captureStackTrace(this, arguments.callee);
28 + this.name = 'AuthorizationError';
29 + this.message = message;
30 + this.code = code || 'server_error';
31 + this.uri = uri;
32 + this.status = status || 500;
33 +}
34 +
35 +/**
36 + * Inherit from `Error`.
37 + */
38 +AuthorizationError.prototype.__proto__ = Error.prototype;
39 +
40 +
41 +/**
42 + * Expose `AuthorizationError`.
43 + */
44 +module.exports = AuthorizationError;
1 +/**
2 + * `InternalOAuthError` error.
3 + *
4 + * InternalOAuthError wraps errors generated by node-oauth. By wrapping these
5 + * objects, error messages can be formatted in a manner that aids in debugging
6 + * OAuth issues.
7 + *
8 + * @constructor
9 + * @param {String} [message]
10 + * @param {Object|Error} [err]
11 + * @api public
12 + */
13 +function InternalOAuthError(message, err) {
14 + Error.call(this);
15 + Error.captureStackTrace(this, arguments.callee);
16 + this.name = 'InternalOAuthError';
17 + this.message = message;
18 + this.oauthError = err;
19 +}
20 +
21 +/**
22 + * Inherit from `Error`.
23 + */
24 +InternalOAuthError.prototype.__proto__ = Error.prototype;
25 +
26 +/**
27 + * Returns a string representing the error.
28 + *
29 + * @return {String}
30 + * @api public
31 + */
32 +InternalOAuthError.prototype.toString = function() {
33 + var m = this.name;
34 + if (this.message) { m += ': ' + this.message; }
35 + if (this.oauthError) {
36 + if (this.oauthError instanceof Error) {
37 + m = this.oauthError.toString();
38 + } else if (this.oauthError.statusCode && this.oauthError.data) {
39 + m += ' (status: ' + this.oauthError.statusCode + ' data: ' + this.oauthError.data + ')';
40 + }
41 + }
42 + return m;
43 +};
44 +
45 +
46 +/**
47 + * Expose `InternalOAuthError`.
48 + */
49 +module.exports = InternalOAuthError;
1 +/**
2 + * `TokenError` error.
3 + *
4 + * TokenError represents an error received from a token endpoint. For details,
5 + * refer to RFC 6749, section 5.2.
6 + *
7 + * References:
8 + * - [The OAuth 2.0 Authorization Framework](http://tools.ietf.org/html/rfc6749)
9 + *
10 + * @constructor
11 + * @param {String} [message]
12 + * @param {String} [code]
13 + * @param {String} [uri]
14 + * @param {Number} [status]
15 + * @api public
16 + */
17 +function TokenError(message, code, uri, status) {
18 + Error.call(this);
19 + Error.captureStackTrace(this, arguments.callee);
20 + this.name = 'TokenError';
21 + this.message = message;
22 + this.code = code || 'invalid_request';
23 + this.uri = uri;
24 + this.status = status || 500;
25 +}
26 +
27 +/**
28 + * Inherit from `Error`.
29 + */
30 +TokenError.prototype.__proto__ = Error.prototype;
31 +
32 +
33 +/**
34 + * Expose `TokenError`.
35 + */
36 +module.exports = TokenError;
1 +/**
2 + * Module dependencies.
3 + */
4 +var Strategy = require('./strategy')
5 + , AuthorizationError = require('./errors/authorizationerror')
6 + , TokenError = require('./errors/tokenerror')
7 + , InternalOAuthError = require('./errors/internaloautherror');
8 +
9 +
10 +/**
11 + * Expose `Strategy` directly from package.
12 + */
13 +exports = module.exports = Strategy;
14 +
15 +/**
16 + * Export constructors.
17 + */
18 +exports.Strategy = Strategy;
19 +
20 +/**
21 + * Export errors.
22 + */
23 +exports.AuthorizationError = AuthorizationError;
24 +exports.TokenError = TokenError;
25 +exports.InternalOAuthError = InternalOAuthError;
1 +/**
2 + * Reconstructs the original URL of the request.
3 + *
4 + * This function builds a URL that corresponds the original URL requested by the
5 + * client, including the protocol (http or https) and host.
6 + *
7 + * If the request passed through any proxies that terminate SSL, the
8 + * `X-Forwarded-Proto` header is used to detect if the request was encrypted to
9 + * the proxy, assuming that the proxy has been flagged as trusted.
10 + *
11 + * @param {http.IncomingMessage} req
12 + * @param {Object} [options]
13 + * @return {String}
14 + * @api private
15 + */
16 +exports.originalURL = function(req, options) {
17 + options = options || {};
18 + var app = req.app;
19 + if (app && app.get && app.get('trust proxy')) {
20 + options.proxy = true;
21 + }
22 + var trustProxy = options.proxy;
23 +
24 + var proto = (req.headers['x-forwarded-proto'] || '').toLowerCase()
25 + , tls = req.connection.encrypted || (trustProxy && 'https' == proto.split(/\s*,\s*/)[0])
26 + , host = (trustProxy && req.headers['x-forwarded-host']) || req.headers.host
27 + , protocol = tls ? 'https' : 'http'
28 + , path = req.url || '';
29 + return protocol + '://' + host + path;
30 +};
1 +{
2 + "name": "passport-oauth2",
3 + "version": "1.1.2",
4 + "description": "OAuth 2.0 authentication strategy for Passport.",
5 + "keywords": [
6 + "passport",
7 + "auth",
8 + "authn",
9 + "authentication",
10 + "authz",
11 + "authorization",
12 + "oauth",
13 + "oauth2"
14 + ],
15 + "author": {
16 + "name": "Jared Hanson",
17 + "email": "jaredhanson@gmail.com",
18 + "url": "http://www.jaredhanson.net/"
19 + },
20 + "repository": {
21 + "type": "git",
22 + "url": "git://github.com/jaredhanson/passport-oauth2.git"
23 + },
24 + "bugs": {
25 + "url": "http://github.com/jaredhanson/passport-oauth2/issues"
26 + },
27 + "licenses": [
28 + {
29 + "type": "MIT",
30 + "url": "http://www.opensource.org/licenses/MIT"
31 + }
32 + ],
33 + "main": "./lib",
34 + "dependencies": {
35 + "passport-strategy": "1.x.x",
36 + "oauth": "0.9.x",
37 + "uid2": "0.0.x"
38 + },
39 + "devDependencies": {
40 + "mocha": "1.x.x",
41 + "chai": "1.x.x",
42 + "chai-passport-strategy": "0.2.x"
43 + },
44 + "engines": {
45 + "node": ">= 0.4.0"
46 + },
47 + "scripts": {
48 + "test": "node_modules/.bin/mocha --reporter spec --require test/bootstrap/node test/*.test.js test/**/*.test.js"
49 + }
50 +}
1 +{
2 + "name": "passport-kakao",
3 + "version": "1.0.1",
4 + "description": "kakao oauth2 login module",
5 + "main": "./dist/passport-kakao",
6 + "keywords": [
7 + "passport",
8 + "kakao",
9 + "kakaotalk",
10 + "oauth2"
11 + ],
12 + "repository": {
13 + "type": "git",
14 + "url": "git://github.com/rotoshine/passport-kakao.git"
15 + },
16 + "author": "rotoshine@gmail.com",
17 + "license": "MIT",
18 + "dependencies": {
19 + "pkginfo": "~0.3.0",
20 + "passport-oauth2": "~1.1.2"
21 + },
22 + "devDependencies": {
23 + "@types/chai": "^4.2.3",
24 + "@types/mocha": "^5.2.7",
25 + "@types/node": "^12.7.11",
26 + "@typescript-eslint/eslint-plugin": "^2.30.0",
27 + "@typescript-eslint/parser": "^2.30.0",
28 + "chai": "^4.2.0",
29 + "eslint": "^6.8.0",
30 + "mocha": "^6.2.1",
31 + "prettier": "^2.0.5",
32 + "rimraf": "^3.0.2",
33 + "ts-node": "^8.4.1",
34 + "tslint": "^5.20.0",
35 + "tslint-config-prettier": "^1.18.0",
36 + "typescript": "^3.6.3"
37 + },
38 + "scripts": {
39 + "test": "mocha -r node_modules/ts-node/register ./tests/**/*.spec.ts",
40 + "clean": "rimraf dist/*",
41 + "build": "npm run clean && npx tsc",
42 + "lint": "eslint -c .eslintrc.js src/**/*.ts",
43 + "format": "prettier --write \"src/*.{ts,tsx,json,md}\""
44 + }
45 +}
1 +## how to test
2 +
3 +- create `.env` file.
4 +- setup `API_KEY` and `CLIENT_SECRET_KEY`.
5 +- run `npm install & node sample.js`
This diff is collapsed. Click to expand it.
1 +{
2 + "name": "passport-kakao-sample",
3 + "repository": {
4 + "type": "git",
5 + "url": "git://github.com/rotoshine/passport-kakao.git"
6 + },
7 + "dependencies": {
8 + "dotenv": "^8.2.0",
9 + "express": ">= 4.4.5",
10 + "passport": ">= 0.0.0"
11 + }
12 +}
1 +require('dotenv').config()
2 +
3 +const passport = require('passport')
4 +const express = require('express')
5 +const KakaoStrategy = require('../dist/passport-kakao.js').Strategy
6 +
7 +const appKey = process.env.API_KEY
8 +const appSecret = process.env.CLIENT_SECRET_KEY
9 +
10 +// passport 에 Kakao Oauth 추가
11 +passport.use(
12 + new KakaoStrategy(
13 + {
14 + clientID: appKey,
15 + clientSecret: appSecret,
16 + callbackURL: 'http://localhost:3000/oauth',
17 + },
18 + function (accessToken, refreshToken, params, profile, done) {
19 + // authorization 에 성공했을때의 액션
20 + console.log(`accessToken : ${accessToken}`)
21 + console.log(`사용자 profile: ${JSON.stringify(profile._json)}`)
22 +
23 + save(accessToken, refreshToken, profile)
24 + return done(null, profile._json)
25 + }
26 + )
27 +)
28 +passport.serializeUser(function (user, done) {
29 + done(null, user)
30 +})
31 +passport.deserializeUser(function (obj, done) {
32 + done(null, obj)
33 +})
34 +
35 +// express 앱 설정
36 +var app = express()
37 +app.use(passport.initialize())
38 +app.get('/login', passport.authenticate('kakao', { state: 'myStateValue' }))
39 +app.get('/oauth', passport.authenticate('kakao'), function (req, res) {
40 + // 로그인 시작시 state 값을 받을 수 있음
41 + res.send('state :' + req.query.state)
42 +})
43 +app.listen(3000)
44 +console.log('> server start! ')
45 +
46 +// 사용자 구현 부분
47 +function save() {
48 + //save 로직 구현
49 +}
1 +import { inherits } from 'util'
2 +import OAuth2Strategy from 'passport-oauth2'
3 +
4 +import { StrategyOptions, Profile } from './types/models'
5 +
6 +const DEFAULT_CLIENT_SECRET = 'kakao'
7 +const OAUTH_HOST = 'https://kauth.kakao.com'
8 +const USER_PROFILE_URL = 'https://kapi.kakao.com/v2/user/me'
9 +
10 +export const buildOptions = (options: StrategyOptions) => {
11 + options.authorizationURL = `${OAUTH_HOST}/oauth/authorize`
12 + options.tokenURL = `${OAUTH_HOST}/oauth/token`
13 +
14 + if (!options.clientSecret) {
15 + options.clientSecret = DEFAULT_CLIENT_SECRET
16 + }
17 +
18 + options.scopeSeparator = options.scopeSeparator || ','
19 + options.customHeaders = options.customHeaders || {}
20 +
21 + if (!options.customHeaders['User-Agent']) {
22 + options.customHeaders['User-Agent'] = options.userAgent || 'passport-kakao'
23 + }
24 +
25 + return options
26 +}
27 +/**
28 + * KaKaoStrategy 생성자 함수.<br/>
29 + * @param options.clientID 필수. kakao rest app key.
30 + * @param options.callbackURL 필수. 로그인 처리 후 호출할 URL
31 + * @param verify
32 + * @constructor
33 + */
34 +function Strategy(options: StrategyOptions = {}, verify: any) {
35 + OAuth2Strategy.call(this, buildOptions(options), verify)
36 + this.name = 'kakao'
37 + this._userProfileURL = USER_PROFILE_URL
38 +}
39 +
40 +/**
41 + * `OAuth2Stragegy`를 상속 받는다.
42 + */
43 +inherits(Strategy, OAuth2Strategy)
44 +
45 +/**
46 + * kakao 사용자 정보를 얻는다.<br/>
47 + * 사용자 정보를 성공적으로 조회하면 아래의 object가 done 콜백함수 호출과 함꼐 넘어간다.
48 + *
49 + * - `provider` kakao 고정
50 + * - `id` kakao user id number
51 + * - `username` 사용자의 kakao nickname
52 + * - `_raw` json string 원문
53 + * _ `_json` json 원 데이터
54 + *
55 + * @param {String} accessToken
56 + * @param {Function} done
57 + */
58 +Strategy.prototype.userProfile = function (
59 + accessToken: string,
60 + done: (error: Error, profile?: Profile) => void
61 +) {
62 + this._oauth2.get(
63 + this._userProfileURL,
64 + accessToken,
65 + (err: Error, body: string) => {
66 + if (err) {
67 + return done(err)
68 + }
69 +
70 + try {
71 + const json = JSON.parse(body)
72 + // 카카오톡이나 카카오스토리에 연동한 적이 없는 계정의 경우
73 + // properties가 비어있다고 한다. 없을 경우의 처리
74 + const properties = json.properties || {
75 + nickname: '미연동 계정',
76 + }
77 + const profile: Profile = {
78 + provider: 'kakao',
79 + id: json.id,
80 + username: properties.nickname,
81 + displayName: properties.nickname,
82 + _raw: body,
83 + _json: json,
84 + }
85 + return done(null, profile)
86 + } catch (e) {
87 + return done(e)
88 + }
89 + }
90 + )
91 +}
92 +
93 +export default Strategy
1 +import Strategy from './Strategy'
2 +
3 +export default Strategy
4 +
5 +export { Strategy }
1 +export interface StrategyOptions {
2 + authorizationURL?: string
3 + tokenURL?: string
4 + clientSecret?: string
5 + scopeSeparator?: string
6 + customHeaders?: {
7 + 'User-Agent'?: string
8 + }
9 + userAgent?: string
10 +}
11 +
12 +export interface Profile {
13 + provider: 'kakao'
14 + id?: string | number
15 + username?: string
16 + displayName?: string
17 + _raw: string
18 + _json: string
19 +}
1 +import { expect } from 'chai'
2 +import KakaoStrategy, { buildOptions } from '../src/Strategy'
3 +
4 +describe('passport-kakao', () => {
5 + it('passport-kakao 객체가 제대로 생성이 되어 있어야 한다.', () => {
6 + expect(KakaoStrategy).to.not.equals(null)
7 + })
8 + it('Strategy option의 clientSecret 값이 없을 경우 default 값이 설정되어야 한다.', () => {
9 + const options = buildOptions({})
10 +
11 + expect(options).to.not.equals(null)
12 + expect(options.clientSecret).to.be.equals('kakao')
13 + expect(options.scopeSeparator).to.be.equals(',')
14 + expect(options.customHeaders['User-Agent']).to.be.equals('passport-kakao')
15 + })
16 + it('Strategy option의 User-Agent값이 있을 경우 customHeaders의 User-Agent가 해당 값으로 설정되어야 한다.', () => {
17 + const options = buildOptions({
18 + customHeaders: {
19 + 'User-Agent': 'HELLO ROTO',
20 + },
21 + })
22 + expect(options.customHeaders['User-Agent']).to.be.equals('HELLO ROTO')
23 + })
24 +})
1 +{
2 + "include": ["./src"],
3 + "compilerOptions": {
4 + /* Basic Options */
5 + // "incremental": true, /* Enable incremental compilation */
6 + "target": "es5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */,
7 + "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */,
8 + // "lib": [], /* Specify library files to be included in the compilation. */
9 + // "allowJs": true, /* Allow javascript files to be compiled. */
10 + // "checkJs": true, /* Report errors in .js files. */
11 + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
12 + // "declaration": true, /* Generates corresponding '.d.ts' file. */
13 + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
14 + // "sourceMap": true, /* Generates corresponding '.map' file. */
15 + // "outFile": "./", /* Concatenate and emit output to single file. */
16 + "outDir": "./dist" /* Redirect output structure to the directory. */,
17 + "rootDir": "./src" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */,
18 + // "composite": true, /* Enable project compilation */
19 + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
20 + // "removeComments": true, /* Do not emit comments to output. */
21 + // "noEmit": true, /* Do not emit outputs. */
22 + // "importHelpers": true, /* Import emit helpers from 'tslib'. */
23 + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
24 + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
25 +
26 + /* Strict Type-Checking Options */
27 + "strict": false /* Enable all strict type-checking options. */,
28 + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
29 + // "strictNullChecks": true, /* Enable strict null checks. */
30 + // "strictFunctionTypes": true, /* Enable strict checking of function types. */
31 + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
32 + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
33 + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
34 + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
35 +
36 + /* Additional Checks */
37 + // "noUnusedLocals": true, /* Report errors on unused locals. */
38 + // "noUnusedParameters": true, /* Report errors on unused parameters. */
39 + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
40 + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
41 +
42 + /* Module Resolution Options */
43 + // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
44 + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
45 + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
46 + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
47 + // "typeRoots": [], /* List of folders to include type definitions from. */
48 + // "types": [], /* Type declaration files to be included in compilation. */
49 + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
50 + "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
51 + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
52 + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
53 +
54 + /* Source Map Options */
55 + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
56 + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
57 + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
58 + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
59 +
60 + /* Experimental Options */
61 + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
62 + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
63 + }
64 +}
1 +node_modules/
2 +npm-debug.log
...\ No newline at end of file ...\ No newline at end of file
1 +Copyright (c) 2010 Charlie Robbins.
2 +
3 +Permission is hereby granted, free of charge, to any person obtaining a copy
4 +of this software and associated documentation files (the "Software"), to deal
5 +in the Software without restriction, including without limitation the rights
6 +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 +copies of the Software, and to permit persons to whom the Software is
8 +furnished to do so, subject to the following conditions:
9 +
10 +The above copyright notice and this permission notice shall be included in
11 +all copies or substantial portions of the Software.
12 +
13 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 +THE SOFTWARE.
...\ No newline at end of file ...\ No newline at end of file
1 +# node-pkginfo
2 +
3 +An easy way to expose properties on a module from a package.json
4 +
5 +## Installation
6 +
7 +### Installing npm (node package manager)
8 +```
9 + curl http://npmjs.org/install.sh | sh
10 +```
11 +
12 +### Installing pkginfo
13 +```
14 + [sudo] npm install pkginfo
15 +```
16 +
17 +## Motivation
18 +How often when writing node.js modules have you written the following line(s) of code?
19 +
20 +* Hard code your version string into your code
21 +
22 +``` js
23 + exports.version = '0.1.0';
24 +```
25 +
26 +* Programmatically expose the version from the package.json
27 +
28 +``` js
29 + exports.version = require('/path/to/package.json').version;
30 +```
31 +
32 +In other words, how often have you wanted to expose basic information from your package.json onto your module programmatically? **WELL NOW YOU CAN!**
33 +
34 +## Usage
35 +
36 +Using `pkginfo` is idiot-proof, just require and invoke it.
37 +
38 +``` js
39 + var pkginfo = require('pkginfo')(module);
40 +
41 + console.dir(module.exports);
42 +```
43 +
44 +By invoking the `pkginfo` module all of the properties in your `package.json` file will be automatically exposed on the callee module (i.e. the parent module of `pkginfo`).
45 +
46 +Here's a sample of the output:
47 +
48 +```
49 + { name: 'simple-app',
50 + description: 'A test fixture for pkginfo',
51 + version: '0.1.0',
52 + author: 'Charlie Robbins <charlie.robbins@gmail.com>',
53 + keywords: [ 'test', 'fixture' ],
54 + main: './index.js',
55 + scripts: { test: 'vows test/*-test.js --spec' },
56 + engines: { node: '>= 0.4.0' } }
57 +```
58 +
59 +### Expose specific properties
60 +If you don't want to expose **all** properties on from your `package.json` on your module then simple pass those properties to the `pkginfo` function:
61 +
62 +``` js
63 + var pkginfo = require('pkginfo')(module, 'version', 'author');
64 +
65 + console.dir(module.exports);
66 +```
67 +
68 +```
69 + { version: '0.1.0',
70 + author: 'Charlie Robbins <charlie.robbins@gmail.com>' }
71 +```
72 +
73 +If you're looking for further usage see the [examples][0] included in this repository.
74 +
75 +## Run Tests
76 +Tests are written in [vows][1] and give complete coverage of all APIs.
77 +
78 +```
79 + vows test/*-test.js --spec
80 +```
81 +
82 +[0]: https://github.com/indexzero/node-pkginfo/tree/master/examples
83 +[1]: http://vowsjs.org
84 +
85 +#### Author: [Charlie Robbins](http://nodejitsu.com)
86 +#### License: MIT
1 +/*--------------------- Layout and Typography ----------------------------*/
2 +body {
3 + font-family: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif;
4 + font-size: 15px;
5 + line-height: 22px;
6 + color: #252519;
7 + margin: 0; padding: 0;
8 +}
9 +a {
10 + color: #261a3b;
11 +}
12 + a:visited {
13 + color: #261a3b;
14 + }
15 +p {
16 + margin: 0 0 15px 0;
17 +}
18 +h4, h5, h6 {
19 + color: #333;
20 + margin: 6px 0 6px 0;
21 + font-size: 13px;
22 +}
23 + h2, h3 {
24 + margin-bottom: 0;
25 + color: #000;
26 + }
27 + h1 {
28 + margin-top: 40px;
29 + margin-bottom: 15px;
30 + color: #000;
31 + }
32 +#container {
33 + position: relative;
34 +}
35 +#background {
36 + position: fixed;
37 + top: 0; left: 525px; right: 0; bottom: 0;
38 + background: #f5f5ff;
39 + border-left: 1px solid #e5e5ee;
40 + z-index: -1;
41 +}
42 +#jump_to, #jump_page {
43 + background: white;
44 + -webkit-box-shadow: 0 0 25px #777; -moz-box-shadow: 0 0 25px #777;
45 + -webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomleft: 5px;
46 + font: 10px Arial;
47 + text-transform: uppercase;
48 + cursor: pointer;
49 + text-align: right;
50 +}
51 +#jump_to, #jump_wrapper {
52 + position: fixed;
53 + right: 0; top: 0;
54 + padding: 5px 10px;
55 +}
56 + #jump_wrapper {
57 + padding: 0;
58 + display: none;
59 + }
60 + #jump_to:hover #jump_wrapper {
61 + display: block;
62 + }
63 + #jump_page {
64 + padding: 5px 0 3px;
65 + margin: 0 0 25px 25px;
66 + }
67 + #jump_page .source {
68 + display: block;
69 + padding: 5px 10px;
70 + text-decoration: none;
71 + border-top: 1px solid #eee;
72 + }
73 + #jump_page .source:hover {
74 + background: #f5f5ff;
75 + }
76 + #jump_page .source:first-child {
77 + }
78 +table td {
79 + border: 0;
80 + outline: 0;
81 +}
82 + td.docs, th.docs {
83 + max-width: 450px;
84 + min-width: 450px;
85 + min-height: 5px;
86 + padding: 10px 25px 1px 50px;
87 + overflow-x: hidden;
88 + vertical-align: top;
89 + text-align: left;
90 + }
91 + .docs pre {
92 + margin: 15px 0 15px;
93 + padding-left: 15px;
94 + }
95 + .docs p tt, .docs p code {
96 + background: #f8f8ff;
97 + border: 1px solid #dedede;
98 + font-size: 12px;
99 + padding: 0 0.2em;
100 + }
101 + .pilwrap {
102 + position: relative;
103 + }
104 + .pilcrow {
105 + font: 12px Arial;
106 + text-decoration: none;
107 + color: #454545;
108 + position: absolute;
109 + top: 3px; left: -20px;
110 + padding: 1px 2px;
111 + opacity: 0;
112 + -webkit-transition: opacity 0.2s linear;
113 + }
114 + td.docs:hover .pilcrow {
115 + opacity: 1;
116 + }
117 + td.code, th.code {
118 + padding: 14px 15px 16px 25px;
119 + width: 100%;
120 + vertical-align: top;
121 + background: #f5f5ff;
122 + border-left: 1px solid #e5e5ee;
123 + }
124 + pre, tt, code {
125 + font-size: 12px; line-height: 18px;
126 + font-family: Menlo, Monaco, Consolas, "Lucida Console", monospace;
127 + margin: 0; padding: 0;
128 + }
129 +
130 +
131 +/*---------------------- Syntax Highlighting -----------------------------*/
132 +td.linenos { background-color: #f0f0f0; padding-right: 10px; }
133 +span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; }
134 +body .hll { background-color: #ffffcc }
135 +body .c { color: #408080; font-style: italic } /* Comment */
136 +body .err { border: 1px solid #FF0000 } /* Error */
137 +body .k { color: #954121 } /* Keyword */
138 +body .o { color: #666666 } /* Operator */
139 +body .cm { color: #408080; font-style: italic } /* Comment.Multiline */
140 +body .cp { color: #BC7A00 } /* Comment.Preproc */
141 +body .c1 { color: #408080; font-style: italic } /* Comment.Single */
142 +body .cs { color: #408080; font-style: italic } /* Comment.Special */
143 +body .gd { color: #A00000 } /* Generic.Deleted */
144 +body .ge { font-style: italic } /* Generic.Emph */
145 +body .gr { color: #FF0000 } /* Generic.Error */
146 +body .gh { color: #000080; font-weight: bold } /* Generic.Heading */
147 +body .gi { color: #00A000 } /* Generic.Inserted */
148 +body .go { color: #808080 } /* Generic.Output */
149 +body .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
150 +body .gs { font-weight: bold } /* Generic.Strong */
151 +body .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
152 +body .gt { color: #0040D0 } /* Generic.Traceback */
153 +body .kc { color: #954121 } /* Keyword.Constant */
154 +body .kd { color: #954121; font-weight: bold } /* Keyword.Declaration */
155 +body .kn { color: #954121; font-weight: bold } /* Keyword.Namespace */
156 +body .kp { color: #954121 } /* Keyword.Pseudo */
157 +body .kr { color: #954121; font-weight: bold } /* Keyword.Reserved */
158 +body .kt { color: #B00040 } /* Keyword.Type */
159 +body .m { color: #666666 } /* Literal.Number */
160 +body .s { color: #219161 } /* Literal.String */
161 +body .na { color: #7D9029 } /* Name.Attribute */
162 +body .nb { color: #954121 } /* Name.Builtin */
163 +body .nc { color: #0000FF; font-weight: bold } /* Name.Class */
164 +body .no { color: #880000 } /* Name.Constant */
165 +body .nd { color: #AA22FF } /* Name.Decorator */
166 +body .ni { color: #999999; font-weight: bold } /* Name.Entity */
167 +body .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
168 +body .nf { color: #0000FF } /* Name.Function */
169 +body .nl { color: #A0A000 } /* Name.Label */
170 +body .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
171 +body .nt { color: #954121; font-weight: bold } /* Name.Tag */
172 +body .nv { color: #19469D } /* Name.Variable */
173 +body .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
174 +body .w { color: #bbbbbb } /* Text.Whitespace */
175 +body .mf { color: #666666 } /* Literal.Number.Float */
176 +body .mh { color: #666666 } /* Literal.Number.Hex */
177 +body .mi { color: #666666 } /* Literal.Number.Integer */
178 +body .mo { color: #666666 } /* Literal.Number.Oct */
179 +body .sb { color: #219161 } /* Literal.String.Backtick */
180 +body .sc { color: #219161 } /* Literal.String.Char */
181 +body .sd { color: #219161; font-style: italic } /* Literal.String.Doc */
182 +body .s2 { color: #219161 } /* Literal.String.Double */
183 +body .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
184 +body .sh { color: #219161 } /* Literal.String.Heredoc */
185 +body .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
186 +body .sx { color: #954121 } /* Literal.String.Other */
187 +body .sr { color: #BB6688 } /* Literal.String.Regex */
188 +body .s1 { color: #219161 } /* Literal.String.Single */
189 +body .ss { color: #19469D } /* Literal.String.Symbol */
190 +body .bp { color: #954121 } /* Name.Builtin.Pseudo */
191 +body .vc { color: #19469D } /* Name.Variable.Class */
192 +body .vg { color: #19469D } /* Name.Variable.Global */
193 +body .vi { color: #19469D } /* Name.Variable.Instance */
194 +body .il { color: #666666 } /* Literal.Number.Integer.Long */
...\ No newline at end of file ...\ No newline at end of file
This diff is collapsed. Click to expand it.
1 +/*
2 + * all-properties.js: Sample of including all properties from a package.json file
3 + *
4 + * (C) 2011, Charlie Robbins
5 + *
6 + */
7 +
8 +var util = require('util'),
9 + pkginfo = require('../lib/pkginfo')(module);
10 +
11 +exports.someFunction = function () {
12 + console.log('some of your custom logic here');
13 +};
14 +
15 +console.log('Inspecting module:');
16 +console.dir(module.exports);
17 +
18 +console.log('\nAll exports exposed:');
19 +console.error(Object.keys(module.exports));
...\ No newline at end of file ...\ No newline at end of file
1 +/*
2 + * array-argument.js: Sample of including specific properties from a package.json file
3 + * using Array argument syntax.
4 + *
5 + * (C) 2011, Charlie Robbins
6 + *
7 + */
8 +
9 +var util = require('util'),
10 + pkginfo = require('../lib/pkginfo')(module, ['version', 'author']);
11 +
12 +exports.someFunction = function () {
13 + console.log('some of your custom logic here');
14 +};
15 +
16 +console.log('Inspecting module:');
17 +console.dir(module.exports);
18 +
19 +console.log('\nAll exports exposed:');
20 +console.error(Object.keys(module.exports));
...\ No newline at end of file ...\ No newline at end of file
1 +/*
2 + * multiple-properties.js: Sample of including multiple properties from a package.json file
3 + *
4 + * (C) 2011, Charlie Robbins
5 + *
6 + */
7 +
8 +var util = require('util'),
9 + pkginfo = require('../lib/pkginfo')(module, 'version', 'author');
10 +
11 +exports.someFunction = function () {
12 + console.log('some of your custom logic here');
13 +};
14 +
15 +console.log('Inspecting module:');
16 +console.dir(module.exports);
17 +
18 +console.log('\nAll exports exposed:');
19 +console.error(Object.keys(module.exports));
...\ No newline at end of file ...\ No newline at end of file
1 +/*
2 + * object-argument.js: Sample of including specific properties from a package.json file
3 + * using Object argument syntax.
4 + *
5 + * (C) 2011, Charlie Robbins
6 + *
7 + */
8 +
9 +var util = require('util'),
10 + pkginfo = require('../lib/pkginfo')(module, {
11 + include: ['version', 'author']
12 + });
13 +
14 +exports.someFunction = function () {
15 + console.log('some of your custom logic here');
16 +};
17 +
18 +console.log('Inspecting module:');
19 +console.dir(module.exports);
20 +
21 +console.log('\nAll exports exposed:');
22 +console.error(Object.keys(module.exports));
...\ No newline at end of file ...\ No newline at end of file
1 +{
2 + "name": "simple-app",
3 + "description": "A test fixture for pkginfo",
4 + "version": "0.1.0",
5 + "author": "Charlie Robbins <charlie.robbins@gmail.com>",
6 + "keywords": ["test", "fixture"],
7 + "main": "./index.js",
8 + "scripts": { "test": "vows test/*-test.js --spec" },
9 + "engines": { "node": ">= 0.4.0" }
10 +}
1 +/*
2 + * single-property.js: Sample of including a single specific properties from a package.json file
3 + *
4 + * (C) 2011, Charlie Robbins
5 + *
6 + */
7 +
8 +var util = require('util'),
9 + pkginfo = require('../lib/pkginfo')(module, 'version');
10 +
11 +exports.someFunction = function () {
12 + console.log('some of your custom logic here');
13 +};
14 +
15 +console.log('Inspecting module:');
16 +console.dir(module.exports);
17 +
18 +console.log('\nAll exports exposed:');
19 +console.error(Object.keys(module.exports));
...\ No newline at end of file ...\ No newline at end of file
1 +{
2 + "name": "simple-app-subdir",
3 + "description": "A test fixture for pkginfo",
4 + "version": "0.1.0",
5 + "author": "Charlie Robbins <charlie.robbins@gmail.com>",
6 + "keywords": ["test", "fixture"],
7 + "main": "./index.js",
8 + "scripts": { "test": "vows test/*-test.js --spec" },
9 + "engines": { "node": ">= 0.4.0" },
10 + "subdironly": "true"
11 +}
1 +/*
2 + * multiple-properties.js: Sample of including multiple properties from a package.json file
3 + *
4 + * (C) 2011, Charlie Robbins
5 + *
6 + */
7 +
8 +var util = require('util'),
9 + path = require('path'),
10 + pkginfo = require('../lib/pkginfo')(module, { dir: path.resolve(__dirname, 'subdir' )});
11 +
12 +exports.someFunction = function () {
13 + console.log('some of your custom logic here');
14 +};
15 +
16 +console.log('Inspecting module:');
17 +console.dir(module.exports);
18 +
19 +console.log('\nAll exports exposed:');
20 +console.error(Object.keys(module.exports));
...\ No newline at end of file ...\ No newline at end of file
1 +/*
2 + * pkginfo.js: Top-level include for the pkginfo module
3 + *
4 + * (C) 2011, Charlie Robbins
5 + *
6 + */
7 +
8 +var fs = require('fs'),
9 + path = require('path');
10 +
11 +//
12 +// ### function pkginfo ([options, 'property', 'property' ..])
13 +// #### @pmodule {Module} Parent module to read from.
14 +// #### @options {Object|Array|string} **Optional** Options used when exposing properties.
15 +// #### @arguments {string...} **Optional** Specified properties to expose.
16 +// Exposes properties from the package.json file for the parent module on
17 +// it's exports. Valid usage:
18 +//
19 +// `require('pkginfo')()`
20 +//
21 +// `require('pkginfo')('version', 'author');`
22 +//
23 +// `require('pkginfo')(['version', 'author']);`
24 +//
25 +// `require('pkginfo')({ include: ['version', 'author'] });`
26 +//
27 +var pkginfo = module.exports = function (pmodule, options) {
28 + var args = [].slice.call(arguments, 2).filter(function (arg) {
29 + return typeof arg === 'string';
30 + });
31 +
32 + //
33 + // **Parse variable arguments**
34 + //
35 + if (Array.isArray(options)) {
36 + //
37 + // If the options passed in is an Array assume that
38 + // it is the Array of properties to expose from the
39 + // on the package.json file on the parent module.
40 + //
41 + options = { include: options };
42 + }
43 + else if (typeof options === 'string') {
44 + //
45 + // Otherwise if the first argument is a string, then
46 + // assume that it is the first property to expose from
47 + // the package.json file on the parent module.
48 + //
49 + options = { include: [options] };
50 + }
51 +
52 + //
53 + // **Setup default options**
54 + //
55 + options = options || {};
56 +
57 + // ensure that includes have been defined
58 + options.include = options.include || [];
59 +
60 + if (args.length > 0) {
61 + //
62 + // If additional string arguments have been passed in
63 + // then add them to the properties to expose on the
64 + // parent module.
65 + //
66 + options.include = options.include.concat(args);
67 + }
68 +
69 + var pkg = pkginfo.read(pmodule, options.dir).package;
70 + Object.keys(pkg).forEach(function (key) {
71 + if (options.include.length > 0 && !~options.include.indexOf(key)) {
72 + return;
73 + }
74 +
75 + if (!pmodule.exports[key]) {
76 + pmodule.exports[key] = pkg[key];
77 + }
78 + });
79 +
80 + return pkginfo;
81 +};
82 +
83 +//
84 +// ### function find (dir)
85 +// #### @pmodule {Module} Parent module to read from.
86 +// #### @dir {string} **Optional** Directory to start search from.
87 +// Searches up the directory tree from `dir` until it finds a directory
88 +// which contains a `package.json` file.
89 +//
90 +pkginfo.find = function (pmodule, dir) {
91 + if (! dir) {
92 + dir = path.dirname(pmodule.filename);
93 + }
94 +
95 + var files = fs.readdirSync(dir);
96 +
97 + if (~files.indexOf('package.json')) {
98 + return path.join(dir, 'package.json');
99 + }
100 +
101 + if (dir === '/') {
102 + throw new Error('Could not find package.json up from: ' + dir);
103 + }
104 + else if (!dir || dir === '.') {
105 + throw new Error('Cannot find package.json from unspecified directory');
106 + }
107 +
108 + return pkginfo.find(pmodule, path.dirname(dir));
109 +};
110 +
111 +//
112 +// ### function read (pmodule, dir)
113 +// #### @pmodule {Module} Parent module to read from.
114 +// #### @dir {string} **Optional** Directory to start search from.
115 +// Searches up the directory tree from `dir` until it finds a directory
116 +// which contains a `package.json` file and returns the package information.
117 +//
118 +pkginfo.read = function (pmodule, dir) {
119 + dir = pkginfo.find(pmodule, dir);
120 +
121 + var data = fs.readFileSync(dir).toString();
122 +
123 + return {
124 + dir: dir,
125 + package: JSON.parse(data)
126 + };
127 +};
128 +
129 +//
130 +// Call `pkginfo` on this module and expose version.
131 +//
132 +pkginfo(module, {
133 + dir: __dirname,
134 + include: ['version'],
135 + target: pkginfo
136 +});
...\ No newline at end of file ...\ No newline at end of file
1 +{
2 + "name": "pkginfo",
3 + "version": "0.3.1",
4 + "license": "MIT",
5 + "description": "An easy way to expose properties on a module from a package.json",
6 + "author": "Charlie Robbins <charlie.robbins@gmail.com>",
7 + "repository": {
8 + "type": "git",
9 + "url": "http://github.com/indexzero/node-pkginfo.git"
10 + },
11 + "bugs": {
12 + "url": "https://github.com/indexzero/node-pkginfo/issues"
13 + },
14 + "keywords": ["info", "tools", "package.json"],
15 + "devDependencies": {
16 + "vows": "0.7.x"
17 + },
18 + "main": "./lib/pkginfo.js",
19 + "scripts": { "test": "vows test/*-test.js --spec" },
20 + "engines": { "node": ">= 0.4.0" }
21 +}
1 +/*
2 + * pkginfo-test.js: Tests for the pkginfo module.
3 + *
4 + * (C) 2011, Charlie Robbins
5 + *
6 + */
7 +
8 +var assert = require('assert'),
9 + exec = require('child_process').exec,
10 + fs = require('fs'),
11 + path = require('path'),
12 + vows = require('vows'),
13 + pkginfo = require('../lib/pkginfo');
14 +
15 +function assertProperties (source, target) {
16 + assert.lengthOf(source, target.length + 1);
17 + target.forEach(function (prop) {
18 + assert.isTrue(!!~source.indexOf(prop));
19 + });
20 +}
21 +
22 +function compareWithExample(targetPath) {
23 + var examplePaths = ['package.json'];
24 +
25 + if (targetPath) {
26 + examplePaths.unshift(targetPath);
27 + }
28 +
29 + return function(exposed) {
30 + var pkg = fs.readFileSync(path.join.apply(null, [__dirname, '..', 'examples'].concat(examplePaths))).toString(),
31 + keys = Object.keys(JSON.parse(pkg));
32 +
33 + assertProperties(exposed, keys);
34 + };
35 +}
36 +
37 +function testExposes (options) {
38 + return {
39 + topic: function () {
40 + exec('node ' + path.join(__dirname, '..', 'examples', options.script), this.callback);
41 + },
42 + "should expose that property correctly": function (err, stdout, stderr) {
43 + assert.isNull(err);
44 +
45 + var exposed = stderr.match(/'(\w+)'/ig).map(function (p) {
46 + return p.substring(1, p.length - 1);
47 + });
48 +
49 + return !options.assert
50 + ? assertProperties(exposed, options.properties)
51 + : options.assert(exposed);
52 + }
53 + }
54 +}
55 +
56 +vows.describe('pkginfo').addBatch({
57 + "When using the pkginfo module": {
58 + "and passed a single `string` argument": testExposes({
59 + script: 'single-property.js',
60 + properties: ['version']
61 + }),
62 + "and passed multiple `string` arguments": testExposes({
63 + script: 'multiple-properties.js',
64 + properties: ['version', 'author']
65 + }),
66 + "and passed an `object` argument": testExposes({
67 + script: 'object-argument.js',
68 + properties: ['version', 'author']
69 + }),
70 + "and passed an `array` argument": testExposes({
71 + script: 'array-argument.js',
72 + properties: ['version', 'author']
73 + }),
74 + "and read from a specified directory": testExposes({
75 + script: 'target-dir.js',
76 + assert: compareWithExample('subdir')
77 + }),
78 + "and passed no arguments": testExposes({
79 + script: 'all-properties.js',
80 + assert: compareWithExample()
81 + })
82 + }
83 +}).export(module);
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
14 "express": "^4.17.1", 14 "express": "^4.17.1",
15 "express-flash": "^0.0.2", 15 "express-flash": "^0.0.2",
16 "fs": "^0.0.1-security", 16 "fs": "^0.0.1-security",
17 + "passport-kakao": "^1.0.1",
17 "passport-naver": "^1.0.6", 18 "passport-naver": "^1.0.6",
18 "path": "^0.12.7", 19 "path": "^0.12.7",
19 "request": "^2.88.2", 20 "request": "^2.88.2",
...@@ -1750,6 +1751,28 @@ ...@@ -1750,6 +1751,28 @@
1750 "node": ">= 0.4.0" 1751 "node": ">= 0.4.0"
1751 } 1752 }
1752 }, 1753 },
1754 + "node_modules/passport-kakao": {
1755 + "version": "1.0.1",
1756 + "resolved": "https://registry.npmjs.org/passport-kakao/-/passport-kakao-1.0.1.tgz",
1757 + "integrity": "sha512-uItaYRVrTHL6iGPMnMZvPa/O1GrAdh/V6EMjOHcFlQcVroZ9wgG7BZ5PonMNJCxfHQ3L2QVNRnzhKWUzSsumbw==",
1758 + "dependencies": {
1759 + "passport-oauth2": "~1.1.2",
1760 + "pkginfo": "~0.3.0"
1761 + }
1762 + },
1763 + "node_modules/passport-kakao/node_modules/passport-oauth2": {
1764 + "version": "1.1.2",
1765 + "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.1.2.tgz",
1766 + "integrity": "sha1-vXFjsbYJA3GGjcTvb58uHkzEuUg=",
1767 + "dependencies": {
1768 + "oauth": "0.9.x",
1769 + "passport-strategy": "1.x.x",
1770 + "uid2": "0.0.x"
1771 + },
1772 + "engines": {
1773 + "node": ">= 0.4.0"
1774 + }
1775 + },
1753 "node_modules/passport-local": { 1776 "node_modules/passport-local": {
1754 "version": "1.0.0", 1777 "version": "1.0.0",
1755 "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz", 1778 "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz",
...@@ -1864,6 +1887,14 @@ ...@@ -1864,6 +1887,14 @@
1864 "url": "https://github.com/sponsors/jonschlinkert" 1887 "url": "https://github.com/sponsors/jonschlinkert"
1865 } 1888 }
1866 }, 1889 },
1890 + "node_modules/pkginfo": {
1891 + "version": "0.3.1",
1892 + "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz",
1893 + "integrity": "sha1-Wyn2qB9wcXFC4J52W76rl7T4HiE=",
1894 + "engines": {
1895 + "node": ">= 0.4.0"
1896 + }
1897 + },
1867 "node_modules/prepend-http": { 1898 "node_modules/prepend-http": {
1868 "version": "2.0.0", 1899 "version": "2.0.0",
1869 "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", 1900 "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz",
...@@ -4017,6 +4048,27 @@ ...@@ -4017,6 +4048,27 @@
4017 "pause": "0.0.1" 4048 "pause": "0.0.1"
4018 } 4049 }
4019 }, 4050 },
4051 + "passport-kakao": {
4052 + "version": "1.0.1",
4053 + "resolved": "https://registry.npmjs.org/passport-kakao/-/passport-kakao-1.0.1.tgz",
4054 + "integrity": "sha512-uItaYRVrTHL6iGPMnMZvPa/O1GrAdh/V6EMjOHcFlQcVroZ9wgG7BZ5PonMNJCxfHQ3L2QVNRnzhKWUzSsumbw==",
4055 + "requires": {
4056 + "passport-oauth2": "~1.1.2",
4057 + "pkginfo": "~0.3.0"
4058 + },
4059 + "dependencies": {
4060 + "passport-oauth2": {
4061 + "version": "1.1.2",
4062 + "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.1.2.tgz",
4063 + "integrity": "sha1-vXFjsbYJA3GGjcTvb58uHkzEuUg=",
4064 + "requires": {
4065 + "oauth": "0.9.x",
4066 + "passport-strategy": "1.x.x",
4067 + "uid2": "0.0.x"
4068 + }
4069 + }
4070 + }
4071 + },
4020 "passport-local": { 4072 "passport-local": {
4021 "version": "1.0.0", 4073 "version": "1.0.0",
4022 "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz", 4074 "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz",
...@@ -4102,6 +4154,11 @@ ...@@ -4102,6 +4154,11 @@
4102 "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", 4154 "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==",
4103 "dev": true 4155 "dev": true
4104 }, 4156 },
4157 + "pkginfo": {
4158 + "version": "0.3.1",
4159 + "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz",
4160 + "integrity": "sha1-Wyn2qB9wcXFC4J52W76rl7T4HiE="
4161 + },
4105 "prepend-http": { 4162 "prepend-http": {
4106 "version": "2.0.0", 4163 "version": "2.0.0",
4107 "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", 4164 "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz",
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
19 "express": "^4.17.1", 19 "express": "^4.17.1",
20 "express-flash": "^0.0.2", 20 "express-flash": "^0.0.2",
21 "fs": "^0.0.1-security", 21 "fs": "^0.0.1-security",
22 + "passport-kakao": "^1.0.1",
22 "passport-naver": "^1.0.6", 23 "passport-naver": "^1.0.6",
23 "path": "^0.12.7", 24 "path": "^0.12.7",
24 "request": "^2.88.2", 25 "request": "^2.88.2",
......
1 -{"cookie":{"originalMaxAge":null,"expires":null,"httpOnly":true,"path":"/"},"flash":{"error":["Missing username or password."]},"__lastAccess":1638110555478}
...\ No newline at end of file ...\ No newline at end of file
1 +{"cookie":{"originalMaxAge":null,"expires":null,"httpOnly":true,"path":"/"},"flash":{"error":["Missing username or password."]},"__lastAccess":1638446277022}
...\ No newline at end of file ...\ No newline at end of file
......