@beiguancyc/parse-server 9.9.0-ruixiang.1
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/LICENSE +176 -0
- package/NOTICE +10 -0
- package/README.md +1311 -0
- package/bin/parse-live-query-server +3 -0
- package/bin/parse-server +3 -0
- package/lib/AccountLockout.js +149 -0
- package/lib/Adapters/AdapterLoader.js +60 -0
- package/lib/Adapters/Analytics/AnalyticsAdapter.js +32 -0
- package/lib/Adapters/Auth/AuthAdapter.js +132 -0
- package/lib/Adapters/Auth/BaseCodeAuthAdapter.js +119 -0
- package/lib/Adapters/Auth/OAuth1Client.js +204 -0
- package/lib/Adapters/Auth/apple.js +125 -0
- package/lib/Adapters/Auth/facebook.js +187 -0
- package/lib/Adapters/Auth/gcenter.js +217 -0
- package/lib/Adapters/Auth/github.js +125 -0
- package/lib/Adapters/Auth/google.js +122 -0
- package/lib/Adapters/Auth/gpgames.js +125 -0
- package/lib/Adapters/Auth/httpsRequest.js +41 -0
- package/lib/Adapters/Auth/index.js +248 -0
- package/lib/Adapters/Auth/instagram.js +118 -0
- package/lib/Adapters/Auth/janraincapture.js +85 -0
- package/lib/Adapters/Auth/janrainengage.js +57 -0
- package/lib/Adapters/Auth/keycloak.js +168 -0
- package/lib/Adapters/Auth/ldap.js +193 -0
- package/lib/Adapters/Auth/line.js +125 -0
- package/lib/Adapters/Auth/linkedin.js +117 -0
- package/lib/Adapters/Auth/meetup.js +44 -0
- package/lib/Adapters/Auth/mfa.js +334 -0
- package/lib/Adapters/Auth/microsoft.js +111 -0
- package/lib/Adapters/Auth/oauth2.js +115 -0
- package/lib/Adapters/Auth/phantauth.js +53 -0
- package/lib/Adapters/Auth/qq.js +113 -0
- package/lib/Adapters/Auth/spotify.js +115 -0
- package/lib/Adapters/Auth/twitter.js +197 -0
- package/lib/Adapters/Auth/utils.js +28 -0
- package/lib/Adapters/Auth/vkontakte.js +55 -0
- package/lib/Adapters/Auth/wechat.js +110 -0
- package/lib/Adapters/Auth/weibo.js +141 -0
- package/lib/Adapters/Cache/CacheAdapter.js +40 -0
- package/lib/Adapters/Cache/InMemoryCache.js +61 -0
- package/lib/Adapters/Cache/InMemoryCacheAdapter.js +34 -0
- package/lib/Adapters/Cache/LRUCache.js +35 -0
- package/lib/Adapters/Cache/NullCacheAdapter.js +26 -0
- package/lib/Adapters/Cache/RedisCacheAdapter.js +109 -0
- package/lib/Adapters/Cache/SchemaCache.js +25 -0
- package/lib/Adapters/Email/MailAdapter.js +32 -0
- package/lib/Adapters/Files/FilesAdapter.js +129 -0
- package/lib/Adapters/Files/GridFSBucketAdapter.js +280 -0
- package/lib/Adapters/Files/GridStoreAdapter.js +5 -0
- package/lib/Adapters/Logger/LoggerAdapter.js +27 -0
- package/lib/Adapters/Logger/WinstonLogger.js +107 -0
- package/lib/Adapters/Logger/WinstonLoggerAdapter.js +63 -0
- package/lib/Adapters/MessageQueue/EventEmitterMQ.js +55 -0
- package/lib/Adapters/PubSub/EventEmitterPubSub.js +53 -0
- package/lib/Adapters/PubSub/PubSubAdapter.js +34 -0
- package/lib/Adapters/PubSub/RedisPubSub.js +49 -0
- package/lib/Adapters/Push/PushAdapter.js +40 -0
- package/lib/Adapters/Storage/Mongo/MongoCollection.js +212 -0
- package/lib/Adapters/Storage/Mongo/MongoSchemaCollection.js +334 -0
- package/lib/Adapters/Storage/Mongo/MongoStorageAdapter.js +1047 -0
- package/lib/Adapters/Storage/Mongo/MongoTransform.js +1397 -0
- package/lib/Adapters/Storage/Postgres/PostgresClient.js +38 -0
- package/lib/Adapters/Storage/Postgres/PostgresConfigParser.js +73 -0
- package/lib/Adapters/Storage/Postgres/PostgresStorageAdapter.js +2409 -0
- package/lib/Adapters/Storage/Postgres/sql/array/add-unique.sql +11 -0
- package/lib/Adapters/Storage/Postgres/sql/array/add.sql +11 -0
- package/lib/Adapters/Storage/Postgres/sql/array/contains-all-regex.sql +14 -0
- package/lib/Adapters/Storage/Postgres/sql/array/contains-all.sql +14 -0
- package/lib/Adapters/Storage/Postgres/sql/array/contains.sql +11 -0
- package/lib/Adapters/Storage/Postgres/sql/array/remove.sql +11 -0
- package/lib/Adapters/Storage/Postgres/sql/index.js +32 -0
- package/lib/Adapters/Storage/Postgres/sql/misc/json-object-set-keys.sql +19 -0
- package/lib/Adapters/Storage/StorageAdapter.js +2 -0
- package/lib/Adapters/WebSocketServer/WSAdapter.js +35 -0
- package/lib/Adapters/WebSocketServer/WSSAdapter.js +66 -0
- package/lib/Auth.js +687 -0
- package/lib/AuthDataLock.js +53 -0
- package/lib/ClientSDK.js +39 -0
- package/lib/Config.js +879 -0
- package/lib/Controllers/AdaptableController.js +72 -0
- package/lib/Controllers/AnalyticsController.js +43 -0
- package/lib/Controllers/CacheController.js +71 -0
- package/lib/Controllers/DatabaseController.js +1685 -0
- package/lib/Controllers/FilesController.js +122 -0
- package/lib/Controllers/HooksController.js +254 -0
- package/lib/Controllers/LiveQueryController.js +72 -0
- package/lib/Controllers/LoggerController.js +222 -0
- package/lib/Controllers/ParseGraphQLController.js +294 -0
- package/lib/Controllers/PushController.js +224 -0
- package/lib/Controllers/SchemaController.js +1563 -0
- package/lib/Controllers/UserController.js +364 -0
- package/lib/Controllers/index.js +251 -0
- package/lib/Controllers/types.js +2 -0
- package/lib/Deprecator/Deprecations.js +100 -0
- package/lib/Deprecator/Deprecator.js +140 -0
- package/lib/Error.js +50 -0
- package/lib/FileUrlValidator.js +69 -0
- package/lib/GraphQL/ParseGraphQLSchema.js +375 -0
- package/lib/GraphQL/ParseGraphQLServer.js +241 -0
- package/lib/GraphQL/helpers/objectsMutations.js +30 -0
- package/lib/GraphQL/helpers/objectsQueries.js +246 -0
- package/lib/GraphQL/helpers/queryComplexity.js +138 -0
- package/lib/GraphQL/loaders/configMutations.js +87 -0
- package/lib/GraphQL/loaders/configQueries.js +79 -0
- package/lib/GraphQL/loaders/defaultGraphQLMutations.js +21 -0
- package/lib/GraphQL/loaders/defaultGraphQLQueries.js +23 -0
- package/lib/GraphQL/loaders/defaultGraphQLTypes.js +1099 -0
- package/lib/GraphQL/loaders/defaultRelaySchema.js +53 -0
- package/lib/GraphQL/loaders/filesMutations.js +107 -0
- package/lib/GraphQL/loaders/functionsMutations.js +77 -0
- package/lib/GraphQL/loaders/parseClassMutations.js +267 -0
- package/lib/GraphQL/loaders/parseClassQueries.js +126 -0
- package/lib/GraphQL/loaders/parseClassTypes.js +493 -0
- package/lib/GraphQL/loaders/schemaDirectives.js +62 -0
- package/lib/GraphQL/loaders/schemaMutations.js +161 -0
- package/lib/GraphQL/loaders/schemaQueries.js +80 -0
- package/lib/GraphQL/loaders/schemaTypes.js +341 -0
- package/lib/GraphQL/loaders/usersMutations.js +433 -0
- package/lib/GraphQL/loaders/usersQueries.js +90 -0
- package/lib/GraphQL/parseGraphQLUtils.js +71 -0
- package/lib/GraphQL/transformers/className.js +14 -0
- package/lib/GraphQL/transformers/constraintType.js +53 -0
- package/lib/GraphQL/transformers/inputType.js +51 -0
- package/lib/GraphQL/transformers/mutation.js +280 -0
- package/lib/GraphQL/transformers/outputType.js +51 -0
- package/lib/GraphQL/transformers/query.js +237 -0
- package/lib/GraphQL/transformers/schemaFields.js +99 -0
- package/lib/InstallationDedup.js +178 -0
- package/lib/KeyPromiseQueue.js +48 -0
- package/lib/LiveQuery/Client.js +90 -0
- package/lib/LiveQuery/Id.js +20 -0
- package/lib/LiveQuery/ParseCloudCodePublisher.js +54 -0
- package/lib/LiveQuery/ParseLiveQueryServer.js +1071 -0
- package/lib/LiveQuery/ParsePubSub.js +37 -0
- package/lib/LiveQuery/ParseWebSocketServer.js +66 -0
- package/lib/LiveQuery/QueryTools.js +443 -0
- package/lib/LiveQuery/RequestSchema.js +161 -0
- package/lib/LiveQuery/SessionTokenCache.js +51 -0
- package/lib/LiveQuery/Subscription.js +47 -0
- package/lib/LiveQuery/equalObjects.js +50 -0
- package/lib/Options/Definitions.js +1599 -0
- package/lib/Options/docs.js +389 -0
- package/lib/Options/index.js +12 -0
- package/lib/Options/parsers.js +89 -0
- package/lib/Page.js +44 -0
- package/lib/ParseMessageQueue.js +24 -0
- package/lib/ParseServer.js +632 -0
- package/lib/ParseServerRESTController.js +173 -0
- package/lib/PromiseRouter.js +208 -0
- package/lib/Push/PushQueue.js +67 -0
- package/lib/Push/PushWorker.js +101 -0
- package/lib/Push/utils.js +129 -0
- package/lib/RestQuery.js +1208 -0
- package/lib/RestWrite.js +1671 -0
- package/lib/Routers/AggregateRouter.js +150 -0
- package/lib/Routers/AnalyticsRouter.js +26 -0
- package/lib/Routers/AudiencesRouter.js +54 -0
- package/lib/Routers/ClassesRouter.js +191 -0
- package/lib/Routers/CloudCodeRouter.js +87 -0
- package/lib/Routers/FeaturesRouter.js +72 -0
- package/lib/Routers/FilesRouter.js +772 -0
- package/lib/Routers/FunctionsRouter.js +341 -0
- package/lib/Routers/GlobalConfigRouter.js +123 -0
- package/lib/Routers/GraphQLRouter.js +41 -0
- package/lib/Routers/HooksRouter.js +121 -0
- package/lib/Routers/IAPValidationRouter.js +113 -0
- package/lib/Routers/InstallationsRouter.js +46 -0
- package/lib/Routers/LogsRouter.js +57 -0
- package/lib/Routers/PagesRouter.js +676 -0
- package/lib/Routers/PurgeRouter.js +45 -0
- package/lib/Routers/PushRouter.js +75 -0
- package/lib/Routers/RolesRouter.js +33 -0
- package/lib/Routers/SchemasRouter.js +118 -0
- package/lib/Routers/SecurityRouter.js +34 -0
- package/lib/Routers/SessionsRouter.js +126 -0
- package/lib/Routers/UsersRouter.js +733 -0
- package/lib/SchemaMigrations/DefinedSchemas.js +379 -0
- package/lib/SchemaMigrations/Migrations.js +30 -0
- package/lib/Security/Check.js +109 -0
- package/lib/Security/CheckGroup.js +44 -0
- package/lib/Security/CheckGroups/CheckGroupDatabase.js +44 -0
- package/lib/Security/CheckGroups/CheckGroupServerConfig.js +160 -0
- package/lib/Security/CheckGroups/CheckGroups.js +21 -0
- package/lib/Security/CheckRunner.js +213 -0
- package/lib/SharedRest.js +34 -0
- package/lib/StatusHandler.js +329 -0
- package/lib/TestUtils.js +87 -0
- package/lib/Utils.js +577 -0
- package/lib/batch.js +185 -0
- package/lib/cache.js +12 -0
- package/lib/cli/definitions/parse-live-query-server.js +9 -0
- package/lib/cli/definitions/parse-server.js +9 -0
- package/lib/cli/parse-live-query-server.js +14 -0
- package/lib/cli/parse-server.js +101 -0
- package/lib/cli/utils/commander.js +132 -0
- package/lib/cli/utils/runner.js +53 -0
- package/lib/cloud-code/Parse.Cloud.js +797 -0
- package/lib/cloud-code/Parse.Server.js +21 -0
- package/lib/cryptoUtils.js +54 -0
- package/lib/defaults.js +58 -0
- package/lib/deprecated.js +12 -0
- package/lib/index.js +89 -0
- package/lib/logger.js +40 -0
- package/lib/middlewares.js +810 -0
- package/lib/password.js +40 -0
- package/lib/request.js +175 -0
- package/lib/requiredParameter.js +11 -0
- package/lib/rest.js +264 -0
- package/lib/triggers.js +949 -0
- package/lib/vendor/README.md +8 -0
- package/lib/vendor/mongodbUrl.js +1012 -0
- package/package.json +172 -0
- package/postinstall.js +38 -0
- package/public/custom_json.html +17 -0
- package/public/custom_json.json +23 -0
- package/public/custom_page.html +15 -0
- package/public/de/email_verification_link_expired.html +24 -0
- package/public/de/email_verification_link_invalid.html +21 -0
- package/public/de/email_verification_send_fail.html +21 -0
- package/public/de/email_verification_send_success.html +19 -0
- package/public/de/email_verification_success.html +18 -0
- package/public/de/password_reset.html +65 -0
- package/public/de/password_reset_link_invalid.html +19 -0
- package/public/de/password_reset_success.html +18 -0
- package/public/de-AT/email_verification_link_expired.html +24 -0
- package/public/de-AT/email_verification_link_invalid.html +21 -0
- package/public/de-AT/email_verification_send_fail.html +21 -0
- package/public/de-AT/email_verification_send_success.html +19 -0
- package/public/de-AT/email_verification_success.html +18 -0
- package/public/de-AT/password_reset.html +65 -0
- package/public/de-AT/password_reset_link_invalid.html +19 -0
- package/public/de-AT/password_reset_success.html +18 -0
- package/public/email_verification_link_expired.html +24 -0
- package/public/email_verification_link_invalid.html +21 -0
- package/public/email_verification_send_fail.html +21 -0
- package/public/email_verification_send_success.html +19 -0
- package/public/email_verification_success.html +18 -0
- package/public/password_reset.html +65 -0
- package/public/password_reset_link_invalid.html +19 -0
- package/public/password_reset_success.html +18 -0
- package/types/@types/@parse/fs-files-adapter/index.d.ts +5 -0
- package/types/@types/deepcopy/index.d.ts +5 -0
- package/types/LiveQuery/ParseLiveQueryServer.d.ts +40 -0
- package/types/Options/index.d.ts +320 -0
- package/types/ParseServer.d.ts +65 -0
- package/types/eslint.config.mjs +30 -0
- package/types/index.d.ts +21 -0
- package/types/logger.d.ts +2 -0
- package/types/tests.ts +44 -0
- package/types/tsconfig.json +24 -0
- package/views/choose_password +215 -0
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var https = require('https'),
|
|
4
|
+
crypto = require('crypto');
|
|
5
|
+
var Parse = require('parse/node').Parse;
|
|
6
|
+
var OAuth = function (options) {
|
|
7
|
+
if (!options) {
|
|
8
|
+
throw new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'No options passed to OAuth');
|
|
9
|
+
}
|
|
10
|
+
this.consumer_key = options.consumer_key;
|
|
11
|
+
this.consumer_secret = options.consumer_secret;
|
|
12
|
+
this.auth_token = options.auth_token;
|
|
13
|
+
this.auth_token_secret = options.auth_token_secret;
|
|
14
|
+
this.host = options.host;
|
|
15
|
+
this.oauth_params = options.oauth_params || {};
|
|
16
|
+
};
|
|
17
|
+
OAuth.prototype.send = function (method, path, params, body) {
|
|
18
|
+
var request = this.buildRequest(method, path, params, body);
|
|
19
|
+
// Encode the body properly, the current Parse Implementation don't do it properly
|
|
20
|
+
return new Promise(function (resolve, reject) {
|
|
21
|
+
var httpRequest = https.request(request, function (res) {
|
|
22
|
+
var data = '';
|
|
23
|
+
res.on('data', function (chunk) {
|
|
24
|
+
data += chunk;
|
|
25
|
+
});
|
|
26
|
+
res.on('end', function () {
|
|
27
|
+
data = JSON.parse(data);
|
|
28
|
+
resolve(data);
|
|
29
|
+
});
|
|
30
|
+
}).on('error', function () {
|
|
31
|
+
reject('Failed to make an OAuth request');
|
|
32
|
+
});
|
|
33
|
+
if (request.body) {
|
|
34
|
+
httpRequest.write(request.body);
|
|
35
|
+
}
|
|
36
|
+
httpRequest.end();
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
OAuth.prototype.buildRequest = function (method, path, params, body) {
|
|
40
|
+
if (path.indexOf('/') != 0) {
|
|
41
|
+
path = '/' + path;
|
|
42
|
+
}
|
|
43
|
+
if (params && Object.keys(params).length > 0) {
|
|
44
|
+
path += '?' + OAuth.buildParameterString(params);
|
|
45
|
+
}
|
|
46
|
+
var request = {
|
|
47
|
+
host: this.host,
|
|
48
|
+
path: path,
|
|
49
|
+
method: method.toUpperCase()
|
|
50
|
+
};
|
|
51
|
+
var oauth_params = this.oauth_params || {};
|
|
52
|
+
oauth_params.oauth_consumer_key = this.consumer_key;
|
|
53
|
+
if (this.auth_token) {
|
|
54
|
+
oauth_params['oauth_token'] = this.auth_token;
|
|
55
|
+
}
|
|
56
|
+
request = OAuth.signRequest(request, oauth_params, this.consumer_secret, this.auth_token_secret);
|
|
57
|
+
if (body && Object.keys(body).length > 0) {
|
|
58
|
+
request.body = OAuth.buildParameterString(body);
|
|
59
|
+
}
|
|
60
|
+
return request;
|
|
61
|
+
};
|
|
62
|
+
OAuth.prototype.get = function (path, params) {
|
|
63
|
+
return this.send('GET', path, params);
|
|
64
|
+
};
|
|
65
|
+
OAuth.prototype.post = function (path, params, body) {
|
|
66
|
+
return this.send('POST', path, params, body);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/*
|
|
70
|
+
Proper string %escape encoding
|
|
71
|
+
*/
|
|
72
|
+
OAuth.encode = function (str) {
|
|
73
|
+
// discuss at: http://phpjs.org/functions/rawurlencode/
|
|
74
|
+
// original by: Brett Zamir (http://brett-zamir.me)
|
|
75
|
+
// input by: travc
|
|
76
|
+
// input by: Brett Zamir (http://brett-zamir.me)
|
|
77
|
+
// input by: Michael Grier
|
|
78
|
+
// input by: Ratheous
|
|
79
|
+
// bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
|
|
80
|
+
// bugfixed by: Brett Zamir (http://brett-zamir.me)
|
|
81
|
+
// bugfixed by: Joris
|
|
82
|
+
// reimplemented by: Brett Zamir (http://brett-zamir.me)
|
|
83
|
+
// reimplemented by: Brett Zamir (http://brett-zamir.me)
|
|
84
|
+
// note: This reflects PHP 5.3/6.0+ behavior
|
|
85
|
+
// note: Please be aware that this function expects to encode into UTF-8 encoded strings, as found on
|
|
86
|
+
// note: pages served as UTF-8
|
|
87
|
+
// example 1: rawurlencode('Kevin van Zonneveld!');
|
|
88
|
+
// returns 1: 'Kevin%20van%20Zonneveld%21'
|
|
89
|
+
// example 2: rawurlencode('http://kevin.vanzonneveld.net/');
|
|
90
|
+
// returns 2: 'http%3A%2F%2Fkevin.vanzonneveld.net%2F'
|
|
91
|
+
// example 3: rawurlencode('http://www.google.nl/search?q=php.js&ie=utf-8&oe=utf-8&aq=t&rls=com.ubuntu:en-US:unofficial&client=firefox-a');
|
|
92
|
+
// returns 3: 'http%3A%2F%2Fwww.google.nl%2Fsearch%3Fq%3Dphp.js%26ie%3Dutf-8%26oe%3Dutf-8%26aq%3Dt%26rls%3Dcom.ubuntu%3Aen-US%3Aunofficial%26client%3Dfirefox-a'
|
|
93
|
+
|
|
94
|
+
str = (str + '').toString();
|
|
95
|
+
|
|
96
|
+
// Tilde should be allowed unescaped in future versions of PHP (as reflected below), but if you want to reflect current
|
|
97
|
+
// PHP behavior, you would need to add ".replace(/~/g, '%7E');" to the following.
|
|
98
|
+
return encodeURIComponent(str).replace(/!/g, '%21').replace(/'/g, '%27').replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/\*/g, '%2A');
|
|
99
|
+
};
|
|
100
|
+
OAuth.signatureMethod = 'HMAC-SHA1';
|
|
101
|
+
OAuth.version = '1.0';
|
|
102
|
+
|
|
103
|
+
/*
|
|
104
|
+
Generate a nonce
|
|
105
|
+
*/
|
|
106
|
+
OAuth.nonce = function () {
|
|
107
|
+
var text = '';
|
|
108
|
+
var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
109
|
+
for (var i = 0; i < 30; i++) {
|
|
110
|
+
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
|
111
|
+
}
|
|
112
|
+
return text;
|
|
113
|
+
};
|
|
114
|
+
OAuth.buildParameterString = function (obj) {
|
|
115
|
+
// Sort keys and encode values
|
|
116
|
+
if (obj) {
|
|
117
|
+
var keys = Object.keys(obj).sort();
|
|
118
|
+
|
|
119
|
+
// Map key=value, join them by &
|
|
120
|
+
return keys.map(function (key) {
|
|
121
|
+
return key + '=' + OAuth.encode(obj[key]);
|
|
122
|
+
}).join('&');
|
|
123
|
+
}
|
|
124
|
+
return '';
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
/*
|
|
128
|
+
Build the signature string from the object
|
|
129
|
+
*/
|
|
130
|
+
|
|
131
|
+
OAuth.buildSignatureString = function (method, url, parameters) {
|
|
132
|
+
return [method.toUpperCase(), OAuth.encode(url), OAuth.encode(parameters)].join('&');
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
/*
|
|
136
|
+
Retuns encoded HMAC-SHA1 from key and text
|
|
137
|
+
*/
|
|
138
|
+
OAuth.signature = function (text, key) {
|
|
139
|
+
crypto = require('crypto');
|
|
140
|
+
return OAuth.encode(crypto.createHmac('sha1', key).update(text).digest('base64'));
|
|
141
|
+
};
|
|
142
|
+
OAuth.signRequest = function (request, oauth_parameters, consumer_secret, auth_token_secret) {
|
|
143
|
+
oauth_parameters = oauth_parameters || {};
|
|
144
|
+
|
|
145
|
+
// Set default values
|
|
146
|
+
if (!oauth_parameters.oauth_nonce) {
|
|
147
|
+
oauth_parameters.oauth_nonce = OAuth.nonce();
|
|
148
|
+
}
|
|
149
|
+
if (!oauth_parameters.oauth_timestamp) {
|
|
150
|
+
oauth_parameters.oauth_timestamp = Math.floor(new Date().getTime() / 1000);
|
|
151
|
+
}
|
|
152
|
+
if (!oauth_parameters.oauth_signature_method) {
|
|
153
|
+
oauth_parameters.oauth_signature_method = OAuth.signatureMethod;
|
|
154
|
+
}
|
|
155
|
+
if (!oauth_parameters.oauth_version) {
|
|
156
|
+
oauth_parameters.oauth_version = OAuth.version;
|
|
157
|
+
}
|
|
158
|
+
if (!auth_token_secret) {
|
|
159
|
+
auth_token_secret = '';
|
|
160
|
+
}
|
|
161
|
+
// Force GET method if unset
|
|
162
|
+
if (!request.method) {
|
|
163
|
+
request.method = 'GET';
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Collect all the parameters in one signatureParameters object
|
|
167
|
+
var signatureParams = {};
|
|
168
|
+
var parametersToMerge = [request.params, request.body, oauth_parameters];
|
|
169
|
+
for (var i in parametersToMerge) {
|
|
170
|
+
var parameters = parametersToMerge[i];
|
|
171
|
+
for (var k in parameters) {
|
|
172
|
+
signatureParams[k] = parameters[k];
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Create a string based on the parameters
|
|
177
|
+
var parameterString = OAuth.buildParameterString(signatureParams);
|
|
178
|
+
|
|
179
|
+
// Build the signature string
|
|
180
|
+
var url = 'https://' + request.host + '' + request.path;
|
|
181
|
+
var signatureString = OAuth.buildSignatureString(request.method, url, parameterString);
|
|
182
|
+
// Hash the signature string
|
|
183
|
+
var signatureKey = [OAuth.encode(consumer_secret), OAuth.encode(auth_token_secret)].join('&');
|
|
184
|
+
var signature = OAuth.signature(signatureString, signatureKey);
|
|
185
|
+
|
|
186
|
+
// Set the signature in the params
|
|
187
|
+
oauth_parameters.oauth_signature = signature;
|
|
188
|
+
if (!request.headers) {
|
|
189
|
+
request.headers = {};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Set the authorization header
|
|
193
|
+
var authHeader = Object.keys(oauth_parameters).sort().map(function (key) {
|
|
194
|
+
var value = oauth_parameters[key];
|
|
195
|
+
return key + '="' + value + '"';
|
|
196
|
+
}).join(', ');
|
|
197
|
+
request.headers.Authorization = 'OAuth ' + authHeader;
|
|
198
|
+
|
|
199
|
+
// Set the content type header
|
|
200
|
+
request.headers['Content-Type'] = 'application/x-www-form-urlencoded';
|
|
201
|
+
return request;
|
|
202
|
+
};
|
|
203
|
+
module.exports = OAuth;
|
|
204
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJodHRwcyIsInJlcXVpcmUiLCJjcnlwdG8iLCJQYXJzZSIsIk9BdXRoIiwib3B0aW9ucyIsIkVycm9yIiwiSU5URVJOQUxfU0VSVkVSX0VSUk9SIiwiY29uc3VtZXJfa2V5IiwiY29uc3VtZXJfc2VjcmV0IiwiYXV0aF90b2tlbiIsImF1dGhfdG9rZW5fc2VjcmV0IiwiaG9zdCIsIm9hdXRoX3BhcmFtcyIsInByb3RvdHlwZSIsInNlbmQiLCJtZXRob2QiLCJwYXRoIiwicGFyYW1zIiwiYm9keSIsInJlcXVlc3QiLCJidWlsZFJlcXVlc3QiLCJQcm9taXNlIiwicmVzb2x2ZSIsInJlamVjdCIsImh0dHBSZXF1ZXN0IiwicmVzIiwiZGF0YSIsIm9uIiwiY2h1bmsiLCJKU09OIiwicGFyc2UiLCJ3cml0ZSIsImVuZCIsImluZGV4T2YiLCJPYmplY3QiLCJrZXlzIiwibGVuZ3RoIiwiYnVpbGRQYXJhbWV0ZXJTdHJpbmciLCJ0b1VwcGVyQ2FzZSIsIm9hdXRoX2NvbnN1bWVyX2tleSIsInNpZ25SZXF1ZXN0IiwiZ2V0IiwicG9zdCIsImVuY29kZSIsInN0ciIsInRvU3RyaW5nIiwiZW5jb2RlVVJJQ29tcG9uZW50IiwicmVwbGFjZSIsInNpZ25hdHVyZU1ldGhvZCIsInZlcnNpb24iLCJub25jZSIsInRleHQiLCJwb3NzaWJsZSIsImkiLCJjaGFyQXQiLCJNYXRoIiwiZmxvb3IiLCJyYW5kb20iLCJvYmoiLCJzb3J0IiwibWFwIiwia2V5Iiwiam9pbiIsImJ1aWxkU2lnbmF0dXJlU3RyaW5nIiwidXJsIiwicGFyYW1ldGVycyIsInNpZ25hdHVyZSIsImNyZWF0ZUhtYWMiLCJ1cGRhdGUiLCJkaWdlc3QiLCJvYXV0aF9wYXJhbWV0ZXJzIiwib2F1dGhfbm9uY2UiLCJvYXV0aF90aW1lc3RhbXAiLCJEYXRlIiwiZ2V0VGltZSIsIm9hdXRoX3NpZ25hdHVyZV9tZXRob2QiLCJvYXV0aF92ZXJzaW9uIiwic2lnbmF0dXJlUGFyYW1zIiwicGFyYW1ldGVyc1RvTWVyZ2UiLCJrIiwicGFyYW1ldGVyU3RyaW5nIiwic2lnbmF0dXJlU3RyaW5nIiwic2lnbmF0dXJlS2V5Iiwib2F1dGhfc2lnbmF0dXJlIiwiaGVhZGVycyIsImF1dGhIZWFkZXIiLCJ2YWx1ZSIsIkF1dGhvcml6YXRpb24iLCJtb2R1bGUiLCJleHBvcnRzIl0sInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL0FkYXB0ZXJzL0F1dGgvT0F1dGgxQ2xpZW50LmpzIl0sInNvdXJjZXNDb250ZW50IjpbInZhciBodHRwcyA9IHJlcXVpcmUoJ2h0dHBzJyksXG4gIGNyeXB0byA9IHJlcXVpcmUoJ2NyeXB0bycpO1xudmFyIFBhcnNlID0gcmVxdWlyZSgncGFyc2Uvbm9kZScpLlBhcnNlO1xuXG52YXIgT0F1dGggPSBmdW5jdGlvbiAob3B0aW9ucykge1xuICBpZiAoIW9wdGlvbnMpIHtcbiAgICB0aHJvdyBuZXcgUGFyc2UuRXJyb3IoUGFyc2UuRXJyb3IuSU5URVJOQUxfU0VSVkVSX0VSUk9SLCAnTm8gb3B0aW9ucyBwYXNzZWQgdG8gT0F1dGgnKTtcbiAgfVxuICB0aGlzLmNvbnN1bWVyX2tleSA9IG9wdGlvbnMuY29uc3VtZXJfa2V5O1xuICB0aGlzLmNvbnN1bWVyX3NlY3JldCA9IG9wdGlvbnMuY29uc3VtZXJfc2VjcmV0O1xuICB0aGlzLmF1dGhfdG9rZW4gPSBvcHRpb25zLmF1dGhfdG9rZW47XG4gIHRoaXMuYXV0aF90b2tlbl9zZWNyZXQgPSBvcHRpb25zLmF1dGhfdG9rZW5fc2VjcmV0O1xuICB0aGlzLmhvc3QgPSBvcHRpb25zLmhvc3Q7XG4gIHRoaXMub2F1dGhfcGFyYW1zID0gb3B0aW9ucy5vYXV0aF9wYXJhbXMgfHwge307XG59O1xuXG5PQXV0aC5wcm90b3R5cGUuc2VuZCA9IGZ1bmN0aW9uIChtZXRob2QsIHBhdGgsIHBhcmFtcywgYm9keSkge1xuICB2YXIgcmVxdWVzdCA9IHRoaXMuYnVpbGRSZXF1ZXN0KG1ldGhvZCwgcGF0aCwgcGFyYW1zLCBib2R5KTtcbiAgLy8gRW5jb2RlIHRoZSBib2R5IHByb3Blcmx5LCB0aGUgY3VycmVudCBQYXJzZSBJbXBsZW1lbnRhdGlvbiBkb24ndCBkbyBpdCBwcm9wZXJseVxuICByZXR1cm4gbmV3IFByb21pc2UoZnVuY3Rpb24gKHJlc29sdmUsIHJlamVjdCkge1xuICAgIHZhciBodHRwUmVxdWVzdCA9IGh0dHBzXG4gICAgICAucmVxdWVzdChyZXF1ZXN0LCBmdW5jdGlvbiAocmVzKSB7XG4gICAgICAgIHZhciBkYXRhID0gJyc7XG4gICAgICAgIHJlcy5vbignZGF0YScsIGZ1bmN0aW9uIChjaHVuaykge1xuICAgICAgICAgIGRhdGEgKz0gY2h1bms7XG4gICAgICAgIH0pO1xuICAgICAgICByZXMub24oJ2VuZCcsIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICBkYXRhID0gSlNPTi5wYXJzZShkYXRhKTtcbiAgICAgICAgICByZXNvbHZlKGRhdGEpO1xuICAgICAgICB9KTtcbiAgICAgIH0pXG4gICAgICAub24oJ2Vycm9yJywgZnVuY3Rpb24gKCkge1xuICAgICAgICByZWplY3QoJ0ZhaWxlZCB0byBtYWtlIGFuIE9BdXRoIHJlcXVlc3QnKTtcbiAgICAgIH0pO1xuICAgIGlmIChyZXF1ZXN0LmJvZHkpIHtcbiAgICAgIGh0dHBSZXF1ZXN0LndyaXRlKHJlcXVlc3QuYm9keSk7XG4gICAgfVxuICAgIGh0dHBSZXF1ZXN0LmVuZCgpO1xuICB9KTtcbn07XG5cbk9BdXRoLnByb3RvdHlwZS5idWlsZFJlcXVlc3QgPSBmdW5jdGlvbiAobWV0aG9kLCBwYXRoLCBwYXJhbXMsIGJvZHkpIHtcbiAgaWYgKHBhdGguaW5kZXhPZignLycpICE9IDApIHtcbiAgICBwYXRoID0gJy8nICsgcGF0aDtcbiAgfVxuICBpZiAocGFyYW1zICYmIE9iamVjdC5rZXlzKHBhcmFtcykubGVuZ3RoID4gMCkge1xuICAgIHBhdGggKz0gJz8nICsgT0F1dGguYnVpbGRQYXJhbWV0ZXJTdHJpbmcocGFyYW1zKTtcbiAgfVxuXG4gIHZhciByZXF1ZXN0ID0ge1xuICAgIGhvc3Q6IHRoaXMuaG9zdCxcbiAgICBwYXRoOiBwYXRoLFxuICAgIG1ldGhvZDogbWV0aG9kLnRvVXBwZXJDYXNlKCksXG4gIH07XG5cbiAgdmFyIG9hdXRoX3BhcmFtcyA9IHRoaXMub2F1dGhfcGFyYW1zIHx8IHt9O1xuICBvYXV0aF9wYXJhbXMub2F1dGhfY29uc3VtZXJfa2V5ID0gdGhpcy5jb25zdW1lcl9rZXk7XG4gIGlmICh0aGlzLmF1dGhfdG9rZW4pIHtcbiAgICBvYXV0aF9wYXJhbXNbJ29hdXRoX3Rva2VuJ10gPSB0aGlzLmF1dGhfdG9rZW47XG4gIH1cblxuICByZXF1ZXN0ID0gT0F1dGguc2lnblJlcXVlc3QocmVxdWVzdCwgb2F1dGhfcGFyYW1zLCB0aGlzLmNvbnN1bWVyX3NlY3JldCwgdGhpcy5hdXRoX3Rva2VuX3NlY3JldCk7XG5cbiAgaWYgKGJvZHkgJiYgT2JqZWN0LmtleXMoYm9keSkubGVuZ3RoID4gMCkge1xuICAgIHJlcXVlc3QuYm9keSA9IE9BdXRoLmJ1aWxkUGFyYW1ldGVyU3RyaW5nKGJvZHkpO1xuICB9XG4gIHJldHVybiByZXF1ZXN0O1xufTtcblxuT0F1dGgucHJvdG90eXBlLmdldCA9IGZ1bmN0aW9uIChwYXRoLCBwYXJhbXMpIHtcbiAgcmV0dXJuIHRoaXMuc2VuZCgnR0VUJywgcGF0aCwgcGFyYW1zKTtcbn07XG5cbk9BdXRoLnByb3RvdHlwZS5wb3N0ID0gZnVuY3Rpb24gKHBhdGgsIHBhcmFtcywgYm9keSkge1xuICByZXR1cm4gdGhpcy5zZW5kKCdQT1NUJywgcGF0aCwgcGFyYW1zLCBib2R5KTtcbn07XG5cbi8qXG5cdFByb3BlciBzdHJpbmcgJWVzY2FwZSBlbmNvZGluZ1xuKi9cbk9BdXRoLmVuY29kZSA9IGZ1bmN0aW9uIChzdHIpIHtcbiAgLy8gICAgICAgZGlzY3VzcyBhdDogaHR0cDovL3BocGpzLm9yZy9mdW5jdGlvbnMvcmF3dXJsZW5jb2RlL1xuICAvLyAgICAgIG9yaWdpbmFsIGJ5OiBCcmV0dCBaYW1pciAoaHR0cDovL2JyZXR0LXphbWlyLm1lKVxuICAvLyAgICAgICAgIGlucHV0IGJ5OiB0cmF2Y1xuICAvLyAgICAgICAgIGlucHV0IGJ5OiBCcmV0dCBaYW1pciAoaHR0cDovL2JyZXR0LXphbWlyLm1lKVxuICAvLyAgICAgICAgIGlucHV0IGJ5OiBNaWNoYWVsIEdyaWVyXG4gIC8vICAgICAgICAgaW5wdXQgYnk6IFJhdGhlb3VzXG4gIC8vICAgICAgYnVnZml4ZWQgYnk6IEtldmluIHZhbiBab25uZXZlbGQgKGh0dHA6Ly9rZXZpbi52YW56b25uZXZlbGQubmV0KVxuICAvLyAgICAgIGJ1Z2ZpeGVkIGJ5OiBCcmV0dCBaYW1pciAoaHR0cDovL2JyZXR0LXphbWlyLm1lKVxuICAvLyAgICAgIGJ1Z2ZpeGVkIGJ5OiBKb3Jpc1xuICAvLyByZWltcGxlbWVudGVkIGJ5OiBCcmV0dCBaYW1pciAoaHR0cDovL2JyZXR0LXphbWlyLm1lKVxuICAvLyByZWltcGxlbWVudGVkIGJ5OiBCcmV0dCBaYW1pciAoaHR0cDovL2JyZXR0LXphbWlyLm1lKVxuICAvLyAgICAgICAgICAgICBub3RlOiBUaGlzIHJlZmxlY3RzIFBIUCA1LjMvNi4wKyBiZWhhdmlvclxuICAvLyAgICAgICAgICAgICBub3RlOiBQbGVhc2UgYmUgYXdhcmUgdGhhdCB0aGlzIGZ1bmN0aW9uIGV4cGVjdHMgdG8gZW5jb2RlIGludG8gVVRGLTggZW5jb2RlZCBzdHJpbmdzLCBhcyBmb3VuZCBvblxuICAvLyAgICAgICAgICAgICBub3RlOiBwYWdlcyBzZXJ2ZWQgYXMgVVRGLThcbiAgLy8gICAgICAgIGV4YW1wbGUgMTogcmF3dXJsZW5jb2RlKCdLZXZpbiB2YW4gWm9ubmV2ZWxkIScpO1xuICAvLyAgICAgICAgcmV0dXJucyAxOiAnS2V2aW4lMjB2YW4lMjBab25uZXZlbGQlMjEnXG4gIC8vICAgICAgICBleGFtcGxlIDI6IHJhd3VybGVuY29kZSgnaHR0cDovL2tldmluLnZhbnpvbm5ldmVsZC5uZXQvJyk7XG4gIC8vICAgICAgICByZXR1cm5zIDI6ICdodHRwJTNBJTJGJTJGa2V2aW4udmFuem9ubmV2ZWxkLm5ldCUyRidcbiAgLy8gICAgICAgIGV4YW1wbGUgMzogcmF3dXJsZW5jb2RlKCdodHRwOi8vd3d3Lmdvb2dsZS5ubC9zZWFyY2g/cT1waHAuanMmaWU9dXRmLTgmb2U9dXRmLTgmYXE9dCZybHM9Y29tLnVidW50dTplbi1VUzp1bm9mZmljaWFsJmNsaWVudD1maXJlZm94LWEnKTtcbiAgLy8gICAgICAgIHJldHVybnMgMzogJ2h0dHAlM0ElMkYlMkZ3d3cuZ29vZ2xlLm5sJTJGc2VhcmNoJTNGcSUzRHBocC5qcyUyNmllJTNEdXRmLTglMjZvZSUzRHV0Zi04JTI2YXElM0R0JTI2cmxzJTNEY29tLnVidW50dSUzQWVuLVVTJTNBdW5vZmZpY2lhbCUyNmNsaWVudCUzRGZpcmVmb3gtYSdcblxuICBzdHIgPSAoc3RyICsgJycpLnRvU3RyaW5nKCk7XG5cbiAgLy8gVGlsZGUgc2hvdWxkIGJlIGFsbG93ZWQgdW5lc2NhcGVkIGluIGZ1dHVyZSB2ZXJzaW9ucyBvZiBQSFAgKGFzIHJlZmxlY3RlZCBiZWxvdyksIGJ1dCBpZiB5b3Ugd2FudCB0byByZWZsZWN0IGN1cnJlbnRcbiAgLy8gUEhQIGJlaGF2aW9yLCB5b3Ugd291bGQgbmVlZCB0byBhZGQgXCIucmVwbGFjZSgvfi9nLCAnJTdFJyk7XCIgdG8gdGhlIGZvbGxvd2luZy5cbiAgcmV0dXJuIGVuY29kZVVSSUNvbXBvbmVudChzdHIpXG4gICAgLnJlcGxhY2UoLyEvZywgJyUyMScpXG4gICAgLnJlcGxhY2UoLycvZywgJyUyNycpXG4gICAgLnJlcGxhY2UoL1xcKC9nLCAnJTI4JylcbiAgICAucmVwbGFjZSgvXFwpL2csICclMjknKVxuICAgIC5yZXBsYWNlKC9cXCovZywgJyUyQScpO1xufTtcblxuT0F1dGguc2lnbmF0dXJlTWV0aG9kID0gJ0hNQUMtU0hBMSc7XG5PQXV0aC52ZXJzaW9uID0gJzEuMCc7XG5cbi8qXG5cdEdlbmVyYXRlIGEgbm9uY2VcbiovXG5PQXV0aC5ub25jZSA9IGZ1bmN0aW9uICgpIHtcbiAgdmFyIHRleHQgPSAnJztcbiAgdmFyIHBvc3NpYmxlID0gJ0FCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXowMTIzNDU2Nzg5JztcblxuICBmb3IgKHZhciBpID0gMDsgaSA8IDMwOyBpKyspIHsgdGV4dCArPSBwb3NzaWJsZS5jaGFyQXQoTWF0aC5mbG9vcihNYXRoLnJhbmRvbSgpICogcG9zc2libGUubGVuZ3RoKSk7IH1cblxuICByZXR1cm4gdGV4dDtcbn07XG5cbk9BdXRoLmJ1aWxkUGFyYW1ldGVyU3RyaW5nID0gZnVuY3Rpb24gKG9iaikge1xuICAvLyBTb3J0IGtleXMgYW5kIGVuY29kZSB2YWx1ZXNcbiAgaWYgKG9iaikge1xuICAgIHZhciBrZXlzID0gT2JqZWN0LmtleXMob2JqKS5zb3J0KCk7XG5cbiAgICAvLyBNYXAga2V5PXZhbHVlLCBqb2luIHRoZW0gYnkgJlxuICAgIHJldHVybiBrZXlzXG4gICAgICAubWFwKGZ1bmN0aW9uIChrZXkpIHtcbiAgICAgICAgcmV0dXJuIGtleSArICc9JyArIE9BdXRoLmVuY29kZShvYmpba2V5XSk7XG4gICAgICB9KVxuICAgICAgLmpvaW4oJyYnKTtcbiAgfVxuXG4gIHJldHVybiAnJztcbn07XG5cbi8qXG5cdEJ1aWxkIHRoZSBzaWduYXR1cmUgc3RyaW5nIGZyb20gdGhlIG9iamVjdFxuKi9cblxuT0F1dGguYnVpbGRTaWduYXR1cmVTdHJpbmcgPSBmdW5jdGlvbiAobWV0aG9kLCB1cmwsIHBhcmFtZXRlcnMpIHtcbiAgcmV0dXJuIFttZXRob2QudG9VcHBlckNhc2UoKSwgT0F1dGguZW5jb2RlKHVybCksIE9BdXRoLmVuY29kZShwYXJhbWV0ZXJzKV0uam9pbignJicpO1xufTtcblxuLypcblx0UmV0dW5zIGVuY29kZWQgSE1BQy1TSEExIGZyb20ga2V5IGFuZCB0ZXh0XG4qL1xuT0F1dGguc2lnbmF0dXJlID0gZnVuY3Rpb24gKHRleHQsIGtleSkge1xuICBjcnlwdG8gPSByZXF1aXJlKCdjcnlwdG8nKTtcbiAgcmV0dXJuIE9BdXRoLmVuY29kZShjcnlwdG8uY3JlYXRlSG1hYygnc2hhMScsIGtleSkudXBkYXRlKHRleHQpLmRpZ2VzdCgnYmFzZTY0JykpO1xufTtcblxuT0F1dGguc2lnblJlcXVlc3QgPSBmdW5jdGlvbiAocmVxdWVzdCwgb2F1dGhfcGFyYW1ldGVycywgY29uc3VtZXJfc2VjcmV0LCBhdXRoX3Rva2VuX3NlY3JldCkge1xuICBvYXV0aF9wYXJhbWV0ZXJzID0gb2F1dGhfcGFyYW1ldGVycyB8fCB7fTtcblxuICAvLyBTZXQgZGVmYXVsdCB2YWx1ZXNcbiAgaWYgKCFvYXV0aF9wYXJhbWV0ZXJzLm9hdXRoX25vbmNlKSB7XG4gICAgb2F1dGhfcGFyYW1ldGVycy5vYXV0aF9ub25jZSA9IE9BdXRoLm5vbmNlKCk7XG4gIH1cbiAgaWYgKCFvYXV0aF9wYXJhbWV0ZXJzLm9hdXRoX3RpbWVzdGFtcCkge1xuICAgIG9hdXRoX3BhcmFtZXRlcnMub2F1dGhfdGltZXN0YW1wID0gTWF0aC5mbG9vcihuZXcgRGF0ZSgpLmdldFRpbWUoKSAvIDEwMDApO1xuICB9XG4gIGlmICghb2F1dGhfcGFyYW1ldGVycy5vYXV0aF9zaWduYXR1cmVfbWV0aG9kKSB7XG4gICAgb2F1dGhfcGFyYW1ldGVycy5vYXV0aF9zaWduYXR1cmVfbWV0aG9kID0gT0F1dGguc2lnbmF0dXJlTWV0aG9kO1xuICB9XG4gIGlmICghb2F1dGhfcGFyYW1ldGVycy5vYXV0aF92ZXJzaW9uKSB7XG4gICAgb2F1dGhfcGFyYW1ldGVycy5vYXV0aF92ZXJzaW9uID0gT0F1dGgudmVyc2lvbjtcbiAgfVxuXG4gIGlmICghYXV0aF90b2tlbl9zZWNyZXQpIHtcbiAgICBhdXRoX3Rva2VuX3NlY3JldCA9ICcnO1xuICB9XG4gIC8vIEZvcmNlIEdFVCBtZXRob2QgaWYgdW5zZXRcbiAgaWYgKCFyZXF1ZXN0Lm1ldGhvZCkge1xuICAgIHJlcXVlc3QubWV0aG9kID0gJ0dFVCc7XG4gIH1cblxuICAvLyBDb2xsZWN0ICBhbGwgdGhlIHBhcmFtZXRlcnMgaW4gb25lIHNpZ25hdHVyZVBhcmFtZXRlcnMgb2JqZWN0XG4gIHZhciBzaWduYXR1cmVQYXJhbXMgPSB7fTtcbiAgdmFyIHBhcmFtZXRlcnNUb01lcmdlID0gW3JlcXVlc3QucGFyYW1zLCByZXF1ZXN0LmJvZHksIG9hdXRoX3BhcmFtZXRlcnNdO1xuICBmb3IgKHZhciBpIGluIHBhcmFtZXRlcnNUb01lcmdlKSB7XG4gICAgdmFyIHBhcmFtZXRlcnMgPSBwYXJhbWV0ZXJzVG9NZXJnZVtpXTtcbiAgICBmb3IgKHZhciBrIGluIHBhcmFtZXRlcnMpIHtcbiAgICAgIHNpZ25hdHVyZVBhcmFtc1trXSA9IHBhcmFtZXRlcnNba107XG4gICAgfVxuICB9XG5cbiAgLy8gQ3JlYXRlIGEgc3RyaW5nIGJhc2VkIG9uIHRoZSBwYXJhbWV0ZXJzXG4gIHZhciBwYXJhbWV0ZXJTdHJpbmcgPSBPQXV0aC5idWlsZFBhcmFtZXRlclN0cmluZyhzaWduYXR1cmVQYXJhbXMpO1xuXG4gIC8vIEJ1aWxkIHRoZSBzaWduYXR1cmUgc3RyaW5nXG4gIHZhciB1cmwgPSAnaHR0cHM6Ly8nICsgcmVxdWVzdC5ob3N0ICsgJycgKyByZXF1ZXN0LnBhdGg7XG5cbiAgdmFyIHNpZ25hdHVyZVN0cmluZyA9IE9BdXRoLmJ1aWxkU2lnbmF0dXJlU3RyaW5nKHJlcXVlc3QubWV0aG9kLCB1cmwsIHBhcmFtZXRlclN0cmluZyk7XG4gIC8vIEhhc2ggdGhlIHNpZ25hdHVyZSBzdHJpbmdcbiAgdmFyIHNpZ25hdHVyZUtleSA9IFtPQXV0aC5lbmNvZGUoY29uc3VtZXJfc2VjcmV0KSwgT0F1dGguZW5jb2RlKGF1dGhfdG9rZW5fc2VjcmV0KV0uam9pbignJicpO1xuXG4gIHZhciBzaWduYXR1cmUgPSBPQXV0aC5zaWduYXR1cmUoc2lnbmF0dXJlU3RyaW5nLCBzaWduYXR1cmVLZXkpO1xuXG4gIC8vIFNldCB0aGUgc2lnbmF0dXJlIGluIHRoZSBwYXJhbXNcbiAgb2F1dGhfcGFyYW1ldGVycy5vYXV0aF9zaWduYXR1cmUgPSBzaWduYXR1cmU7XG4gIGlmICghcmVxdWVzdC5oZWFkZXJzKSB7XG4gICAgcmVxdWVzdC5oZWFkZXJzID0ge307XG4gIH1cblxuICAvLyBTZXQgdGhlIGF1dGhvcml6YXRpb24gaGVhZGVyXG4gIHZhciBhdXRoSGVhZGVyID0gT2JqZWN0LmtleXMob2F1dGhfcGFyYW1ldGVycylcbiAgICAuc29ydCgpXG4gICAgLm1hcChmdW5jdGlvbiAoa2V5KSB7XG4gICAgICB2YXIgdmFsdWUgPSBvYXV0aF9wYXJhbWV0ZXJzW2tleV07XG4gICAgICByZXR1cm4ga2V5ICsgJz1cIicgKyB2YWx1ZSArICdcIic7XG4gICAgfSlcbiAgICAuam9pbignLCAnKTtcblxuICByZXF1ZXN0LmhlYWRlcnMuQXV0aG9yaXphdGlvbiA9ICdPQXV0aCAnICsgYXV0aEhlYWRlcjtcblxuICAvLyBTZXQgdGhlIGNvbnRlbnQgdHlwZSBoZWFkZXJcbiAgcmVxdWVzdC5oZWFkZXJzWydDb250ZW50LVR5cGUnXSA9ICdhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQnO1xuICByZXR1cm4gcmVxdWVzdDtcbn07XG5cbm1vZHVsZS5leHBvcnRzID0gT0F1dGg7XG4iXSwibWFwcGluZ3MiOiI7O0FBQUEsSUFBSUEsS0FBSyxHQUFHQyxPQUFPLENBQUMsT0FBTyxDQUFDO0VBQzFCQyxNQUFNLEdBQUdELE9BQU8sQ0FBQyxRQUFRLENBQUM7QUFDNUIsSUFBSUUsS0FBSyxHQUFHRixPQUFPLENBQUMsWUFBWSxDQUFDLENBQUNFLEtBQUs7QUFFdkMsSUFBSUMsS0FBSyxHQUFHLFNBQUFBLENBQVVDLE9BQU8sRUFBRTtFQUM3QixJQUFJLENBQUNBLE9BQU8sRUFBRTtJQUNaLE1BQU0sSUFBSUYsS0FBSyxDQUFDRyxLQUFLLENBQUNILEtBQUssQ0FBQ0csS0FBSyxDQUFDQyxxQkFBcUIsRUFBRSw0QkFBNEIsQ0FBQztFQUN4RjtFQUNBLElBQUksQ0FBQ0MsWUFBWSxHQUFHSCxPQUFPLENBQUNHLFlBQVk7RUFDeEMsSUFBSSxDQUFDQyxl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
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Parse Server authentication adapter for Apple.
|
|
5
|
+
*
|
|
6
|
+
* @class AppleAdapter
|
|
7
|
+
* @param {Object} options - Configuration options for the adapter.
|
|
8
|
+
* @param {string} options.clientId - Your Apple App ID.
|
|
9
|
+
*
|
|
10
|
+
* @param {Object} authData - The authentication data provided by the client.
|
|
11
|
+
* @param {string} authData.id - The user ID obtained from Apple.
|
|
12
|
+
* @param {string} authData.token - The token obtained from Apple.
|
|
13
|
+
*
|
|
14
|
+
* @description
|
|
15
|
+
* ## Parse Server Configuration
|
|
16
|
+
* To configure Parse Server for Apple authentication, use the following structure:
|
|
17
|
+
* ```json
|
|
18
|
+
* {
|
|
19
|
+
* "auth": {
|
|
20
|
+
* "apple": {
|
|
21
|
+
* "clientId": "12345"
|
|
22
|
+
* }
|
|
23
|
+
* }
|
|
24
|
+
* }
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* ## Expected `authData` from the Client
|
|
28
|
+
* The adapter expects the client to provide the following `authData` payload:
|
|
29
|
+
* - `authData.id` (**string**, required): The user ID obtained from Apple.
|
|
30
|
+
* - `authData.token` (**string**, required): The token obtained from Apple.
|
|
31
|
+
*
|
|
32
|
+
* Parse Server stores the required authentication data in the database.
|
|
33
|
+
*
|
|
34
|
+
* ### Example AuthData from Apple
|
|
35
|
+
* ```json
|
|
36
|
+
* {
|
|
37
|
+
* "apple": {
|
|
38
|
+
* "id": "1234567",
|
|
39
|
+
* "token": "xxxxx.yyyyy.zzzzz"
|
|
40
|
+
* }
|
|
41
|
+
* }
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
* @see {@link https://developer.apple.com/documentation/signinwithapplerestapi Sign in with Apple REST API Documentation}
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
// Apple SignIn Auth
|
|
48
|
+
// https://developer.apple.com/documentation/signinwithapplerestapi
|
|
49
|
+
|
|
50
|
+
const Parse = require('parse/node').Parse;
|
|
51
|
+
const jwksClient = require('jwks-rsa');
|
|
52
|
+
const jwt = require('jsonwebtoken');
|
|
53
|
+
const authUtils = require('./utils');
|
|
54
|
+
const TOKEN_ISSUER = 'https://appleid.apple.com';
|
|
55
|
+
const getAppleKeyByKeyId = async (keyId, cacheMaxEntries, cacheMaxAge) => {
|
|
56
|
+
const client = jwksClient({
|
|
57
|
+
jwksUri: `${TOKEN_ISSUER}/auth/keys`,
|
|
58
|
+
cache: true,
|
|
59
|
+
cacheMaxEntries,
|
|
60
|
+
cacheMaxAge
|
|
61
|
+
});
|
|
62
|
+
let key;
|
|
63
|
+
try {
|
|
64
|
+
key = await authUtils.getSigningKey(client, keyId);
|
|
65
|
+
} catch {
|
|
66
|
+
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `Unable to find matching key for Key ID: ${keyId}`);
|
|
67
|
+
}
|
|
68
|
+
return key;
|
|
69
|
+
};
|
|
70
|
+
const verifyIdToken = async ({
|
|
71
|
+
token,
|
|
72
|
+
id
|
|
73
|
+
}, {
|
|
74
|
+
clientId,
|
|
75
|
+
cacheMaxEntries,
|
|
76
|
+
cacheMaxAge
|
|
77
|
+
}) => {
|
|
78
|
+
if (!clientId) {
|
|
79
|
+
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Apple auth is not configured.');
|
|
80
|
+
}
|
|
81
|
+
if (!token) {
|
|
82
|
+
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `id token is invalid for this user.`);
|
|
83
|
+
}
|
|
84
|
+
const {
|
|
85
|
+
kid: keyId
|
|
86
|
+
} = authUtils.getHeaderFromToken(token);
|
|
87
|
+
const ONE_HOUR_IN_MS = 3600000;
|
|
88
|
+
let jwtClaims;
|
|
89
|
+
cacheMaxAge = cacheMaxAge || ONE_HOUR_IN_MS;
|
|
90
|
+
cacheMaxEntries = cacheMaxEntries || 5;
|
|
91
|
+
const appleKey = await getAppleKeyByKeyId(keyId, cacheMaxEntries, cacheMaxAge);
|
|
92
|
+
const signingKey = appleKey.publicKey || appleKey.rsaPublicKey;
|
|
93
|
+
try {
|
|
94
|
+
jwtClaims = jwt.verify(token, signingKey, {
|
|
95
|
+
algorithms: ['RS256'],
|
|
96
|
+
// the audience can be checked against a string, a regular expression or a list of strings and/or regular expressions.
|
|
97
|
+
audience: clientId
|
|
98
|
+
});
|
|
99
|
+
} catch (exception) {
|
|
100
|
+
const message = exception.message;
|
|
101
|
+
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `${message}`);
|
|
102
|
+
}
|
|
103
|
+
if (jwtClaims.iss !== TOKEN_ISSUER) {
|
|
104
|
+
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `id token not issued by correct OpenID provider - expected: ${TOKEN_ISSUER} | from: ${jwtClaims.iss}`);
|
|
105
|
+
}
|
|
106
|
+
if (jwtClaims.sub !== id) {
|
|
107
|
+
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `auth data is invalid for this user.`);
|
|
108
|
+
}
|
|
109
|
+
return jwtClaims;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// Returns a promise that fulfills if this id token is valid
|
|
113
|
+
function validateAuthData(authData, options = {}) {
|
|
114
|
+
return verifyIdToken(authData, options);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Returns a promise that fulfills if this app id is valid.
|
|
118
|
+
function validateAppId() {
|
|
119
|
+
return Promise.resolve();
|
|
120
|
+
}
|
|
121
|
+
module.exports = {
|
|
122
|
+
validateAppId,
|
|
123
|
+
validateAuthData
|
|
124
|
+
};
|
|
125
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJQYXJzZSIsInJlcXVpcmUiLCJqd2tzQ2xpZW50Iiwiand0IiwiYXV0aFV0aWxzIiwiVE9LRU5fSVNTVUVSIiwiZ2V0QXBwbGVLZXlCeUtleUlkIiwia2V5SWQiLCJjYWNoZU1heEVudHJpZXMiLCJjYWNoZU1heEFnZSIsImNsaWVudCIsImp3a3NVcmkiLCJjYWNoZSIsImtleSIsImdldFNpZ25pbmdLZXkiLCJFcnJvciIsIk9CSkVDVF9OT1RfRk9VTkQiLCJ2ZXJpZnlJZFRva2VuIiwidG9rZW4iLCJpZCIsImNsaWVudElkIiwia2lkIiwiZ2V0SGVhZGVyRnJvbVRva2VuIiwiT05FX0hPVVJfSU5fTVMiLCJqd3RDbGFpbXMiLCJhcHBsZUtleSIsInNpZ25pbmdLZXkiLCJwdWJsaWNLZXkiLCJyc2FQdWJsaWNLZXkiLCJ2ZXJpZnkiLCJhbGdvcml0aG1zIiwiYXVkaWVuY2UiLCJleGNlcHRpb24iLCJtZXNzYWdlIiwiaXNzIiwic3ViIiwidmFsaWRhdGVBdXRoRGF0YSIsImF1dGhEYXRhIiwib3B0aW9ucyIsInZhbGlkYXRlQXBwSWQiLCJQcm9taXNlIiwicmVzb2x2ZSIsIm1vZHVsZSIsImV4cG9ydHMiXSwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvQWRhcHRlcnMvQXV0aC9hcHBsZS5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFBhcnNlIFNlcnZlciBhdXRoZW50aWNhdGlvbiBhZGFwdGVyIGZvciBBcHBsZS5cbiAqXG4gKiBAY2xhc3MgQXBwbGVBZGFwdGVyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIENvbmZpZ3VyYXRpb24gb3B0aW9ucyBmb3IgdGhlIGFkYXB0ZXIuXG4gKiBAcGFyYW0ge3N0cmluZ30gb3B0aW9ucy5jbGllbnRJZCAtIFlvdXIgQXBwbGUgQXBwIElELlxuICpcbiAqIEBwYXJhbSB7T2JqZWN0fSBhdXRoRGF0YSAtIFRoZSBhdXRoZW50aWNhdGlvbiBkYXRhIHByb3ZpZGVkIGJ5IHRoZSBjbGllbnQuXG4gKiBAcGFyYW0ge3N0cmluZ30gYXV0aERhdGEuaWQgLSBUaGUgdXNlciBJRCBvYnRhaW5lZCBmcm9tIEFwcGxlLlxuICogQHBhcmFtIHtzdHJpbmd9IGF1dGhEYXRhLnRva2VuIC0gVGhlIHRva2VuIG9idGFpbmVkIGZyb20gQXBwbGUuXG4gKlxuICogQGRlc2NyaXB0aW9uXG4gKiAjIyBQYXJzZSBTZXJ2ZXIgQ29uZmlndXJhdGlvblxuICogVG8gY29uZmlndXJlIFBhcnNlIFNlcnZlciBmb3IgQXBwbGUgYXV0aGVudGljYXRpb24sIHVzZSB0aGUgZm9sbG93aW5nIHN0cnVjdHVyZTpcbiAqIGBgYGpzb25cbiAqIHtcbiAqICAgXCJhdXRoXCI6IHtcbiAqICAgICBcImFwcGxlXCI6IHtcbiAqICAgICAgIFwiY2xpZW50SWRcIjogXCIxMjM0NVwiXG4gKiAgICAgfVxuICogICB9XG4gKiB9XG4gKiBgYGBcbiAqXG4gKiAjIyBFeHBlY3RlZCBgYXV0aERhdGFgIGZyb20gdGhlIENsaWVudFxuICogVGhlIGFkYXB0ZXIgZXhwZWN0cyB0aGUgY2xpZW50IHRvIHByb3ZpZGUgdGhlIGZvbGxvd2luZyBgYXV0aERhdGFgIHBheWxvYWQ6XG4gKiAtIGBhdXRoRGF0YS5pZGAgKCoqc3RyaW5nKiosIHJlcXVpcmVkKTogVGhlIHVzZXIgSUQgb2J0YWluZWQgZnJvbSBBcHBsZS5cbiAqIC0gYGF1dGhEYXRhLnRva2VuYCAoKipzdHJpbmcqKiwgcmVxdWlyZWQpOiBUaGUgdG9rZW4gb2J0YWluZWQgZnJvbSBBcHBsZS5cbiAqXG4gKiBQYXJzZSBTZXJ2ZXIgc3RvcmVzIHRoZSByZXF1aXJlZCBhdXRoZW50aWNhdGlvbiBkYXRhIGluIHRoZSBkYXRhYmFzZS5cbiAqXG4gKiAjIyMgRXhhbXBsZSBBdXRoRGF0YSBmcm9tIEFwcGxlXG4gKiBgYGBqc29uXG4gKiB7XG4gKiAgIFwiYXBwbGVcIjoge1xuICogICAgIFwiaWRcIjogXCIxMjM0NTY3XCIsXG4gKiAgICAgXCJ0b2tlblwiOiBcInh4eHh4Lnl5eXl5Lnp6enp6XCJcbiAqICAgfVxuICogfVxuICogYGBgXG4gKlxuICogQHNlZSB7QGxpbmsgaHR0cHM6Ly9kZXZlbG9wZXIuYXBwbGUuY29tL2RvY3VtZW50YXRpb24vc2lnbmlud2l0aGFwcGxlcmVzdGFwaSBTaWduIGluIHdpdGggQXBwbGUgUkVTVCBBUEkgRG9jdW1lbnRhdGlvbn1cbiAqL1xuXG4vLyBBcHBsZSBTaWduSW4gQXV0aFxuLy8gaHR0cHM6Ly9kZXZlbG9wZXIuYXBwbGUuY29tL2RvY3VtZW50YXRpb24vc2lnbmlud2l0aGFwcGxlcmVzdGFwaVxuXG5jb25zdCBQYXJzZSA9IHJlcXVpcmUoJ3BhcnNlL25vZGUnKS5QYXJzZTtcbmNvbnN0IGp3a3NDbGllbnQgPSByZXF1aXJlKCdqd2tzLXJzYScpO1xuY29uc3Qgand0ID0gcmVxdWlyZSgnanNvbndlYnRva2VuJyk7XG5jb25zdCBhdXRoVXRpbHMgPSByZXF1aXJlKCcuL3V0aWxzJyk7XG5cbmNvbnN0IFRPS0VOX0lTU1VFUiA9ICdodHRwczovL2FwcGxlaWQuYXBwbGUuY29tJztcblxuY29uc3QgZ2V0QXBwbGVLZXlCeUtleUlkID0gYXN5bmMgKGtleUlkLCBjYWNoZU1heEVudHJpZXMsIGNhY2hlTWF4QWdlKSA9PiB7XG4gIGNvbnN0IGNsaWVudCA9IGp3a3NDbGllbnQoe1xuICAgIGp3a3NVcmk6IGAke1RPS0VOX0lTU1VFUn0vYXV0aC9rZXlzYCxcbiAgICBjYWNoZTogdHJ1ZSxcbiAgICBjYWNoZU1heEVudHJpZXMsXG4gICAgY2FjaGVNYXhBZ2UsXG4gIH0pO1xuXG4gIGxldCBrZXk7XG4gIHRyeSB7XG4gICAga2V5ID0gYXdhaXQgYXV0aFV0aWxzLmdldFNpZ25pbmdLZXkoY2xpZW50LCBrZXlJZCk7XG4gIH0gY2F0Y2gge1xuICAgIHRocm93IG5ldyBQYXJzZS5FcnJvcihcbiAgICAgIFBhcnNlLkVycm9yLk9CSkVDVF9OT1RfRk9VTkQsXG4gICAgICBgVW5hYmxlIHRvIGZpbmQgbWF0Y2hpbmcga2V5IGZvciBLZXkgSUQ6ICR7a2V5SWR9YFxuICAgICk7XG4gIH1cbiAgcmV0dXJuIGtleTtcbn07XG5cbmNvbnN0IHZlcmlmeUlkVG9rZW4gPSBhc3luYyAoeyB0b2tlbiwgaWQgfSwgeyBjbGllbnRJZCwgY2FjaGVNYXhFbnRyaWVzLCBjYWNoZU1heEFnZSB9KSA9PiB7XG4gIGlmICghY2xpZW50SWQpIHtcbiAgICB0aHJvdyBuZXcgUGFyc2UuRXJyb3IoXG4gICAgICBQYXJzZS5FcnJvci5PQkpFQ1RfTk9UX0ZPVU5ELFxuICAgICAgJ0FwcGxlIGF1dGggaXMgbm90IGNvbmZpZ3VyZWQuJ1xuICAgICk7XG4gIH1cblxuICBpZiAoIXRva2VuKSB7XG4gICAgdGhyb3cgbmV3IFBhcnNlLkVycm9yKFBhcnNlLkVycm9yLk9CSkVDVF9OT1RfRk9VTkQsIGBpZCB0b2tlbiBpcyBpbnZhbGlkIGZvciB0aGlzIHVzZXIuYCk7XG4gIH1cblxuICBjb25zdCB7IGtpZDoga2V5SWQgfSA9IGF1dGhVdGlscy5nZXRIZWFkZXJGcm9tVG9rZW4odG9rZW4pO1xuICBjb25zdCBPTkVfSE9VUl9JTl9NUyA9IDM2MDAwMDA7XG4gIGxldCBqd3RDbGFpbXM7XG5cbiAgY2FjaGVNYXhBZ2UgPSBjYWNoZU1heEFnZSB8fCBPTkVfSE9VUl9JTl9NUztcbiAgY2FjaGVNYXhFbnRyaWVzID0gY2FjaGVNYXhFbnRyaWVzIHx8IDU7XG5cbiAgY29uc3QgYXBwbGVLZXkgPSBhd2FpdCBnZXRBcHBsZUtleUJ5S2V5SWQoa2V5SWQsIGNhY2hlTWF4RW50cmllcywgY2FjaGVNYXhBZ2UpO1xuICBjb25zdCBzaWduaW5nS2V5ID0gYXBwbGVLZXkucHVibGljS2V5IHx8IGFwcGxlS2V5LnJzYVB1YmxpY0tleTtcblxuICB0cnkge1xuICAgIGp3dENsYWltcyA9IGp3dC52ZXJpZnkodG9rZW4sIHNpZ25pbmdLZXksIHtcbiAgICAgIGFsZ29yaXRobXM6IFsnUlMyNTYnXSxcbiAgICAgIC8vIHRoZSBhdWRpZW5jZSBjYW4gYmUgY2hlY2tlZCBhZ2FpbnN0IGEgc3RyaW5nLCBhIHJlZ3VsYXIgZXhwcmVzc2lvbiBvciBhIGxpc3Qgb2Ygc3RyaW5ncyBhbmQvb3IgcmVndWxhciBleHByZXNzaW9ucy5cbiAgICAgIGF1ZGllbmNlOiBjbGllbnRJZCxcbiAgICB9KTtcbiAgfSBjYXRjaCAoZXhjZXB0aW9uKSB7XG4gICAgY29uc3QgbWVzc2FnZSA9IGV4Y2VwdGlvbi5tZXNzYWdlO1xuXG4gICAgdGhyb3cgbmV3IFBhcnNlLkVycm9yKFBhcnNlLkVycm9yLk9CSkVDVF9OT1RfRk9VTkQsIGAke21lc3NhZ2V9YCk7XG4gIH1cblxuICBpZiAoand0Q2xhaW1zLmlzcyAhPT0gVE9LRU5fSVNTVUVSKSB7XG4gICAgdGhyb3cgbmV3IFBhcnNlLkVycm9yKFxuICAgICAgUGFyc2UuRXJyb3IuT0JKRUNUX05PVF9GT1VORCxcbiAgICAgIGBpZCB0b2tlbiBub3QgaXNzdWVkIGJ5IGNvcnJlY3QgT3BlbklEIHByb3ZpZGVyIC0gZXhwZWN0ZWQ6ICR7VE9LRU5fSVNTVUVSfSB8IGZyb206ICR7and0Q2xhaW1zLmlzc31gXG4gICAgKTtcbiAgfVxuXG4gIGlmIChqd3RDbGFpbXMuc3ViICE9PSBpZCkge1xuICAgIHRocm93IG5ldyBQYXJzZS5FcnJvcihQYXJzZS5FcnJvci5PQkpFQ1RfTk9UX0ZPVU5ELCBgYXV0aCBkYXRhIGlzIGludmFsaWQgZm9yIHRoaXMgdXNlci5gKTtcbiAgfVxuICByZXR1cm4gand0Q2xhaW1zO1xufTtcblxuLy8gUmV0dXJucyBhIHByb21pc2UgdGhhdCBmdWxmaWxscyBpZiB0aGlzIGlkIHRva2VuIGlzIHZhbGlkXG5mdW5jdGlvbiB2YWxpZGF0ZUF1dGhEYXRhKGF1dGhEYXRhLCBvcHRpb25zID0ge30pIHtcbiAgcmV0dXJuIHZlcmlmeUlkVG9rZW4oYXV0aERhdGEsIG9wdGlvbnMpO1xufVxuXG4vLyBSZXR1cm5zIGEgcHJvbWlzZSB0aGF0IGZ1bGZpbGxzIGlmIHRoaXMgYXBwIGlkIGlzIHZhbGlkLlxuZnVuY3Rpb24gdmFsaWRhdGVBcHBJZCgpIHtcbiAgcmV0dXJuIFByb21pc2UucmVzb2x2ZSgpO1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IHtcbiAgdmFsaWRhdGVBcHBJZCxcbiAgdmFsaWRhdGVBdXRoRGF0YSxcbn07XG4iXSwibWFwcGluZ3MiOiI7O0FBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQSxNQUFNQSxLQUFLLEdBQUdDLE9BQU8sQ0FBQyxZQUFZLENBQUMsQ0FBQ0QsS0FBSztBQUN6QyxNQUFNRSxVQUFVLEdBQUdELE9BQU8sQ0FBQyxVQUFVLENBQUM7QUFDdEMsTUFBTUUsR0FBRyxHQUFHRixPQUFPLENBQUMsY0FBYyxDQUFDO0FBQ25DLE1BQU1HLFNBQVMsR0FBR0gsT0FBTyxDQUFDLFNBQVMsQ0FBQztBQUVwQyxNQUFNSSxZQUFZLEdBQUcsMkJBQTJCO0FBRWhELE1BQU1DLGtCQUFrQixHQUFHLE1BQUFBLENBQU9DLEtBQUssRUFBRUMsZUFBZSxFQUFFQyxXQUFXLEtBQUs7RUFDeEUsTUFBTUMsTUFBTSxHQUFHUixVQUFVLENBQUM7SUFDeEJTLE9BQU8sRUFBRSxHQUFHTixZQUFZLFlBQVk7SUFDcENPLEtBQUssRUFBRSxJQUFJO0lBQ1hKLGVBQWU7SUFDZkM7RUFDRixDQUFDLENBQUM7RUFFRixJQUFJSSxHQUFHO0VBQ1AsSUFBSTtJQUNGQSxHQUFHLEdBQUcsTUFBTVQsU0FBUyxDQUFDVSxhQUFhLENBQUNKLE1BQU0sRUFBRUgsS0FBSyxDQUFDO0VBQ3BELENBQUMsQ0FBQyxNQUFNO0lBQ04sTUFBTSxJQUFJUCxLQUFLLENBQUNlLEtBQUssQ0FDbkJmLEtBQUssQ0FBQ2UsS0FBSyxDQUFD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
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Parse Server authentication adapter for Facebook.
|
|
5
|
+
*
|
|
6
|
+
* @class FacebookAdapter
|
|
7
|
+
* @param {Object} options - The adapter configuration options.
|
|
8
|
+
* @param {string} options.appSecret - Your Facebook App Secret. Required for secure authentication.
|
|
9
|
+
* @param {string[]} options.appIds - An array of Facebook App IDs. Required for validating the app.
|
|
10
|
+
*
|
|
11
|
+
* @description
|
|
12
|
+
* ## Parse Server Configuration
|
|
13
|
+
* To configure Parse Server for Facebook authentication, use the following structure:
|
|
14
|
+
* ```json
|
|
15
|
+
* {
|
|
16
|
+
* "auth": {
|
|
17
|
+
* "facebook": {
|
|
18
|
+
* "appSecret": "your-app-secret",
|
|
19
|
+
* "appIds": ["your-app-id"]
|
|
20
|
+
* }
|
|
21
|
+
* }
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* The adapter supports the following authentication methods:
|
|
26
|
+
* - **Standard Login**: Requires `id` and `access_token`.
|
|
27
|
+
* - **Limited Login**: Requires `id` and `token`.
|
|
28
|
+
*
|
|
29
|
+
* ## Auth Payloads
|
|
30
|
+
* ### Standard Login Payload
|
|
31
|
+
* ```json
|
|
32
|
+
* {
|
|
33
|
+
* "facebook": {
|
|
34
|
+
* "id": "1234567",
|
|
35
|
+
* "access_token": "abc123def456ghi789"
|
|
36
|
+
* }
|
|
37
|
+
* }
|
|
38
|
+
* ```
|
|
39
|
+
*
|
|
40
|
+
* ### Limited Login Payload
|
|
41
|
+
* ```json
|
|
42
|
+
* {
|
|
43
|
+
* "facebook": {
|
|
44
|
+
* "id": "1234567",
|
|
45
|
+
* "token": "xxxxx.yyyyy.zzzzz"
|
|
46
|
+
* }
|
|
47
|
+
* }
|
|
48
|
+
* ```
|
|
49
|
+
*
|
|
50
|
+
* ## Notes
|
|
51
|
+
* - **Standard Login**: Use `id` and `access_token` for full functionality.
|
|
52
|
+
* - **Limited Login**: Use `id` and `token` (JWT) when tracking is opted out (e.g., via Apple's App Tracking Transparency).
|
|
53
|
+
* - Supported Parse Server versions:
|
|
54
|
+
* - `>= 6.5.6 < 7`
|
|
55
|
+
* - `>= 7.0.1`
|
|
56
|
+
*
|
|
57
|
+
* @see {@link https://developers.facebook.com/docs/facebook-login/limited-login/ Facebook Limited Login}
|
|
58
|
+
* @see {@link https://developers.facebook.com/docs/facebook-login/facebook-login-for-business/ Facebook Login for Business}
|
|
59
|
+
*/
|
|
60
|
+
|
|
61
|
+
// Helper functions for accessing the Facebook Graph API.
|
|
62
|
+
const Parse = require('parse/node').Parse;
|
|
63
|
+
const crypto = require('crypto');
|
|
64
|
+
const jwksClient = require('jwks-rsa');
|
|
65
|
+
const jwt = require('jsonwebtoken');
|
|
66
|
+
const httpsRequest = require('./httpsRequest');
|
|
67
|
+
const authUtils = require('./utils');
|
|
68
|
+
const TOKEN_ISSUER = 'https://www.facebook.com';
|
|
69
|
+
function getAppSecretPath(authData, options = {}) {
|
|
70
|
+
const appSecret = options.appSecret;
|
|
71
|
+
if (!appSecret) {
|
|
72
|
+
return '';
|
|
73
|
+
}
|
|
74
|
+
const appsecret_proof = crypto.createHmac('sha256', appSecret).update(authData.access_token).digest('hex');
|
|
75
|
+
return `&appsecret_proof=${appsecret_proof}`;
|
|
76
|
+
}
|
|
77
|
+
function validateGraphToken(authData, options = {}) {
|
|
78
|
+
if (!Array.isArray(options.appIds) || !options.appIds.length) {
|
|
79
|
+
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Facebook auth is not configured.');
|
|
80
|
+
}
|
|
81
|
+
return graphRequest('me?fields=id&access_token=' + authData.access_token + getAppSecretPath(authData, options)).then(data => {
|
|
82
|
+
if (data && data.id == authData.id || process.env.TESTING && authData.id === 'test') {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Facebook auth is invalid for this user.');
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
async function validateGraphAppId(appIds, authData, options) {
|
|
89
|
+
var access_token = authData.access_token;
|
|
90
|
+
if (process.env.TESTING && access_token === 'test') {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (!Array.isArray(appIds)) {
|
|
94
|
+
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'appIds must be an array.');
|
|
95
|
+
}
|
|
96
|
+
if (!appIds.length) {
|
|
97
|
+
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Facebook auth is not configured.');
|
|
98
|
+
}
|
|
99
|
+
const data = await graphRequest(`app?access_token=${access_token}${getAppSecretPath(authData, options)}`);
|
|
100
|
+
if (!data || !appIds.includes(data.id)) {
|
|
101
|
+
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Facebook auth is invalid for this user.');
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const getFacebookKeyByKeyId = async (keyId, cacheMaxEntries, cacheMaxAge) => {
|
|
105
|
+
const client = jwksClient({
|
|
106
|
+
jwksUri: `${TOKEN_ISSUER}/.well-known/oauth/openid/jwks/`,
|
|
107
|
+
cache: true,
|
|
108
|
+
cacheMaxEntries,
|
|
109
|
+
cacheMaxAge
|
|
110
|
+
});
|
|
111
|
+
let key;
|
|
112
|
+
try {
|
|
113
|
+
key = await authUtils.getSigningKey(client, keyId);
|
|
114
|
+
} catch {
|
|
115
|
+
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `Unable to find matching key for Key ID: ${keyId}`);
|
|
116
|
+
}
|
|
117
|
+
return key;
|
|
118
|
+
};
|
|
119
|
+
const verifyIdToken = async ({
|
|
120
|
+
token,
|
|
121
|
+
id
|
|
122
|
+
}, {
|
|
123
|
+
appIds,
|
|
124
|
+
cacheMaxEntries,
|
|
125
|
+
cacheMaxAge
|
|
126
|
+
}) => {
|
|
127
|
+
if (!Array.isArray(appIds) || !appIds.length) {
|
|
128
|
+
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Facebook auth is not configured.');
|
|
129
|
+
}
|
|
130
|
+
if (!token) {
|
|
131
|
+
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'id token is invalid for this user.');
|
|
132
|
+
}
|
|
133
|
+
const {
|
|
134
|
+
kid: keyId
|
|
135
|
+
} = authUtils.getHeaderFromToken(token);
|
|
136
|
+
const ONE_HOUR_IN_MS = 3600000;
|
|
137
|
+
let jwtClaims;
|
|
138
|
+
cacheMaxAge = cacheMaxAge || ONE_HOUR_IN_MS;
|
|
139
|
+
cacheMaxEntries = cacheMaxEntries || 5;
|
|
140
|
+
const facebookKey = await getFacebookKeyByKeyId(keyId, cacheMaxEntries, cacheMaxAge);
|
|
141
|
+
const signingKey = facebookKey.publicKey || facebookKey.rsaPublicKey;
|
|
142
|
+
try {
|
|
143
|
+
jwtClaims = jwt.verify(token, signingKey, {
|
|
144
|
+
algorithms: ['RS256'],
|
|
145
|
+
// the audience can be checked against a string, a regular expression or a list of strings and/or regular expressions.
|
|
146
|
+
audience: appIds
|
|
147
|
+
});
|
|
148
|
+
} catch (exception) {
|
|
149
|
+
const message = exception.message;
|
|
150
|
+
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `${message}`);
|
|
151
|
+
}
|
|
152
|
+
if (jwtClaims.iss !== TOKEN_ISSUER) {
|
|
153
|
+
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `id token not issued by correct OpenID provider - expected: ${TOKEN_ISSUER} | from: ${jwtClaims.iss}`);
|
|
154
|
+
}
|
|
155
|
+
if (jwtClaims.sub !== id) {
|
|
156
|
+
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'auth data is invalid for this user.');
|
|
157
|
+
}
|
|
158
|
+
return jwtClaims;
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
// Returns a promise that fulfills iff this user id is valid.
|
|
162
|
+
function validateAuthData(authData, options) {
|
|
163
|
+
if (authData.token) {
|
|
164
|
+
return verifyIdToken(authData, options);
|
|
165
|
+
} else {
|
|
166
|
+
return validateGraphToken(authData, options);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Returns a promise that fulfills iff this app id is valid.
|
|
171
|
+
function validateAppId(appIds, authData, options) {
|
|
172
|
+
if (authData.token) {
|
|
173
|
+
return Promise.resolve();
|
|
174
|
+
} else {
|
|
175
|
+
return validateGraphAppId(appIds, authData, options);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// A promisey wrapper for FB graph requests.
|
|
180
|
+
function graphRequest(path) {
|
|
181
|
+
return httpsRequest.get('https://graph.facebook.com/' + path);
|
|
182
|
+
}
|
|
183
|
+
module.exports = {
|
|
184
|
+
validateAppId: validateAppId,
|
|
185
|
+
validateAuthData: validateAuthData
|
|
186
|
+
};
|
|
187
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJQYXJzZSIsInJlcXVpcmUiLCJjcnlwdG8iLCJqd2tzQ2xpZW50Iiwiand0IiwiaHR0cHNSZXF1ZXN0IiwiYXV0aFV0aWxzIiwiVE9LRU5fSVNTVUVSIiwiZ2V0QXBwU2VjcmV0UGF0aCIsImF1dGhEYXRhIiwib3B0aW9ucyIsImFwcFNlY3JldCIsImFwcHNlY3JldF9wcm9vZiIsImNyZWF0ZUhtYWMiLCJ1cGRhdGUiLCJhY2Nlc3NfdG9rZW4iLCJkaWdlc3QiLCJ2YWxpZGF0ZUdyYXBoVG9rZW4iLCJBcnJheSIsImlzQXJyYXkiLCJhcHBJZHMiLCJsZW5ndGgiLCJFcnJvciIsIk9CSkVDVF9OT1RfRk9VTkQiLCJncmFwaFJlcXVlc3QiLCJ0aGVuIiwiZGF0YSIsImlkIiwicHJvY2VzcyIsImVudiIsIlRFU1RJTkciLCJ2YWxpZGF0ZUdyYXBoQXBwSWQiLCJpbmNsdWRlcyIsImdldEZhY2Vib29rS2V5QnlLZXlJZCIsImtleUlkIiwiY2FjaGVNYXhFbnRyaWVzIiwiY2FjaGVNYXhBZ2UiLCJjbGllbnQiLCJqd2tzVXJpIiwiY2FjaGUiLCJrZXkiLCJnZXRTaWduaW5nS2V5IiwidmVyaWZ5SWRUb2tlbiIsInRva2VuIiwia2lkIiwiZ2V0SGVhZGVyRnJvbVRva2VuIiwiT05FX0hPVVJfSU5fTVMiLCJqd3RDbGFpbXMiLCJmYWNlYm9va0tleSIsInNpZ25pbmdLZXkiLCJwdWJsaWNLZXkiLCJyc2FQdWJsaWNLZXkiLCJ2ZXJpZnkiLCJhbGdvcml0aG1zIiwiYXVkaWVuY2UiLCJleGNlcHRpb24iLCJtZXNzYWdlIiwiaXNzIiwic3ViIiwidmFsaWRhdGVBdXRoRGF0YSIsInZhbGlkYXRlQXBwSWQiLCJQcm9taXNlIiwicmVzb2x2ZSIsInBhdGgiLCJnZXQiLCJtb2R1bGUiLCJleHBvcnRzIl0sInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL0FkYXB0ZXJzL0F1dGgvZmFjZWJvb2suanMiXSwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBQYXJzZSBTZXJ2ZXIgYXV0aGVudGljYXRpb24gYWRhcHRlciBmb3IgRmFjZWJvb2suXG4gKlxuICogQGNsYXNzIEZhY2Vib29rQWRhcHRlclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgYWRhcHRlciBjb25maWd1cmF0aW9uIG9wdGlvbnMuXG4gKiBAcGFyYW0ge3N0cmluZ30gb3B0aW9ucy5hcHBTZWNyZXQgLSBZb3VyIEZhY2Vib29rIEFwcCBTZWNyZXQuIFJlcXVpcmVkIGZvciBzZWN1cmUgYXV0aGVudGljYXRpb24uXG4gKiBAcGFyYW0ge3N0cmluZ1tdfSBvcHRpb25zLmFwcElkcyAtIEFuIGFycmF5IG9mIEZhY2Vib29rIEFwcCBJRHMuIFJlcXVpcmVkIGZvciB2YWxpZGF0aW5nIHRoZSBhcHAuXG4gKlxuICogQGRlc2NyaXB0aW9uXG4gKiAjIyBQYXJzZSBTZXJ2ZXIgQ29uZmlndXJhdGlvblxuICogVG8gY29uZmlndXJlIFBhcnNlIFNlcnZlciBmb3IgRmFjZWJvb2sgYXV0aGVudGljYXRpb24sIHVzZSB0aGUgZm9sbG93aW5nIHN0cnVjdHVyZTpcbiAqIGBgYGpzb25cbiAqIHtcbiAqICAgXCJhdXRoXCI6IHtcbiAqICAgICBcImZhY2Vib29rXCI6IHtcbiAqICAgICAgIFwiYXBwU2VjcmV0XCI6IFwieW91ci1hcHAtc2VjcmV0XCIsXG4gKiAgICAgICBcImFwcElkc1wiOiBbXCJ5b3VyLWFwcC1pZFwiXVxuICogICAgIH1cbiAqICAgfVxuICogfVxuICogYGBgXG4gKlxuICogVGhlIGFkYXB0ZXIgc3VwcG9ydHMgdGhlIGZvbGxvd2luZyBhdXRoZW50aWNhdGlvbiBtZXRob2RzOlxuICogLSAqKlN0YW5kYXJkIExvZ2luKio6IFJlcXVpcmVzIGBpZGAgYW5kIGBhY2Nlc3NfdG9rZW5gLlxuICogLSAqKkxpbWl0ZWQgTG9naW4qKjogUmVxdWlyZXMgYGlkYCBhbmQgYHRva2VuYC5cbiAqXG4gKiAjIyBBdXRoIFBheWxvYWRzXG4gKiAjIyMgU3RhbmRhcmQgTG9naW4gUGF5bG9hZFxuICogYGBganNvblxuICoge1xuICogICBcImZhY2Vib29rXCI6IHtcbiAqICAgICBcImlkXCI6IFwiMTIzNDU2N1wiLFxuICogICAgIFwiYWNjZXNzX3Rva2VuXCI6IFwiYWJjMTIzZGVmNDU2Z2hpNzg5XCJcbiAqICAgfVxuICogfVxuICogYGBgXG4gKlxuICogIyMjIExpbWl0ZWQgTG9naW4gUGF5bG9hZFxuICogYGBganNvblxuICoge1xuICogICBcImZhY2Vib29rXCI6IHtcbiAqICAgICBcImlkXCI6IFwiMTIzNDU2N1wiLFxuICogICAgIFwidG9rZW5cIjogXCJ4eHh4eC55eXl5eS56enp6elwiXG4gKiAgIH1cbiAqIH1cbiAqIGBgYFxuICpcbiAqICMjIE5vdGVzXG4gKiAtICoqU3RhbmRhcmQgTG9naW4qKjogVXNlIGBpZGAgYW5kIGBhY2Nlc3NfdG9rZW5gIGZvciBmdWxsIGZ1bmN0aW9uYWxpdHkuXG4gKiAtICoqTGltaXRlZCBMb2dpbioqOiBVc2UgYGlkYCBhbmQgYHRva2VuYCAoSldUKSB3aGVuIHRyYWNraW5nIGlzIG9wdGVkIG91dCAoZS5nLiwgdmlhIEFwcGxlJ3MgQXBwIFRyYWNraW5nIFRyYW5zcGFyZW5jeSkuXG4gKiAtIFN1cHBvcnRlZCBQYXJzZSBTZXJ2ZXIgdmVyc2lvbnM6XG4gKiAgIC0gYD49IDYuNS42IDwgN2BcbiAqICAgLSBgPj0gNy4wLjFgXG4gKlxuICogQHNlZSB7QGxpbmsgaHR0cHM6Ly9kZXZlbG9wZXJzLmZhY2Vib29rLmNvbS9kb2NzL2ZhY2Vib29rLWxvZ2luL2xpbWl0ZWQtbG9naW4vIEZhY2Vib29rIExpbWl0ZWQgTG9naW59XG4gKiBAc2VlIHtAbGluayBodHRwczovL2RldmVsb3BlcnMuZmFjZWJvb2suY29tL2RvY3MvZmFjZWJvb2stbG9naW4vZmFjZWJvb2stbG9naW4tZm9yLWJ1c2luZXNzLyBGYWNlYm9vayBMb2dpbiBmb3IgQnVzaW5lc3N9XG4gKi9cblxuLy8gSGVscGVyIGZ1bmN0aW9ucyBmb3IgYWNjZXNzaW5nIHRoZSBGYWNlYm9vayBHcmFwaCBBUEkuXG5jb25zdCBQYXJzZSA9IHJlcXVpcmUoJ3BhcnNlL25vZGUnKS5QYXJzZTtcbmNvbnN0IGNyeXB0byA9IHJlcXVpcmUoJ2NyeXB0bycpO1xuY29uc3Qgandrc0NsaWVudCA9IHJlcXVpcmUoJ2p3a3MtcnNhJyk7XG5jb25zdCBqd3QgPSByZXF1aXJlKCdqc29ud2VidG9rZW4nKTtcbmNvbnN0IGh0dHBzUmVxdWVzdCA9IHJlcXVpcmUoJy4vaHR0cHNSZXF1ZXN0Jyk7XG5jb25zdCBhdXRoVXRpbHMgPSByZXF1aXJlKCcuL3V0aWxzJyk7XG5cbmNvbnN0IFRPS0VOX0lTU1VFUiA9ICdodHRwczovL3d3dy5mYWNlYm9vay5jb20nO1xuXG5mdW5jdGlvbiBnZXRBcHBTZWNyZXRQYXRoKGF1dGhEYXRhLCBvcHRpb25zID0ge30pIHtcbiAgY29uc3QgYXBwU2VjcmV0ID0gb3B0aW9ucy5hcHBTZWNyZXQ7XG4gIGlmICghYXBwU2VjcmV0KSB7XG4gICAgcmV0dXJuICcnO1xuICB9XG4gIGNvbnN0IGFwcHNlY3JldF9wcm9vZiA9IGNyeXB0b1xuICAgIC5jcmVhdGVIbWFjKCdzaGEyNTYnLCBhcHBTZWNyZXQpXG4gICAgLnVwZGF0ZShhdXRoRGF0YS5hY2Nlc3NfdG9rZW4pXG4gICAgLmRpZ2VzdCgnaGV4Jyk7XG5cbiAgcmV0dXJuIGAmYXBwc2VjcmV0X3Byb29mPSR7YXBwc2VjcmV0X3Byb29mfWA7XG59XG5cbmZ1bmN0aW9uIHZhbGlkYXRlR3JhcGhUb2tlbihhdXRoRGF0YSwgb3B0aW9ucyA9IHt9KSB7XG4gIGlmICghQXJyYXkuaXNBcnJheShvcHRpb25zLmFwcElkcykgfHwgIW9wdGlvbnMuYXBwSWRzLmxlbmd0aCkge1xuICAgIHRocm93IG5ldyBQYXJzZS5FcnJvcihQYXJzZS5FcnJvci5PQkpFQ1RfTk9UX0ZPVU5ELCAnRmFjZWJvb2sgYXV0aCBpcyBub3QgY29uZmlndXJlZC4nKTtcbiAgfVxuICByZXR1cm4gZ3JhcGhSZXF1ZXN0KFxuICAgICdtZT9maWVsZHM9aWQmYWNjZXNzX3Rva2VuPScgKyBhdXRoRGF0YS5hY2Nlc3NfdG9rZW4gKyBnZXRBcHBTZWNyZXRQYXRoKGF1dGhEYXRhLCBvcHRpb25zKVxuICApLnRoZW4oZGF0YSA9PiB7XG4gICAgaWYgKChkYXRhICYmIGRhdGEuaWQgPT0gYXV0aERhdGEuaWQpIHx8IChwcm9jZXNzLmVudi5URVNUSU5HICYmIGF1dGhEYXRhLmlkID09PSAndGVzdCcpKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRocm93IG5ldyBQYXJzZS5FcnJvcihQYXJzZS5FcnJvci5PQkpFQ1RfTk9UX0ZPVU5ELCAnRmFjZWJvb2sgYXV0aCBpcyBpbnZhbGlkIGZvciB0aGlzIHVzZXIuJyk7XG4gIH0pO1xufVxuXG5hc3luYyBmdW5jdGlvbiB2YWxpZGF0ZUdyYXBoQXBwSWQoYXBwSWRzLCBhdXRoRGF0YSwgb3B0aW9ucykge1xuICB2YXIgYWNjZXNzX3Rva2VuID0gYXV0aERhdGEuYWNjZXNzX3Rva2VuO1xuICBpZiAocHJvY2Vzcy5lbnYuVEVTVElORyAmJiBhY2Nlc3NfdG9rZW4gPT09ICd0ZXN0Jykge1xuICAgIHJldHVybjtcbiAgfVxuICBpZiAoIUFycmF5LmlzQXJyYXkoYXBwSWRzKSkge1xuICAgIHRocm93IG5ldyBQYXJzZS5FcnJvcihQYXJzZS5FcnJvci5PQkpFQ1RfTk9UX0ZPVU5ELCAnYXBwSWRzIG11c3QgYmUgYW4gYXJyYXkuJyk7XG4gIH1cbiAgaWYgKCFhcHBJZHMubGVuZ3RoKSB7XG4gICAgdGhyb3cgbmV3IFBhcnNlLkVycm9yKFBhcnNlLkVycm9yLk9CSkVDVF9OT1RfRk9VTkQsICdGYWNlYm9vayBhdXRoIGlzIG5vdCBjb25maWd1cmVkLicpO1xuICB9XG4gIGNvbnN0IGRhdGEgPSBhd2FpdCBncmFwaFJlcXVlc3QoXG4gICAgYGFwcD9hY2Nlc3NfdG9rZW49JHthY2Nlc3NfdG9rZW59JHtnZXRBcHBTZWNyZXRQYXRoKGF1dGhEYXRhLCBvcHRpb25zKX1gXG4gICk7XG4gIGlmICghZGF0YSB8fCAhYXBwSWRzLmluY2x1ZGVzKGRhdGEuaWQpKSB7XG4gICAgdGhyb3cgbmV3IFBhcnNlLkVycm9yKFBhcnNlLkVycm9yLk9CSkVDVF9OT1RfRk9VTkQsICdGYWNlYm9vayBhdXRoIGlzIGludmFsaWQgZm9yIHRoaXMgdXNlci4nKTtcbiAgfVxufVxuXG5jb25zdCBnZXRGYWNlYm9va0tleUJ5S2V5SWQgPSBhc3luYyAoa2V5SWQsIGNhY2hlTWF4RW50cmllcywgY2FjaGVNYXhBZ2UpID0+IHtcbiAgY29uc3QgY2xpZW50ID0gandrc0NsaWVudCh7XG4gICAgandrc1VyaTogYCR7VE9LRU5fSVNTVUVSfS8ud2VsbC1rbm93bi9vYXV0aC9vcGVuaWQvandrcy9gLFxuICAgIGNhY2hlOiB0cnVlLFxuICAgIGNhY2hlTWF4RW50cmllcyxcbiAgICBjYWNoZU1heEFnZSxcbiAgfSk7XG5cbiAgbGV0IGtleTtcbiAgdHJ5IHtcbiAgICBrZXkgPSBhd2FpdCBhdXRoVXRpbHMuZ2V0U2lnbmluZ0tleShjbGllbnQsIGtleUlkKTtcbiAgfSBjYXRjaCB7XG4gICAgdGhyb3cgbmV3IFBhcnNlLkVycm9yKFxuICAgICAgUGFyc2UuRXJyb3IuT0JKRUNUX05PVF9GT1VORCxcbiAgICAgIGBVbmFibGUgdG8gZmluZCBtYXRjaGluZyBrZXkgZm9yIEtleSBJRDogJHtrZXlJZH1gXG4gICAgKTtcbiAgfVxuICByZXR1cm4ga2V5O1xufTtcblxuY29uc3QgdmVyaWZ5SWRUb2tlbiA9IGFzeW5jICh7IHRva2VuLCBpZCB9LCB7IGFwcElkcywgY2FjaGVNYXhFbnRyaWVzLCBjYWNoZU1heEFnZSB9KSA9PiB7XG4gIGlmICghQXJyYXkuaXNBcnJheShhcHBJZHMpIHx8ICFhcHBJZHMubGVuZ3RoKSB7XG4gICAgdGhyb3cgbmV3IFBhcnNlLkVycm9yKFxuICAgICAgUGFyc2UuRXJyb3IuT0JKRUNUX05PVF9GT1VORCxcbiAgICAgICdGYWNlYm9vayBhdXRoIGlzIG5vdCBjb25maWd1cmVkLidcbiAgICApO1xuICB9XG5cbiAgaWYgKCF0b2tlbikge1xuICAgIHRocm93IG5ldyBQYXJzZS5FcnJvcihQYXJzZS5FcnJvci5PQkpFQ1RfTk9UX0ZPVU5ELCAnaWQgdG9rZW4gaXMgaW52YWxpZCBmb3IgdGhpcyB1c2VyLicpO1xuICB9XG5cbiAgY29uc3QgeyBraWQ6IGtleUlkIH0gPSBhdXRoVXRpbHMuZ2V0SGVhZGVyRnJvbVRva2VuKHRva2VuKTtcbiAgY29uc3QgT05FX0hPVVJfSU5fTVMgPSAzNjAwMDAwO1xuICBsZXQgand0Q2xhaW1zO1xuXG4gIGNhY2hlTWF4QWdlID0gY2FjaGVNYXhBZ2UgfHwgT05FX0hPVVJfSU5fTVM7XG4gIGNhY2hlTWF4RW50cmllcyA9IGNhY2hlTWF4RW50cmllcyB8fCA1O1xuXG4gIGNvbnN0IGZhY2Vib29rS2V5ID0gYXdhaXQgZ2V0RmFjZWJvb2tLZXlCeUtleUlkKGtleUlkLCBjYWNoZU1heEVudHJpZXMsIGNhY2hlTWF4QWdlKTtcbiAgY29uc3Qgc2lnbmluZ0tleSA9IGZhY2Vib29rS2V5LnB1YmxpY0tleSB8fCBmYWNlYm9va0tleS5yc2FQdWJsaWNLZXk7XG5cbiAgdHJ5IHtcbiAgICBqd3RDbGFpbXMgPSBqd3QudmVyaWZ5KHRva2VuLCBzaWduaW5nS2V5LCB7XG4gICAgICBhbGdvcml0aG1zOiBbJ1JTMjU2J10sXG4gICAgICAvLyB0aGUgYXVkaWVuY2UgY2FuIGJlIGNoZWNrZWQgYWdhaW5zdCBhIHN0cmluZywgYSByZWd1bGFyIGV4cHJlc3Npb24gb3IgYSBsaXN0IG9mIHN0cmluZ3MgYW5kL29yIHJlZ3VsYXIgZXhwcmVzc2lvbnMuXG4gICAgICBhdWRpZW5jZTogYXBwSWRzLFxuICAgIH0pO1xuICB9IGNhdGNoIChleGNlcHRpb24pIHtcbiAgICBjb25zdCBtZXNzYWdlID0gZXhjZXB0aW9uLm1lc3NhZ2U7XG5cbiAgICB0aHJvdyBuZXcgUGFyc2UuRXJyb3IoUGFyc2UuRXJyb3IuT0JKRUNUX05PVF9GT1VORCwgYCR7bWVzc2FnZX1gKTtcbiAgfVxuXG4gIGlmIChqd3RDbGFpbXMuaXNzICE9PSBUT0tFTl9JU1NVRVIpIHtcbiAgICB0aHJvdyBuZXcgUGFyc2UuRXJyb3IoXG4gICAgICBQYXJzZS5FcnJvci5PQkpFQ1RfTk9UX0ZPVU5ELFxuICAgICAgYGlkIHRva2VuIG5vdCBpc3N1ZWQgYnkgY29ycmVjdCBPcGVuSUQgcHJvdmlkZXIgLSBleHBlY3RlZDogJHtUT0tFTl9JU1NVRVJ9IHwgZnJvbTogJHtqd3RDbGFpbXMuaXNzfWBcbiAgICApO1xuICB9XG5cbiAgaWYgKGp3dENsYWltcy5zdWIgIT09IGlkKSB7XG4gICAgdGhyb3cgbmV3IFBhcnNlLkVycm9yKFBhcnNlLkVycm9yLk9CSkVDVF9OT1RfRk9VTkQsICdhdXRoIGRhdGEgaXMgaW52YWxpZCBmb3IgdGhpcyB1c2VyLicpO1xuICB9XG4gIHJldHVybiBqd3RDbGFpbXM7XG59O1xuXG4vLyBSZXR1cm5zIGEgcHJvbWlzZSB0aGF0IGZ1bGZpbGxzIGlmZiB0aGlzIHVzZXIgaWQgaXMgdmFsaWQuXG5mdW5jdGlvbiB2YWxpZGF0ZUF1dGhEYXRhKGF1dGhEYXRhLCBvcHRpb25zKSB7XG4gIGlmIChhdXRoRGF0YS50b2tlbikge1xuICAgIHJldHVybiB2ZXJpZnlJZFRva2VuKGF1dGhEYXRhLCBvcHRpb25zKTtcbiAgfSBlbHNlIHtcbiAgICByZXR1cm4gdmFsaWRhdGVHcmFwaFRva2VuKGF1dGhEYXRhLCBvcHRpb25zKTtcbiAgfVxufVxuXG4vLyBSZXR1cm5zIGEgcHJvbWlzZSB0aGF0IGZ1bGZpbGxzIGlmZiB0aGlzIGFwcCBpZCBpcyB2YWxpZC5cbmZ1bmN0aW9uIHZhbGlkYXRlQXBwSWQoYXBwSWRzLCBhdXRoRGF0YSwgb3B0aW9ucykge1xuICBpZiAoYXV0aERhdGEudG9rZW4pIHtcbiAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKCk7XG4gIH0gZWxzZSB7XG4gICAgcmV0dXJuIHZhbGlkYXRlR3JhcGhBcHBJZChhcHBJZHMsIGF1dGhEYXRhLCBvcHRpb25zKTtcbiAgfVxufVxuXG4vLyBBIHByb21pc2V5IHdyYXBwZXIgZm9yIEZCIGdyYXBoIHJlcXVlc3RzLlxuZnVuY3Rpb24gZ3JhcGhSZXF1ZXN0KHBhdGgpIHtcbiAgcmV0dXJuIGh0dHBzUmVxdWVzdC5nZXQoJ2h0dHBzOi8vZ3JhcGguZmFjZWJvb2suY29tLycgKyBwYXRoKTtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSB7XG4gIHZhbGlkYXRlQXBwSWQ6IHZhbGlkYXRlQXBwSWQsXG4gIHZhbGlkYXRlQXV0aERhdGE6IHZhbGlkYXRlQXV0aERhdGEsXG59O1xuIl0sIm1hcHBpbmdzIjoiOztBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLE1BQU1BLEtBQUssR0FBR0MsT0FBTyxDQUFDLFlBQVksQ0FBQyxDQUFDRCxLQUFLO0FBQ3pDLE1BQU1FLE1BQU0sR0FBR0QsT0FBTyxDQUFDLFFBQVEsQ0FBQztBQUNoQyxNQUFNRSxVQUFVLEdBQUdGLE9BQU8sQ0FBQyxVQUFVLENBQUM7QUFDdEMsTUFBTUcsR0FBRyxHQUFHSCxPQUFPLENBQUMsY0FBYyxDQUFDO0FBQ25DLE1BQU1JLFlBQVksR0FBR0osT0FBTyxDQUFDLGdCQUFnQixDQUFDO0FBQzlDLE1BQU1LLFNBQVMsR0FBR0wsT0FBTyxDQUFDLFNBQVMsQ0FBQztBQUVwQyxNQUFNTSxZQUFZLEdBQUcsMEJBQTBCO0FBRS9DLFNBQVNDLGdCQUFnQkEsQ0FBQ0MsUUFBUSxFQUFFQyxPQUFPLEdBQUcsQ0FBQyxDQUFDLEVBQUU7RUFDaEQsTUFBTUMsU0FBUyxHQUFHRCxP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
|