├── .gitignore
├── README.md
├── plugin.json
├── package.json
├── templates
└── admin
│ └── plugins
│ └── sso-qq.tpl
├── .npmignore
└── library.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | .idea/
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # NodeBB Plugin SSO QQ
2 |
3 | NodeBB Plugin that allows users to login/register via their QQ account.
4 |
5 | ## Installation
6 |
7 | npm install nodebb-plugin-sso-qq
8 |
9 | Please config callback address to your nodebb url
10 |
--------------------------------------------------------------------------------
/plugin.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "nodebb-plugin-sso-qq",
3 | "name": "NodeBB QQ SSO",
4 | "description": "NodeBB Plugin that allows users to login/register via their QQ account.",
5 | "url": "https://github.com/pmmsc/nodebb-plugin-sso-qq",
6 | "library": "./library.js",
7 | "hooks": [
8 | {
9 | "hook": "filter:auth.init", "method": "getStrategy"
10 | },
11 | {
12 | "hook": "filter:admin.header.build", "method": "addMenuItem"
13 | },
14 | {
15 | "hook": "static:app.load", "method": "init"
16 | }
17 | ],
18 | "templates": "./templates"
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nodebb-plugin-sso-qq",
3 | "version": "0.1.0",
4 | "description": "NodeBB Plugin that allows users to login/register via their QQ account.",
5 | "main": "library.js",
6 | "repository": {
7 | "type": "git",
8 | "url": "https://github.com/pmmsc/nodebb-plugin-sso-qq"
9 | },
10 | "keywords": [
11 | "nodebb",
12 | "plugin",
13 | "qq",
14 | "sso",
15 | "single sign on",
16 | "registration"
17 | ],
18 | "author": {
19 | "name": "pmmsc",
20 | "email": "yanni_wen@pmmsc.org"
21 | },
22 | "contributors": [
23 | {
24 | "name": "Eric Miao",
25 | "email": "qjmiao@gmail.com",
26 | "url": "https://github.com/qjmiao/nodebb-plugin-sso-qq"
27 | }
28 | ],
29 | "license": "BSD-2-Clause",
30 | "bugs": {
31 | "url": "https://github.com/pmmsc/nodebb-plugin-sso-qq/issues"
32 | },
33 | "dependencies": {
34 | "passport-qq": "~0.0.3"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/templates/admin/plugins/sso-qq.tpl:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
QQ Social Authentication
5 |
6 |
7 | Register a new QQ Application via qq.com and then paste
8 | your application details here. Your callback URL is yourdomain.com/auth/qq/callback
9 |
10 |
11 |
21 |
22 |
23 |
24 |
25 |
26 |
QQ Control Panel
27 |
28 | Save Settings
29 |
30 |
31 |
32 |
33 |
34 |
53 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | #################
2 | ## Eclipse
3 | #################
4 |
5 | *.pydevproject
6 | .project
7 | .metadata
8 | bin/
9 | tmp/
10 | *.tmp
11 | *.bak
12 | *.swp
13 | *~.nib
14 | local.properties
15 | .classpath
16 | .settings/
17 | .loadpath
18 |
19 | # External tool builders
20 | .externalToolBuilders/
21 |
22 | # Locally stored "Eclipse launch configurations"
23 | *.launch
24 |
25 | # CDT-specific
26 | .cproject
27 |
28 | # PDT-specific
29 | .buildpath
30 |
31 |
32 | #################
33 | ## Visual Studio
34 | #################
35 |
36 | ## Ignore Visual Studio temporary files, build results, and
37 | ## files generated by popular Visual Studio add-ons.
38 |
39 | # User-specific files
40 | *.suo
41 | *.user
42 | *.sln.docstates
43 |
44 | # Build results
45 |
46 | [Dd]ebug/
47 | [Rr]elease/
48 | x64/
49 | build/
50 | [Bb]in/
51 | [Oo]bj/
52 |
53 | # MSTest test Results
54 | [Tt]est[Rr]esult*/
55 | [Bb]uild[Ll]og.*
56 |
57 | *_i.c
58 | *_p.c
59 | *.ilk
60 | *.meta
61 | *.obj
62 | *.pch
63 | *.pdb
64 | *.pgc
65 | *.pgd
66 | *.rsp
67 | *.sbr
68 | *.tlb
69 | *.tli
70 | *.tlh
71 | *.tmp
72 | *.tmp_proj
73 | *.log
74 | *.vspscc
75 | *.vssscc
76 | .builds
77 | *.pidb
78 | *.log
79 | *.scc
80 |
81 | # Visual C++ cache files
82 | ipch/
83 | *.aps
84 | *.ncb
85 | *.opensdf
86 | *.sdf
87 | *.cachefile
88 |
89 | # Visual Studio profiler
90 | *.psess
91 | *.vsp
92 | *.vspx
93 |
94 | # Guidance Automation Toolkit
95 | *.gpState
96 |
97 | # ReSharper is a .NET coding add-in
98 | _ReSharper*/
99 | *.[Rr]e[Ss]harper
100 |
101 | # TeamCity is a build add-in
102 | _TeamCity*
103 |
104 | # DotCover is a Code Coverage Tool
105 | *.dotCover
106 |
107 | # NCrunch
108 | *.ncrunch*
109 | .*crunch*.local.xml
110 |
111 | # Installshield output folder
112 | [Ee]xpress/
113 |
114 | # DocProject is a documentation generator add-in
115 | DocProject/buildhelp/
116 | DocProject/Help/*.HxT
117 | DocProject/Help/*.HxC
118 | DocProject/Help/*.hhc
119 | DocProject/Help/*.hhk
120 | DocProject/Help/*.hhp
121 | DocProject/Help/Html2
122 | DocProject/Help/html
123 |
124 | # Click-Once directory
125 | publish/
126 |
127 | # Publish Web Output
128 | *.Publish.xml
129 | *.pubxml
130 |
131 | # NuGet Packages Directory
132 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line
133 | #packages/
134 |
135 | # Windows Azure Build Output
136 | csx
137 | *.build.csdef
138 |
139 | # Windows Store app package directory
140 | AppPackages/
141 |
142 | # Others
143 | sql/
144 | *.Cache
145 | ClientBin/
146 | [Ss]tyle[Cc]op.*
147 | ~$*
148 | *~
149 | *.dbmdl
150 | *.[Pp]ublish.xml
151 | *.pfx
152 | *.publishsettings
153 |
154 | # RIA/Silverlight projects
155 | Generated_Code/
156 |
157 | # Backup & report files from converting an old project file to a newer
158 | # Visual Studio version. Backup files are not needed, because we have git ;-)
159 | _UpgradeReport_Files/
160 | Backup*/
161 | UpgradeLog*.XML
162 | UpgradeLog*.htm
163 |
164 | # SQL Server files
165 | App_Data/*.mdf
166 | App_Data/*.ldf
167 |
168 | #############
169 | ## Windows detritus
170 | #############
171 |
172 | # Windows image file caches
173 | Thumbs.db
174 | ehthumbs.db
175 |
176 | # Folder config file
177 | Desktop.ini
178 |
179 | # Recycle Bin used on file shares
180 | $RECYCLE.BIN/
181 |
182 | # Mac crap
183 | .DS_Store
184 |
185 |
186 | #############
187 | ## Python
188 | #############
189 |
190 | *.py[co]
191 |
192 | # Packages
193 | *.egg
194 | *.egg-info
195 | dist/
196 | build/
197 | eggs/
198 | parts/
199 | var/
200 | sdist/
201 | develop-eggs/
202 | .installed.cfg
203 |
204 | # Installer logs
205 | pip-log.txt
206 |
207 | # Unit test / coverage reports
208 | .coverage
209 | .tox
210 |
211 | #Translations
212 | *.mo
213 |
214 | #Mr Developer
215 | .mr.developer.cfg
216 |
217 | sftp-config.json
218 | node_modules/
219 | .idea
220 |
--------------------------------------------------------------------------------
/library.js:
--------------------------------------------------------------------------------
1 | (function(module) {
2 | "use strict";
3 |
4 | var User = module.parent.require('./user'),
5 | db = module.parent.require('./database'),
6 | meta = module.parent.require('./meta'),
7 | nconf = module.parent.require('nconf'),
8 | passport = module.parent.require('passport'),
9 | QQStrategy = require('passport-qq').Strategy;
10 |
11 | var constants = Object.freeze({
12 | 'name': "QQ",
13 | 'admin': {
14 | 'icon': 'fa-qq',
15 | 'route': '/plugins/sso-qq'
16 | }
17 | });
18 |
19 | var QQ = {};
20 |
21 | QQ.getStrategy = function(strategies, callback) {
22 | meta.settings.get('sso-qq', function(err, settings) {
23 | if (!err && settings.id && settings.secret) {
24 | passport.use(new QQStrategy({
25 | clientID: settings.id,
26 | clientSecret: settings.secret,
27 | callbackURL: nconf.get('url') + '/auth/qq/callback'
28 | }, function(token, tokenSecret, profile, done) {
29 | QQ.login(profile.id, profile.nickname, function(err, user) {
30 | if (err) {
31 | return done(err);
32 | }
33 | done(null, user);
34 | });
35 | }));
36 |
37 | strategies.push({
38 | name: 'qq',
39 | url: '/auth/qq',
40 | callbackURL: '/auth/qq/callback',
41 | icon: 'fa-qq',
42 | scope: 'get_user_info'
43 | });
44 | }
45 |
46 | callback(null, strategies);
47 | });
48 | };
49 |
50 | QQ.login = function(qqID, username, callback) {
51 | var email = username + '@users.noreply.qq.com';
52 |
53 | QQ.getUidByQQID(qqID, function(err, uid) {
54 | if (err) {
55 | return callback(err);
56 | }
57 |
58 | if (uid) {
59 | // Existing User
60 | callback(null, {
61 | uid: uid
62 | });
63 | } else {
64 | // New User
65 | var success = function(uid) {
66 | User.setUserField(uid, 'qqid', qqID);
67 | db.setObjectField('qqid:uid', qqID, uid);
68 | callback(null, {
69 | uid: uid
70 | });
71 | };
72 |
73 | User.getUidByEmail(email, function(err, uid) {
74 | if (!uid) {
75 | User.create({username: username, email: email}, function(err, uid) {
76 | if (err !== null) {
77 | callback(err);
78 | } else {
79 | success(uid);
80 | }
81 | });
82 | } else {
83 | success(uid); // Existing account -- merge
84 | }
85 | });
86 | }
87 | });
88 | };
89 |
90 | QQ.getUidByQQID = function(qqID, callback) {
91 | db.getObjectField('qqid:uid', qqID, function(err, uid) {
92 | if (err) {
93 | callback(err);
94 | } else {
95 | callback(null, uid);
96 | }
97 | });
98 | };
99 |
100 | QQ.addMenuItem = function(custom_header, callback) {
101 | custom_header.authentication.push({
102 | "route": constants.admin.route,
103 | "icon": constants.admin.icon,
104 | "name": constants.name
105 | });
106 |
107 | callback(null, custom_header);
108 | };
109 |
110 | QQ.init = function(data, callback) {
111 | function renderAdmin(req, res) {
112 | res.render('admin/plugins/sso-qq', {});
113 | }
114 |
115 | data.router.get('/admin/plugins/sso-qq', data.middleware.admin.buildHeader, renderAdmin);
116 | data.router.get('/api/admin/plugins/sso-qq', renderAdmin);
117 |
118 | callback();
119 | };
120 |
121 | module.exports = QQ;
122 | }(module));
123 |
--------------------------------------------------------------------------------