wiki-security-passportjs 0.4.8 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AUTHORS.txt +1 -0
- package/Gruntfile.js +0 -7
- package/client/security.js +59 -23
- package/docs/config-oauth2.md +59 -0
- package/docs/configuration.md +1 -0
- package/package.json +6 -6
- package/server/social.coffee +92 -3
package/AUTHORS.txt
CHANGED
package/Gruntfile.js
CHANGED
|
@@ -2,7 +2,6 @@ module.exports = function (grunt) {
|
|
|
2
2
|
grunt.loadNpmTasks('grunt-browserify');
|
|
3
3
|
grunt.loadNpmTasks('grunt-contrib-watch');
|
|
4
4
|
grunt.loadNpmTasks('grunt-git-authors');
|
|
5
|
-
grunt.loadNpmTasks('grunt-retire');
|
|
6
5
|
|
|
7
6
|
grunt.initConfig({
|
|
8
7
|
|
|
@@ -24,16 +23,10 @@ module.exports = function (grunt) {
|
|
|
24
23
|
files: ['client/*.coffee'],
|
|
25
24
|
tasks: ['build']
|
|
26
25
|
}
|
|
27
|
-
},
|
|
28
26
|
|
|
29
|
-
retire: {
|
|
30
|
-
node: ['.'],
|
|
31
|
-
options: {packageOnly: true}
|
|
32
27
|
}
|
|
33
|
-
|
|
34
28
|
});
|
|
35
29
|
|
|
36
|
-
grunt.registerTask('check', ['retire']);
|
|
37
30
|
grunt.registerTask('build', ['browserify']);
|
|
38
31
|
grunt.registerTask('default', ['build']);
|
|
39
32
|
|
package/client/security.js
CHANGED
|
@@ -272,7 +272,7 @@ window.plugins.security = {setup, claim_wiki, update_footer};
|
|
|
272
272
|
|
|
273
273
|
|
|
274
274
|
},{"es6-promise":2,"whatwg-fetch":4}],2:[function(require,module,exports){
|
|
275
|
-
(function (process,global){
|
|
275
|
+
(function (process,global){(function (){
|
|
276
276
|
/*!
|
|
277
277
|
* @overview es6-promise - a tiny implementation of Promises/A+.
|
|
278
278
|
* @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald)
|
|
@@ -1448,7 +1448,7 @@ return Promise$1;
|
|
|
1448
1448
|
|
|
1449
1449
|
|
|
1450
1450
|
|
|
1451
|
-
}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
|
|
1451
|
+
}).call(this)}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
|
|
1452
1452
|
},{"_process":3}],3:[function(require,module,exports){
|
|
1453
1453
|
// shim for using process in browser
|
|
1454
1454
|
var process = module.exports = {};
|
|
@@ -1640,12 +1640,13 @@ process.umask = function() { return 0; };
|
|
|
1640
1640
|
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
|
1641
1641
|
typeof define === 'function' && define.amd ? define(['exports'], factory) :
|
|
1642
1642
|
(factory((global.WHATWGFetch = {})));
|
|
1643
|
-
}(this, (function (exports) {
|
|
1643
|
+
}(this, (function (exports) { 'use strict';
|
|
1644
|
+
|
|
1645
|
+
var global =
|
|
1646
|
+
(typeof globalThis !== 'undefined' && globalThis) ||
|
|
1647
|
+
(typeof self !== 'undefined' && self) ||
|
|
1648
|
+
(typeof global !== 'undefined' && global);
|
|
1644
1649
|
|
|
1645
|
-
var global = (function(self) {
|
|
1646
|
-
return self
|
|
1647
|
-
// eslint-disable-next-line no-invalid-this
|
|
1648
|
-
})(typeof self !== 'undefined' ? self : this);
|
|
1649
1650
|
var support = {
|
|
1650
1651
|
searchParams: 'URLSearchParams' in global,
|
|
1651
1652
|
iterable: 'Symbol' in global && 'iterator' in Symbol,
|
|
@@ -1693,7 +1694,7 @@ process.umask = function() { return 0; };
|
|
|
1693
1694
|
name = String(name);
|
|
1694
1695
|
}
|
|
1695
1696
|
if (/[^a-z0-9\-#$%&'*+.^_`|~!]/i.test(name) || name === '') {
|
|
1696
|
-
throw new TypeError('Invalid character in header field name')
|
|
1697
|
+
throw new TypeError('Invalid character in header field name: "' + name + '"')
|
|
1697
1698
|
}
|
|
1698
1699
|
return name.toLowerCase()
|
|
1699
1700
|
}
|
|
@@ -1920,7 +1921,20 @@ process.umask = function() { return 0; };
|
|
|
1920
1921
|
|
|
1921
1922
|
this.arrayBuffer = function() {
|
|
1922
1923
|
if (this._bodyArrayBuffer) {
|
|
1923
|
-
|
|
1924
|
+
var isConsumed = consumed(this);
|
|
1925
|
+
if (isConsumed) {
|
|
1926
|
+
return isConsumed
|
|
1927
|
+
}
|
|
1928
|
+
if (ArrayBuffer.isView(this._bodyArrayBuffer)) {
|
|
1929
|
+
return Promise.resolve(
|
|
1930
|
+
this._bodyArrayBuffer.buffer.slice(
|
|
1931
|
+
this._bodyArrayBuffer.byteOffset,
|
|
1932
|
+
this._bodyArrayBuffer.byteOffset + this._bodyArrayBuffer.byteLength
|
|
1933
|
+
)
|
|
1934
|
+
)
|
|
1935
|
+
} else {
|
|
1936
|
+
return Promise.resolve(this._bodyArrayBuffer)
|
|
1937
|
+
}
|
|
1924
1938
|
} else {
|
|
1925
1939
|
return this.blob().then(readBlobAsArrayBuffer)
|
|
1926
1940
|
}
|
|
@@ -1966,6 +1980,10 @@ process.umask = function() { return 0; };
|
|
|
1966
1980
|
}
|
|
1967
1981
|
|
|
1968
1982
|
function Request(input, options) {
|
|
1983
|
+
if (!(this instanceof Request)) {
|
|
1984
|
+
throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.')
|
|
1985
|
+
}
|
|
1986
|
+
|
|
1969
1987
|
options = options || {};
|
|
1970
1988
|
var body = options.body;
|
|
1971
1989
|
|
|
@@ -2044,20 +2062,31 @@ process.umask = function() { return 0; };
|
|
|
2044
2062
|
// Replace instances of \r\n and \n followed by at least one space or horizontal tab with a space
|
|
2045
2063
|
// https://tools.ietf.org/html/rfc7230#section-3.2
|
|
2046
2064
|
var preProcessedHeaders = rawHeaders.replace(/\r?\n[\t ]+/g, ' ');
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2065
|
+
// Avoiding split via regex to work around a common IE11 bug with the core-js 3.6.0 regex polyfill
|
|
2066
|
+
// https://github.com/github/fetch/issues/748
|
|
2067
|
+
// https://github.com/zloirock/core-js/issues/751
|
|
2068
|
+
preProcessedHeaders
|
|
2069
|
+
.split('\r')
|
|
2070
|
+
.map(function(header) {
|
|
2071
|
+
return header.indexOf('\n') === 0 ? header.substr(1, header.length) : header
|
|
2072
|
+
})
|
|
2073
|
+
.forEach(function(line) {
|
|
2074
|
+
var parts = line.split(':');
|
|
2075
|
+
var key = parts.shift().trim();
|
|
2076
|
+
if (key) {
|
|
2077
|
+
var value = parts.join(':').trim();
|
|
2078
|
+
headers.append(key, value);
|
|
2079
|
+
}
|
|
2080
|
+
});
|
|
2055
2081
|
return headers
|
|
2056
2082
|
}
|
|
2057
2083
|
|
|
2058
2084
|
Body.call(Request.prototype);
|
|
2059
2085
|
|
|
2060
2086
|
function Response(bodyInit, options) {
|
|
2087
|
+
if (!(this instanceof Response)) {
|
|
2088
|
+
throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.')
|
|
2089
|
+
}
|
|
2061
2090
|
if (!options) {
|
|
2062
2091
|
options = {};
|
|
2063
2092
|
}
|
|
@@ -2065,7 +2094,7 @@ process.umask = function() { return 0; };
|
|
|
2065
2094
|
this.type = 'default';
|
|
2066
2095
|
this.status = options.status === undefined ? 200 : options.status;
|
|
2067
2096
|
this.ok = this.status >= 200 && this.status < 300;
|
|
2068
|
-
this.statusText =
|
|
2097
|
+
this.statusText = options.statusText === undefined ? '' : '' + options.statusText;
|
|
2069
2098
|
this.headers = new Headers(options.headers);
|
|
2070
2099
|
this.url = options.url || '';
|
|
2071
2100
|
this._initBody(bodyInit);
|
|
@@ -2099,8 +2128,9 @@ process.umask = function() { return 0; };
|
|
|
2099
2128
|
};
|
|
2100
2129
|
|
|
2101
2130
|
exports.DOMException = global.DOMException;
|
|
2102
|
-
|
|
2103
|
-
|
|
2131
|
+
try {
|
|
2132
|
+
new exports.DOMException();
|
|
2133
|
+
} catch (err) {
|
|
2104
2134
|
exports.DOMException = function(message, name) {
|
|
2105
2135
|
this.message = message;
|
|
2106
2136
|
this.name = name;
|
|
@@ -2184,9 +2214,15 @@ process.umask = function() { return 0; };
|
|
|
2184
2214
|
}
|
|
2185
2215
|
}
|
|
2186
2216
|
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2217
|
+
if (init && typeof init.headers === 'object' && !(init.headers instanceof Headers)) {
|
|
2218
|
+
Object.getOwnPropertyNames(init.headers).forEach(function(name) {
|
|
2219
|
+
xhr.setRequestHeader(name, normalizeValue(init.headers[name]));
|
|
2220
|
+
});
|
|
2221
|
+
} else {
|
|
2222
|
+
request.headers.forEach(function(value, name) {
|
|
2223
|
+
xhr.setRequestHeader(name, value);
|
|
2224
|
+
});
|
|
2225
|
+
}
|
|
2190
2226
|
|
|
2191
2227
|
if (request.signal) {
|
|
2192
2228
|
request.signal.addEventListener('abort', abortXhr);
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
## Generic OAuth 2
|
|
2
|
+
|
|
3
|
+
### Login provider set-up
|
|
4
|
+
|
|
5
|
+
Like the other PassportJS login providers, we'll need a separate "OAuth2 Client"
|
|
6
|
+
(others call it an "app", a "product" etc.) for our Federated Wiki instance.
|
|
7
|
+
|
|
8
|
+
How to do this varies slightly for each provider.
|
|
9
|
+
|
|
10
|
+
### `config.json`
|
|
11
|
+
|
|
12
|
+
In general, you will need to specify:
|
|
13
|
+
* `oauth2_clientID` -- some systems generate this for you, others allow you to
|
|
14
|
+
specify it
|
|
15
|
+
* `oauth2_clientSecret` -- secure key (keep this secret!)
|
|
16
|
+
* `oauth2_AuthorizationURL` and `oauth2_TokenURL` -- from your login provider's documentation
|
|
17
|
+
|
|
18
|
+
You will also need to specify a callback URL. For some providers, you can add
|
|
19
|
+
this when making a new "OAuth Client" for your wiki, for others you will need to
|
|
20
|
+
specify it with `oauth2_CallbackURL`.
|
|
21
|
+
|
|
22
|
+
You might also need to tell Federated Wiki how to look up usernames:
|
|
23
|
+
* `oauth2_UserInfoURL` -- from login provider's documentation
|
|
24
|
+
* `oauth2_IdField`, `oauth2_DisplayNameField`, `oauth2_UsernameField` -- starting with
|
|
25
|
+
* `params` for information returned in the original token request, or
|
|
26
|
+
* `profile` for data returned from `oauth2_UserInfoURL`, if you provided it.
|
|
27
|
+
|
|
28
|
+
Sometimes, you'll be able to look up the URLs by visiting your provider's
|
|
29
|
+
`/.well-known/openid-configuration` URL in a web browser.
|
|
30
|
+
|
|
31
|
+
### Examples
|
|
32
|
+
|
|
33
|
+
#### Nextcloud
|
|
34
|
+
|
|
35
|
+
```JSON
|
|
36
|
+
{
|
|
37
|
+
"farm": true,
|
|
38
|
+
"security_type": "passportjs",
|
|
39
|
+
"oauth2_clientID": "CLIENT ID",
|
|
40
|
+
"oauth2_clientSecret": "CLIENT SECRET",
|
|
41
|
+
"oauth2_AuthorizationURL": "https://auth.example.com/oauth2/authorize",
|
|
42
|
+
"oauth2_TokenURL": "https://auth.example.com/oauth2/token",
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
#### Keycloak
|
|
47
|
+
|
|
48
|
+
```JSON
|
|
49
|
+
{
|
|
50
|
+
"farm": true,
|
|
51
|
+
"security_type": "passportjs",
|
|
52
|
+
"oauth2_clientID": "CLIENT ID",
|
|
53
|
+
"oauth2_clientSecret": "CLIENT SECRET",
|
|
54
|
+
"oauth2_AuthorizationURL": "https://auth.example.com/auth/realms/Wiki.Cafe/protocol/openid-connect/auth",
|
|
55
|
+
"oauth2_TokenURL": "https://auth.example.com/auth/realms/Wiki.Cafe/protocol/openid-connect/token",
|
|
56
|
+
"oauth2_UserInfoURL": "https://auth.example.com/auth/realms/Wiki.Cafe/protocol/openid-connect/userinfo",
|
|
57
|
+
"oauth2_UsernameField": "profile.preferred_username"
|
|
58
|
+
}
|
|
59
|
+
```
|
package/docs/configuration.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wiki-security-passportjs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Security plugin for Federated Wiki, using passport.js",
|
|
5
5
|
"author": "Paul Rodwell <paul.rodwell@btinternet.com> (http://rodwell.me)",
|
|
6
6
|
"license": "MIT",
|
|
@@ -9,9 +9,10 @@
|
|
|
9
9
|
"es6-promise": "^4.2.8",
|
|
10
10
|
"lodash": "^4.17.19",
|
|
11
11
|
"passport": "^0.3.2",
|
|
12
|
-
"passport-
|
|
12
|
+
"passport-github2": "^0.1.12",
|
|
13
13
|
"passport-google-oauth20": "^2.0.0",
|
|
14
|
-
"passport-
|
|
14
|
+
"passport-oauth2": "^1.6.1",
|
|
15
|
+
"passport-twitter": "github:paul90/passport-twitter#48b52556f48e4e8f7c55288baaf3ba3076eeba16",
|
|
15
16
|
"persona-pass": "^0.2.1",
|
|
16
17
|
"qs": "^6.7.0",
|
|
17
18
|
"whatwg-fetch": "^3.2.0"
|
|
@@ -19,10 +20,9 @@
|
|
|
19
20
|
"devDependencies": {
|
|
20
21
|
"coffeeify": "^3.0.1",
|
|
21
22
|
"grunt": "^1.2.1",
|
|
22
|
-
"grunt-browserify": "^
|
|
23
|
+
"grunt-browserify": "^6.0.0",
|
|
23
24
|
"grunt-contrib-watch": "^1.0.0",
|
|
24
|
-
"grunt-git-authors": "^3.2.0"
|
|
25
|
-
"grunt-retire": "^1.0.8"
|
|
25
|
+
"grunt-git-authors": "^3.2.0"
|
|
26
26
|
},
|
|
27
27
|
"repository": {
|
|
28
28
|
"type": "git",
|
package/server/social.coffee
CHANGED
|
@@ -135,7 +135,7 @@ module.exports = exports = (log, loga, argv) ->
|
|
|
135
135
|
idProvider = _.head(_.keys(req.session.passport.user))
|
|
136
136
|
console.log 'idProvider: ', idProvider
|
|
137
137
|
switch idProvider
|
|
138
|
-
when 'github', 'google', 'twitter'
|
|
138
|
+
when 'github', 'google', 'twitter', 'oauth2'
|
|
139
139
|
if _.isEqual(owner[idProvider].id, req.session.passport.user[idProvider].id)
|
|
140
140
|
return true
|
|
141
141
|
else
|
|
@@ -165,7 +165,7 @@ module.exports = exports = (log, loga, argv) ->
|
|
|
165
165
|
return false
|
|
166
166
|
|
|
167
167
|
switch idProvider
|
|
168
|
-
when "github", "google", "twitter"
|
|
168
|
+
when "github", "google", "twitter", 'oauth2'
|
|
169
169
|
if _.isEqual(admin[idProvider], req.session.passport.user[idProvider].id)
|
|
170
170
|
return true
|
|
171
171
|
else
|
|
@@ -194,10 +194,79 @@ module.exports = exports = (log, loga, argv) ->
|
|
|
194
194
|
passport.deserializeUser = (obj, req, done) ->
|
|
195
195
|
done(null, obj)
|
|
196
196
|
|
|
197
|
+
# OAuth Strategy
|
|
198
|
+
if argv.oauth2_clientID? and argv.oauth2_clientSecret?
|
|
199
|
+
ids.push('oauth2')
|
|
200
|
+
OAuth2Strategy = require('passport-oauth2').Strategy
|
|
201
|
+
|
|
202
|
+
oauth2StrategyName = callbackHost + 'OAuth'
|
|
203
|
+
|
|
204
|
+
if argv.oauth2_UserInfoURL?
|
|
205
|
+
OAuth2Strategy::userProfile = (accesstoken, done) ->
|
|
206
|
+
console.log "hello"
|
|
207
|
+
console.log accesstoken
|
|
208
|
+
@_oauth2._request "GET", argv.oauth2_UserInfoURL, null, null, accesstoken, (err, data) ->
|
|
209
|
+
if err
|
|
210
|
+
return done err
|
|
211
|
+
try
|
|
212
|
+
data = JSON.parse data
|
|
213
|
+
catch e
|
|
214
|
+
return done e
|
|
215
|
+
done(null, data)
|
|
216
|
+
|
|
217
|
+
passport.use(oauth2StrategyName, new OAuth2Strategy({
|
|
218
|
+
clientID: argv.oauth2_clientID
|
|
219
|
+
clientSecret: argv.oauth2_clientSecret
|
|
220
|
+
authorizationURL: argv.oauth2_AuthorizationURL
|
|
221
|
+
tokenURL: argv.oauth2_TokenURL,
|
|
222
|
+
# not all providers have a way of specifying the callback URL
|
|
223
|
+
callbackURL: callbackProtocol + '//' + callbackHost + '/auth/oauth2/callback',
|
|
224
|
+
userInfoURL: argv.oauth2_UserInfoURL
|
|
225
|
+
}, (accessToken, refreshToken, params, profile, cb) ->
|
|
226
|
+
|
|
227
|
+
extractUserInfo = (uiParam, uiDef) ->
|
|
228
|
+
uiPath = ''
|
|
229
|
+
if typeof uiParam == 'undefined' then (uiPath = uiDef) else (uiPath = uiParam)
|
|
230
|
+
console.log('extractUI', uiParam, uiDef, uiPath)
|
|
231
|
+
sParts = uiPath.split('.')
|
|
232
|
+
sFrom = sParts.shift()
|
|
233
|
+
switch sFrom
|
|
234
|
+
when "params"
|
|
235
|
+
obj = params
|
|
236
|
+
when "profile"
|
|
237
|
+
obj = profile
|
|
238
|
+
else
|
|
239
|
+
console.error('*** source of user info not recognised', uiPath)
|
|
240
|
+
obj = {}
|
|
241
|
+
|
|
242
|
+
while (sParts.length)
|
|
243
|
+
obj = obj[sParts.shift()]
|
|
244
|
+
return obj
|
|
245
|
+
|
|
246
|
+
console.log("accessToken", accessToken)
|
|
247
|
+
console.log("refreshToken", refreshToken)
|
|
248
|
+
console.log("params", params)
|
|
249
|
+
console.log("profile", profile)
|
|
250
|
+
if argv.oauth2_UsernameField?
|
|
251
|
+
username_query = argv.oauth2_UsernameField
|
|
252
|
+
else
|
|
253
|
+
username_query = 'params.user_id'
|
|
254
|
+
|
|
255
|
+
try
|
|
256
|
+
user.oauth2 = {
|
|
257
|
+
id: extractUserInfo(argv.oauth2_IdField, 'params.user_id')
|
|
258
|
+
username: extractUserInfo(argv.oauth2_UsernameField, 'params.user_id')
|
|
259
|
+
displayName: extractUserInfo(argv.oauth2_DisplayNameField, 'params.user_id')
|
|
260
|
+
}
|
|
261
|
+
catch e
|
|
262
|
+
console.error('*** Error extracting user info:', e)
|
|
263
|
+
console.log user.oauth2
|
|
264
|
+
cb(null, user)))
|
|
265
|
+
|
|
197
266
|
# Github Strategy
|
|
198
267
|
if argv.github_clientID? and argv.github_clientSecret?
|
|
199
268
|
ids.push('github')
|
|
200
|
-
GithubStrategy = require('passport-
|
|
269
|
+
GithubStrategy = require('passport-github2').Strategy
|
|
201
270
|
|
|
202
271
|
githubStrategyName = callbackHost + 'Github'
|
|
203
272
|
|
|
@@ -275,6 +344,11 @@ module.exports = exports = (log, loga, argv) ->
|
|
|
275
344
|
app.use(passport.initialize())
|
|
276
345
|
app.use(passport.session())
|
|
277
346
|
|
|
347
|
+
# OAuth2
|
|
348
|
+
app.get('/auth/oauth2', passport.authenticate(oauth2StrategyName), (req, res) -> )
|
|
349
|
+
app.get('/auth/oauth2/callback',
|
|
350
|
+
passport.authenticate(oauth2StrategyName, { successRedirect: '/auth/loginDone', failureRedirect: '/auth/loginDialog'}))
|
|
351
|
+
|
|
278
352
|
# Github
|
|
279
353
|
app.get('/auth/github', passport.authenticate(githubStrategyName, {scope: 'user:email'}), (req, res) -> )
|
|
280
354
|
app.get('/auth/github/callback',
|
|
@@ -317,6 +391,7 @@ module.exports = exports = (log, loga, argv) ->
|
|
|
317
391
|
schemeButtons = []
|
|
318
392
|
_(ids).forEach (scheme) ->
|
|
319
393
|
switch scheme
|
|
394
|
+
when "oauth2" then schemeButtons.push({button: "<a href='/auth/oauth2' class='scheme-button oauth2-button'><span>OAuth2</span></a>"})
|
|
320
395
|
when "twitter" then schemeButtons.push({button: "<a href='/auth/twitter' class='scheme-button twitter-button'><span>Twitter</span></a>"})
|
|
321
396
|
when "github" then schemeButtons.push({button: "<a href='/auth/github' class='scheme-button github-button'><span>Github</span></a>"})
|
|
322
397
|
when "google"
|
|
@@ -504,6 +579,13 @@ module.exports = exports = (log, loga, argv) ->
|
|
|
504
579
|
userIds = {}
|
|
505
580
|
idProviders.forEach (idProvider) ->
|
|
506
581
|
id = switch idProvider
|
|
582
|
+
when "oauth2" then {
|
|
583
|
+
name: user.oauth2.displayName
|
|
584
|
+
oauth2: {
|
|
585
|
+
id: user.oauth2.id
|
|
586
|
+
username: user.oauth2.username
|
|
587
|
+
}
|
|
588
|
+
}
|
|
507
589
|
when "twitter" then {
|
|
508
590
|
name: user.twitter.displayName
|
|
509
591
|
twitter: {
|
|
@@ -580,6 +662,13 @@ module.exports = exports = (log, loga, argv) ->
|
|
|
580
662
|
id = {}
|
|
581
663
|
idProviders.forEach (idProvider) ->
|
|
582
664
|
id = switch idProvider
|
|
665
|
+
when "oauth2" then {
|
|
666
|
+
name: user.oauth2.displayName
|
|
667
|
+
oauth2: {
|
|
668
|
+
id: user.oauth2.id
|
|
669
|
+
username: user.oauth2.username
|
|
670
|
+
}
|
|
671
|
+
}
|
|
583
672
|
when "twitter" then {
|
|
584
673
|
name: user.twitter.displayName
|
|
585
674
|
twitter: {
|