(function () {

/* Imports */
var Meteor = Package.meteor.Meteor;
var check = Package.check.check;
var Match = Package.check.Match;
var _ = Package.underscore._;
var RoutePolicy = Package.routepolicy.RoutePolicy;
var WebApp = Package.webapp.WebApp;
var main = Package.webapp.main;
var WebAppInternals = Package.webapp.WebAppInternals;
var MongoInternals = Package.mongo.MongoInternals;
var Mongo = Package.mongo.Mongo;
var ServiceConfiguration = Package['service-configuration'].ServiceConfiguration;
var Log = Package.logging.Log;
var URL = Package.url.URL;

/* Package-scope variables */
var OAuth, OAuthTest, Oauth;

(function(){

////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                //
// packages/oauth/oauth_server.js                                                                 //
//                                                                                                //
////////////////////////////////////////////////////////////////////////////////////////////////////
                                                                                                  //
var Fiber = Npm.require('fibers');                                                                // 1
var url = Npm.require('url');                                                                     // 2
                                                                                                  // 3
OAuth = {};                                                                                       // 4
OAuthTest = {};                                                                                   // 5
                                                                                                  // 6
RoutePolicy.declare('/_oauth/', 'network');                                                       // 7
                                                                                                  // 8
var registeredServices = {};                                                                      // 9
                                                                                                  // 10
// Internal: Maps from service version to handler function. The                                   // 11
// 'oauth1' and 'oauth2' packages manipulate this directly to register                            // 12
// for callbacks.                                                                                 // 13
OAuth._requestHandlers = {};                                                                      // 14
                                                                                                  // 15
                                                                                                  // 16
// Register a handler for an OAuth service. The handler will be called                            // 17
// when we get an incoming http request on /_oauth/{serviceName}. This                            // 18
// handler should use that information to fetch data about the user                               // 19
// logging in.                                                                                    // 20
//                                                                                                // 21
// @param name {String} e.g. "google", "facebook"                                                 // 22
// @param version {Number} OAuth version (1 or 2)                                                 // 23
// @param urls   For OAuth1 only, specify the service's urls                                      // 24
// @param handleOauthRequest {Function(oauthBinding|query)}                                       // 25
//   - (For OAuth1 only) oauthBinding {OAuth1Binding} bound to the appropriate provider           // 26
//   - (For OAuth2 only) query {Object} parameters passed in query string                         // 27
//   - return value is:                                                                           // 28
//     - {serviceData:, (optional options:)} where serviceData should end                         // 29
//       up in the user's services[name] field                                                    // 30
//     - `null` if the user declined to give permissions                                          // 31
//                                                                                                // 32
OAuth.registerService = function (name, version, urls, handleOauthRequest) {                      // 33
  if (registeredServices[name])                                                                   // 34
    throw new Error("Already registered the " + name + " OAuth service");                         // 35
                                                                                                  // 36
  registeredServices[name] = {                                                                    // 37
    serviceName: name,                                                                            // 38
    version: version,                                                                             // 39
    urls: urls,                                                                                   // 40
    handleOauthRequest: handleOauthRequest                                                        // 41
  };                                                                                              // 42
};                                                                                                // 43
                                                                                                  // 44
// For test cleanup.                                                                              // 45
OAuthTest.unregisterService = function (name) {                                                   // 46
  delete registeredServices[name];                                                                // 47
};                                                                                                // 48
                                                                                                  // 49
                                                                                                  // 50
OAuth.retrieveCredential = function(credentialToken, credentialSecret) {                          // 51
  return OAuth._retrievePendingCredential(credentialToken, credentialSecret);                     // 52
};                                                                                                // 53
                                                                                                  // 54
                                                                                                  // 55
// The state parameter is normally generated on the client using                                  // 56
// `btoa`, but for tests we need a version that runs on the server.                               // 57
//                                                                                                // 58
OAuth._generateState = function (loginStyle, credentialToken, redirectUrl) {                      // 59
  return new Buffer(JSON.stringify({                                                              // 60
    loginStyle: loginStyle,                                                                       // 61
    credentialToken: credentialToken,                                                             // 62
    redirectUrl: redirectUrl})).toString('base64');                                               // 63
};                                                                                                // 64
                                                                                                  // 65
OAuth._stateFromQuery = function (query) {                                                        // 66
  var string;                                                                                     // 67
  try {                                                                                           // 68
    string = new Buffer(query.state, 'base64').toString('binary');                                // 69
  } catch (e) {                                                                                   // 70
    Log.warn('Unable to base64 decode state from OAuth query: ' + query.state);                   // 71
    throw e;                                                                                      // 72
  }                                                                                               // 73
                                                                                                  // 74
  try {                                                                                           // 75
    return JSON.parse(string);                                                                    // 76
  } catch (e) {                                                                                   // 77
    Log.warn('Unable to parse state from OAuth query: ' + string);                                // 78
    throw e;                                                                                      // 79
  }                                                                                               // 80
};                                                                                                // 81
                                                                                                  // 82
OAuth._loginStyleFromQuery = function (query) {                                                   // 83
  var style;                                                                                      // 84
  // For backwards-compatibility for older clients, catch any errors                              // 85
  // that result from parsing the state parameter. If we can't parse it,                          // 86
  // set login style to popup by default.                                                         // 87
  try {                                                                                           // 88
    style = OAuth._stateFromQuery(query).loginStyle;                                              // 89
  } catch (err) {                                                                                 // 90
    style = "popup";                                                                              // 91
  }                                                                                               // 92
  if (style !== "popup" && style !== "redirect") {                                                // 93
    throw new Error("Unrecognized login style: " + style);                                        // 94
  }                                                                                               // 95
  return style;                                                                                   // 96
};                                                                                                // 97
                                                                                                  // 98
OAuth._credentialTokenFromQuery = function (query) {                                              // 99
  var state;                                                                                      // 100
  // For backwards-compatibility for older clients, catch any errors                              // 101
  // that result from parsing the state parameter. If we can't parse it,                          // 102
  // assume that the state parameter's value is the credential token, as                          // 103
  // it used to be for older clients.                                                             // 104
  try {                                                                                           // 105
    state = OAuth._stateFromQuery(query);                                                         // 106
  } catch (err) {                                                                                 // 107
    return query.state;                                                                           // 108
  }                                                                                               // 109
  return state.credentialToken;                                                                   // 110
};                                                                                                // 111
                                                                                                  // 112
OAuth._isCordovaFromQuery = function (query) {                                                    // 113
  try {                                                                                           // 114
    return !! OAuth._stateFromQuery(query).isCordova;                                             // 115
  } catch (err) {                                                                                 // 116
    // For backwards-compatibility for older clients, catch any errors                            // 117
    // that result from parsing the state parameter. If we can't parse                            // 118
    // it, assume that we are not on Cordova, since older Meteor didn't                           // 119
    // do Cordova.                                                                                // 120
    return false;                                                                                 // 121
  }                                                                                               // 122
};                                                                                                // 123
                                                                                                  // 124
// Checks if the `redirectUrl` matches the app host.                                              // 125
// We export this function so that developers can override this                                   // 126
// behavior to allow apps from external domains to login using the                                // 127
// redirect OAuth flow.                                                                           // 128
OAuth._checkRedirectUrlOrigin = function (redirectUrl) {                                          // 129
  var appHost = Meteor.absoluteUrl();                                                             // 130
  var appHostReplacedLocalhost = Meteor.absoluteUrl(undefined, {                                  // 131
    replaceLocalhost: true                                                                        // 132
  });                                                                                             // 133
  return (                                                                                        // 134
    redirectUrl.substr(0, appHost.length) !== appHost &&                                          // 135
    redirectUrl.substr(0, appHostReplacedLocalhost.length) !== appHostReplacedLocalhost           // 136
  );                                                                                              // 137
};                                                                                                // 138
                                                                                                  // 139
                                                                                                  // 140
// Listen to incoming OAuth http requests                                                         // 141
WebApp.connectHandlers.use(function(req, res, next) {                                             // 142
  // Need to create a Fiber since we're using synchronous http calls and nothing                  // 143
  // else is wrapping this in a fiber automatically                                               // 144
  Fiber(function () {                                                                             // 145
    middleware(req, res, next);                                                                   // 146
  }).run();                                                                                       // 147
});                                                                                               // 148
                                                                                                  // 149
var middleware = function (req, res, next) {                                                      // 150
  // Make sure to catch any exceptions because otherwise we'd crash                               // 151
  // the runner                                                                                   // 152
  try {                                                                                           // 153
    var serviceName = oauthServiceName(req);                                                      // 154
    if (!serviceName) {                                                                           // 155
      // not an oauth request. pass to next middleware.                                           // 156
      next();                                                                                     // 157
      return;                                                                                     // 158
    }                                                                                             // 159
                                                                                                  // 160
    var service = registeredServices[serviceName];                                                // 161
                                                                                                  // 162
    // Skip everything if there's no service set by the oauth middleware                          // 163
    if (!service)                                                                                 // 164
      throw new Error("Unexpected OAuth service " + serviceName);                                 // 165
                                                                                                  // 166
    // Make sure we're configured                                                                 // 167
    ensureConfigured(serviceName);                                                                // 168
                                                                                                  // 169
    var handler = OAuth._requestHandlers[service.version];                                        // 170
    if (!handler)                                                                                 // 171
      throw new Error("Unexpected OAuth version " + service.version);                             // 172
    handler(service, req.query, res);                                                             // 173
  } catch (err) {                                                                                 // 174
    // if we got thrown an error, save it off, it will get passed to                              // 175
    // the appropriate login call (if any) and reported there.                                    // 176
    //                                                                                            // 177
    // The other option would be to display it in the popup tab that                              // 178
    // is still open at this point, ignoring the 'close' or 'redirect'                            // 179
    // we were passed. But then the developer wouldn't be able to                                 // 180
    // style the error or react to it in any way.                                                 // 181
    if (req.query.state && err instanceof Error) {                                                // 182
      try { // catch any exceptions to avoid crashing runner                                      // 183
        OAuth._storePendingCredential(OAuth._credentialTokenFromQuery(req.query), err);           // 184
      } catch (err) {                                                                             // 185
        // Ignore the error and just give up. If we failed to store the                           // 186
        // error, then the login will just fail with a generic error.                             // 187
        Log.warn("Error in OAuth Server while storing pending login result.\n" +                  // 188
                 err.stack || err.message);                                                       // 189
      }                                                                                           // 190
    }                                                                                             // 191
                                                                                                  // 192
    // close the popup. because nobody likes them just hanging                                    // 193
    // there.  when someone sees this multiple times they might                                   // 194
    // think to check server logs (we hope?)                                                      // 195
    // Catch errors because any exception here will crash the runner.                             // 196
    try {                                                                                         // 197
      OAuth._endOfLoginResponse(res, {                                                            // 198
        query: req.query,                                                                         // 199
        loginStyle: OAuth._loginStyleFromQuery(req.query),                                        // 200
        error: err                                                                                // 201
      });                                                                                         // 202
    } catch (err) {                                                                               // 203
      Log.warn("Error generating end of login response\n" +                                       // 204
               (err && (err.stack || err.message)));                                              // 205
    }                                                                                             // 206
  }                                                                                               // 207
};                                                                                                // 208
                                                                                                  // 209
OAuthTest.middleware = middleware;                                                                // 210
                                                                                                  // 211
// Handle /_oauth/* paths and extract the service name.                                           // 212
//                                                                                                // 213
// @returns {String|null} e.g. "facebook", or null if this isn't an                               // 214
// oauth request                                                                                  // 215
var oauthServiceName = function (req) {                                                           // 216
  // req.url will be "/_oauth/<service name>" with an optional "?close".                          // 217
  var i = req.url.indexOf('?');                                                                   // 218
  var barePath;                                                                                   // 219
  if (i === -1)                                                                                   // 220
    barePath = req.url;                                                                           // 221
  else                                                                                            // 222
    barePath = req.url.substring(0, i);                                                           // 223
  var splitPath = barePath.split('/');                                                            // 224
                                                                                                  // 225
  // Any non-oauth request will continue down the default                                         // 226
  // middlewares.                                                                                 // 227
  if (splitPath[1] !== '_oauth')                                                                  // 228
    return null;                                                                                  // 229
                                                                                                  // 230
  // Find service based on url                                                                    // 231
  var serviceName = splitPath[2];                                                                 // 232
  return serviceName;                                                                             // 233
};                                                                                                // 234
                                                                                                  // 235
// Make sure we're configured                                                                     // 236
var ensureConfigured = function(serviceName) {                                                    // 237
  if (!ServiceConfiguration.configurations.findOne({service: serviceName})) {                     // 238
    throw new ServiceConfiguration.ConfigError();                                                 // 239
  }                                                                                               // 240
};                                                                                                // 241
                                                                                                  // 242
var isSafe = function (value) {                                                                   // 243
  // This matches strings generated by `Random.secret` and                                        // 244
  // `Random.id`.                                                                                 // 245
  return typeof value === "string" &&                                                             // 246
    /^[a-zA-Z0-9\-_]+$/.test(value);                                                              // 247
};                                                                                                // 248
                                                                                                  // 249
// Internal: used by the oauth1 and oauth2 packages                                               // 250
OAuth._renderOauthResults = function(res, query, credentialSecret) {                              // 251
  // For tests, we support the `only_credential_secret_for_test`                                  // 252
  // parameter, which just returns the credential secret without any                              // 253
  // surrounding HTML. (The test needs to be able to easily grab the                              // 254
  // secret and use it to log in.)                                                                // 255
  //                                                                                              // 256
  // XXX only_credential_secret_for_test could be useful for other                                // 257
  // things beside tests, like command-line clients. We should give it a                          // 258
  // real name and serve the credential secret in JSON.                                           // 259
                                                                                                  // 260
  if (query.only_credential_secret_for_test) {                                                    // 261
    res.writeHead(200, {'Content-Type': 'text/html'});                                            // 262
    res.end(credentialSecret, 'utf-8');                                                           // 263
  } else {                                                                                        // 264
    var details = {                                                                               // 265
      query: query,                                                                               // 266
      loginStyle: OAuth._loginStyleFromQuery(query)                                               // 267
    };                                                                                            // 268
    if (query.error) {                                                                            // 269
      details.error = query.error;                                                                // 270
    } else {                                                                                      // 271
      var token = OAuth._credentialTokenFromQuery(query);                                         // 272
      var secret = credentialSecret;                                                              // 273
      if (token && secret &&                                                                      // 274
          isSafe(token) && isSafe(secret)) {                                                      // 275
        details.credentials = { token: token, secret: secret};                                    // 276
      } else {                                                                                    // 277
        details.error = "invalid_credential_token_or_secret";                                     // 278
      }                                                                                           // 279
    }                                                                                             // 280
                                                                                                  // 281
    OAuth._endOfLoginResponse(res, details);                                                      // 282
  }                                                                                               // 283
};                                                                                                // 284
                                                                                                  // 285
// This "template" (not a real Spacebars template, just an HTML file                              // 286
// with some ##PLACEHOLDER##s) communicates the credential secret back                            // 287
// to the main window and then closes the popup.                                                  // 288
OAuth._endOfPopupResponseTemplate = Assets.getText(                                               // 289
  "end_of_popup_response.html");                                                                  // 290
                                                                                                  // 291
OAuth._endOfRedirectResponseTemplate = Assets.getText(                                            // 292
  "end_of_redirect_response.html");                                                               // 293
                                                                                                  // 294
// Renders the end of login response template into some HTML and JavaScript                       // 295
// that closes the popup or redirects at the end of the OAuth flow.                               // 296
//                                                                                                // 297
// options are:                                                                                   // 298
//   - loginStyle ("popup" or "redirect")                                                         // 299
//   - setCredentialToken (boolean)                                                               // 300
//   - credentialToken                                                                            // 301
//   - credentialSecret                                                                           // 302
//   - redirectUrl                                                                                // 303
//   - isCordova (boolean)                                                                        // 304
//                                                                                                // 305
var renderEndOfLoginResponse = function (options) {                                               // 306
  // It would be nice to use Blaze here, but it's a little tricky                                 // 307
  // because our mustaches would be inside a <script> tag, and Blaze                              // 308
  // would treat the <script> tag contents as text (e.g. encode '&' as                            // 309
  // '&amp;'). So we just do a simple replace.                                                    // 310
                                                                                                  // 311
  var escape = function (s) {                                                                     // 312
    if (s) {                                                                                      // 313
      return s.replace(/&/g, "&amp;").                                                            // 314
        replace(/</g, "&lt;").                                                                    // 315
        replace(/>/g, "&gt;").                                                                    // 316
        replace(/\"/g, "&quot;").                                                                 // 317
        replace(/\'/g, "&#x27;").                                                                 // 318
        replace(/\//g, "&#x2F;");                                                                 // 319
    } else {                                                                                      // 320
      return s;                                                                                   // 321
    }                                                                                             // 322
  };                                                                                              // 323
                                                                                                  // 324
  // Escape everything just to be safe (we've already checked that some                           // 325
  // of this data -- the token and secret -- are safe).                                           // 326
  var config = {                                                                                  // 327
    setCredentialToken: !! options.setCredentialToken,                                            // 328
    credentialToken: escape(options.credentialToken),                                             // 329
    credentialSecret: escape(options.credentialSecret),                                           // 330
    storagePrefix: escape(OAuth._storageTokenPrefix),                                             // 331
    redirectUrl: escape(options.redirectUrl),                                                     // 332
    isCordova: !! options.isCordova                                                               // 333
  };                                                                                              // 334
                                                                                                  // 335
  var template;                                                                                   // 336
  if (options.loginStyle === 'popup') {                                                           // 337
    template = OAuth._endOfPopupResponseTemplate;                                                 // 338
  } else if (options.loginStyle === 'redirect') {                                                 // 339
    template = OAuth._endOfRedirectResponseTemplate;                                              // 340
  } else {                                                                                        // 341
    throw new Error('invalid loginStyle: ' + options.loginStyle);                                 // 342
  }                                                                                               // 343
                                                                                                  // 344
  var result = template.replace(/##CONFIG##/, JSON.stringify(config));                            // 345
                                                                                                  // 346
  return "<!DOCTYPE html>\n" + result;                                                            // 347
};                                                                                                // 348
                                                                                                  // 349
// Writes an HTTP response to the popup window at the end of an OAuth                             // 350
// login flow. At this point, if the user has successfully authenticated                          // 351
// to the OAuth server and authorized this app, we communicate the                                // 352
// credentialToken and credentialSecret to the main window. The main                              // 353
// window must provide both these values to the DDP `login` method to                             // 354
// authenticate its DDP connection. After communicating these vaues to                            // 355
// the main window, we close the popup.                                                           // 356
//                                                                                                // 357
// We export this function so that developers can override this                                   // 358
// behavior, which is particularly useful in, for example, some mobile                            // 359
// environments where popups and/or `window.opener` don't work. For                               // 360
// example, an app could override `OAuth._endOfPopupResponse` to put the                          // 361
// credential token and credential secret in the popup URL for the main                           // 362
// window to read them there instead of using `window.opener`. If you                             // 363
// override this function, you take responsibility for writing to the                             // 364
// request and calling `res.end()` to complete the request.                                       // 365
//                                                                                                // 366
// Arguments:                                                                                     // 367
//   - res: the HTTP response object                                                              // 368
//   - details:                                                                                   // 369
//      - query: the query string on the HTTP request                                             // 370
//      - credentials: { token: *, secret: * }. If present, this field                            // 371
//        indicates that the login was successful. Return these values                            // 372
//        to the client, who can use them to log in over DDP. If                                  // 373
//        present, the values have been checked against a limited                                 // 374
//        character set and are safe to include in HTML.                                          // 375
//      - error: if present, a string or Error indicating an error that                           // 376
//        occurred during the login. This can come from the client and                            // 377
//        so shouldn't be trusted for security decisions or included in                           // 378
//        the response without sanitizing it first. Only one of `error`                           // 379
//        or `credentials` should be set.                                                         // 380
OAuth._endOfLoginResponse = function (res, details) {                                             // 381
  res.writeHead(200, {'Content-Type': 'text/html'});                                              // 382
                                                                                                  // 383
  var redirectUrl;                                                                                // 384
  if (details.loginStyle === 'redirect') {                                                        // 385
    redirectUrl = OAuth._stateFromQuery(details.query).redirectUrl;                               // 386
    var appHost = Meteor.absoluteUrl();                                                           // 387
    if (OAuth._checkRedirectUrlOrigin(redirectUrl)) {                                             // 388
      details.error = "redirectUrl (" + redirectUrl +                                             // 389
        ") is not on the same host as the app (" + appHost + ")";                                 // 390
      redirectUrl = appHost;                                                                      // 391
    }                                                                                             // 392
  }                                                                                               // 393
                                                                                                  // 394
  var isCordova = OAuth._isCordovaFromQuery(details.query);                                       // 395
                                                                                                  // 396
  if (details.error) {                                                                            // 397
    Log.warn("Error in OAuth Server: " +                                                          // 398
             (details.error instanceof Error ?                                                    // 399
              details.error.message : details.error));                                            // 400
    res.end(renderEndOfLoginResponse({                                                            // 401
      loginStyle: details.loginStyle,                                                             // 402
      setCredentialToken: false,                                                                  // 403
      redirectUrl: redirectUrl,                                                                   // 404
      isCordova: isCordova                                                                        // 405
    }), "utf-8");                                                                                 // 406
    return;                                                                                       // 407
  }                                                                                               // 408
                                                                                                  // 409
  // If we have a credentialSecret, report it back to the parent                                  // 410
  // window, with the corresponding credentialToken. The parent window                            // 411
  // uses the credentialToken and credentialSecret to log in over DDP.                            // 412
  res.end(renderEndOfLoginResponse({                                                              // 413
    loginStyle: details.loginStyle,                                                               // 414
    setCredentialToken: true,                                                                     // 415
    credentialToken: details.credentials.token,                                                   // 416
    credentialSecret: details.credentials.secret,                                                 // 417
    redirectUrl: redirectUrl,                                                                     // 418
    isCordova: isCordova                                                                          // 419
  }), "utf-8");                                                                                   // 420
};                                                                                                // 421
                                                                                                  // 422
                                                                                                  // 423
var OAuthEncryption = Package["oauth-encryption"] && Package["oauth-encryption"].OAuthEncryption;
                                                                                                  // 425
var usingOAuthEncryption = function () {                                                          // 426
  return OAuthEncryption && OAuthEncryption.keyIsLoaded();                                        // 427
};                                                                                                // 428
                                                                                                  // 429
// Encrypt sensitive service data such as access tokens if the                                    // 430
// "oauth-encryption" package is loaded and the oauth secret key has                              // 431
// been specified.  Returns the unencrypted plaintext otherwise.                                  // 432
//                                                                                                // 433
// The user id is not specified because the user isn't known yet at                               // 434
// this point in the oauth authentication process.  After the oauth                               // 435
// authentication process completes the encrypted service data fields                             // 436
// will be re-encrypted with the user id included before inserting the                            // 437
// service data into the user document.                                                           // 438
//                                                                                                // 439
OAuth.sealSecret = function (plaintext) {                                                         // 440
  if (usingOAuthEncryption())                                                                     // 441
    return OAuthEncryption.seal(plaintext);                                                       // 442
  else                                                                                            // 443
    return plaintext;                                                                             // 444
}                                                                                                 // 445
                                                                                                  // 446
// Unencrypt a service data field, if the "oauth-encryption"                                      // 447
// package is loaded and the field is encrypted.                                                  // 448
//                                                                                                // 449
// Throws an error if the "oauth-encryption" package is loaded and the                            // 450
// field is encrypted, but the oauth secret key hasn't been specified.                            // 451
//                                                                                                // 452
OAuth.openSecret = function (maybeSecret, userId) {                                               // 453
  if (!Package["oauth-encryption"] || !OAuthEncryption.isSealed(maybeSecret))                     // 454
    return maybeSecret;                                                                           // 455
                                                                                                  // 456
  return OAuthEncryption.open(maybeSecret, userId);                                               // 457
};                                                                                                // 458
                                                                                                  // 459
// Unencrypt fields in the service data object.                                                   // 460
//                                                                                                // 461
OAuth.openSecrets = function (serviceData, userId) {                                              // 462
  var result = {};                                                                                // 463
  _.each(_.keys(serviceData), function (key) {                                                    // 464
    result[key] = OAuth.openSecret(serviceData[key], userId);                                     // 465
  });                                                                                             // 466
  return result;                                                                                  // 467
};                                                                                                // 468
                                                                                                  // 469
////////////////////////////////////////////////////////////////////////////////////////////////////

}).call(this);






(function(){

////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                //
// packages/oauth/pending_credentials.js                                                          //
//                                                                                                //
////////////////////////////////////////////////////////////////////////////////////////////////////
                                                                                                  //
//                                                                                                // 1
// When an oauth request is made, Meteor receives oauth credentials                               // 2
// in one browser tab, and temporarily persists them while that                                   // 3
// tab is closed, then retrieves them in the browser tab that                                     // 4
// initiated the credential request.                                                              // 5
//                                                                                                // 6
// _pendingCredentials is the storage mechanism used to share the                                 // 7
// credential between the 2 tabs                                                                  // 8
//                                                                                                // 9
                                                                                                  // 10
                                                                                                  // 11
// Collection containing pending credentials of oauth credential requests                         // 12
// Has key, credential, and createdAt fields.                                                     // 13
OAuth._pendingCredentials = new Mongo.Collection(                                                 // 14
  "meteor_oauth_pendingCredentials", {                                                            // 15
    _preventAutopublish: true                                                                     // 16
  });                                                                                             // 17
                                                                                                  // 18
OAuth._pendingCredentials._ensureIndex('key', {unique: 1});                                       // 19
OAuth._pendingCredentials._ensureIndex('credentialSecret');                                       // 20
OAuth._pendingCredentials._ensureIndex('createdAt');                                              // 21
                                                                                                  // 22
                                                                                                  // 23
                                                                                                  // 24
// Periodically clear old entries that were never retrieved                                       // 25
var _cleanStaleResults = function() {                                                             // 26
  // Remove credentials older than 1 minute                                                       // 27
  var timeCutoff = new Date();                                                                    // 28
  timeCutoff.setMinutes(timeCutoff.getMinutes() - 1);                                             // 29
  OAuth._pendingCredentials.remove({ createdAt: { $lt: timeCutoff } });                           // 30
};                                                                                                // 31
var _cleanupHandle = Meteor.setInterval(_cleanStaleResults, 60 * 1000);                           // 32
                                                                                                  // 33
                                                                                                  // 34
// Stores the key and credential in the _pendingCredentials collection.                           // 35
// Will throw an exception if `key` is not a string.                                              // 36
//                                                                                                // 37
// @param key {string}                                                                            // 38
// @param credential {Object}   The credential to store                                           // 39
// @param credentialSecret {string} A secret that must be presented in                            // 40
//   addition to the `key` to retrieve the credential                                             // 41
//                                                                                                // 42
OAuth._storePendingCredential = function (key, credential, credentialSecret) {                    // 43
  check(key, String);                                                                             // 44
  check(credentialSecret, Match.Optional(String));                                                // 45
                                                                                                  // 46
  if (credential instanceof Error) {                                                              // 47
    credential = storableError(credential);                                                       // 48
  } else {                                                                                        // 49
    credential = OAuth.sealSecret(credential);                                                    // 50
  }                                                                                               // 51
                                                                                                  // 52
  // We do an upsert here instead of an insert in case the user happens                           // 53
  // to somehow send the same `state` parameter twice during an OAuth                             // 54
  // login; we don't want a duplicate key error.                                                  // 55
  OAuth._pendingCredentials.upsert({                                                              // 56
    key: key                                                                                      // 57
  }, {                                                                                            // 58
    key: key,                                                                                     // 59
    credential: credential,                                                                       // 60
    credentialSecret: credentialSecret || null,                                                   // 61
    createdAt: new Date()                                                                         // 62
  });                                                                                             // 63
};                                                                                                // 64
                                                                                                  // 65
                                                                                                  // 66
// Retrieves and removes a credential from the _pendingCredentials collection                     // 67
//                                                                                                // 68
// @param key {string}                                                                            // 69
// @param credentialSecret {string}                                                               // 70
//                                                                                                // 71
OAuth._retrievePendingCredential = function (key, credentialSecret) {                             // 72
  check(key, String);                                                                             // 73
                                                                                                  // 74
  var pendingCredential = OAuth._pendingCredentials.findOne({                                     // 75
    key: key,                                                                                     // 76
    credentialSecret: credentialSecret || null                                                    // 77
  });                                                                                             // 78
  if (pendingCredential) {                                                                        // 79
    OAuth._pendingCredentials.remove({ _id: pendingCredential._id });                             // 80
    if (pendingCredential.credential.error)                                                       // 81
      return recreateError(pendingCredential.credential.error);                                   // 82
    else                                                                                          // 83
      return OAuth.openSecret(pendingCredential.credential);                                      // 84
  } else {                                                                                        // 85
    return undefined;                                                                             // 86
  }                                                                                               // 87
};                                                                                                // 88
                                                                                                  // 89
                                                                                                  // 90
// Convert an Error into an object that can be stored in mongo                                    // 91
// Note: A Meteor.Error is reconstructed as a Meteor.Error                                        // 92
// All other error classes are reconstructed as a plain Error.                                    // 93
var storableError = function(error) {                                                             // 94
  var plainObject = {};                                                                           // 95
  Object.getOwnPropertyNames(error).forEach(function(key) {                                       // 96
    plainObject[key] = error[key];                                                                // 97
  });                                                                                             // 98
                                                                                                  // 99
  // Keep track of whether it's a Meteor.Error                                                    // 100
  if(error instanceof Meteor.Error) {                                                             // 101
    plainObject['meteorError'] = true;                                                            // 102
  }                                                                                               // 103
                                                                                                  // 104
  return { error: plainObject };                                                                  // 105
};                                                                                                // 106
                                                                                                  // 107
// Create an error from the error format stored in mongo                                          // 108
var recreateError = function(errorDoc) {                                                          // 109
  var error;                                                                                      // 110
                                                                                                  // 111
  if (errorDoc.meteorError) {                                                                     // 112
    error = new Meteor.Error();                                                                   // 113
    delete errorDoc.meteorError;                                                                  // 114
  } else {                                                                                        // 115
    error = new Error();                                                                          // 116
  }                                                                                               // 117
                                                                                                  // 118
  Object.getOwnPropertyNames(errorDoc).forEach(function(key) {                                    // 119
    error[key] = errorDoc[key];                                                                   // 120
  });                                                                                             // 121
                                                                                                  // 122
  return error;                                                                                   // 123
};                                                                                                // 124
                                                                                                  // 125
////////////////////////////////////////////////////////////////////////////////////////////////////

}).call(this);






(function(){

////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                //
// packages/oauth/oauth_common.js                                                                 //
//                                                                                                //
////////////////////////////////////////////////////////////////////////////////////////////////////
                                                                                                  //
OAuth._storageTokenPrefix = "Meteor.oauth.credentialSecret-";                                     // 1
                                                                                                  // 2
OAuth._redirectUri = function (serviceName, config, params, absoluteUrlOptions) {                 // 3
  // XXX COMPAT WITH 0.9.0                                                                        // 4
  // The redirect URI used to have a "?close" query argument.  We                                 // 5
  // detect whether we need to be backwards compatible by checking for                            // 6
  // the absence of the `loginStyle` field, which wasn't used in the                              // 7
  // code which had the "?close" argument.                                                        // 8
  // This logic is duplicated in the tool so that the tool can do OAuth                           // 9
  // flow with <= 0.9.0 servers (tools/auth.js).                                                  // 10
  var query = config.loginStyle ? null : "close";                                                 // 11
                                                                                                  // 12
  // Clone because we're going to mutate 'params'. The 'cordova' and                              // 13
  // 'android' parameters are only used for picking the host of the                               // 14
  // redirect URL, and not actually included in the redirect URL itself.                          // 15
  var isCordova = false;                                                                          // 16
  var isAndroid = false;                                                                          // 17
  if (params) {                                                                                   // 18
    params = _.clone(params);                                                                     // 19
    isCordova = params.cordova;                                                                   // 20
    isAndroid = params.android;                                                                   // 21
    delete params.cordova;                                                                        // 22
    delete params.android;                                                                        // 23
    if (_.isEmpty(params)) {                                                                      // 24
      params = undefined;                                                                         // 25
    }                                                                                             // 26
  }                                                                                               // 27
                                                                                                  // 28
  if (Meteor.isServer && isCordova) {                                                             // 29
    var rootUrl = process.env.MOBILE_ROOT_URL ||                                                  // 30
          __meteor_runtime_config__.ROOT_URL;                                                     // 31
                                                                                                  // 32
    if (isAndroid) {                                                                              // 33
      // Match the replace that we do in cordova boilerplate                                      // 34
      // (boilerplate-generator package).                                                         // 35
      // XXX Maybe we should put this in a separate package or something                          // 36
      // that is used here and by boilerplate-generator? Or maybe                                 // 37
      // `Meteor.absoluteUrl` should know how to do this?                                         // 38
      var url = Npm.require("url");                                                               // 39
      var parsedRootUrl = url.parse(rootUrl);                                                     // 40
      if (parsedRootUrl.hostname === "localhost") {                                               // 41
        parsedRootUrl.hostname = "10.0.2.2";                                                      // 42
        delete parsedRootUrl.host;                                                                // 43
      }                                                                                           // 44
      rootUrl = url.format(parsedRootUrl);                                                        // 45
    }                                                                                             // 46
                                                                                                  // 47
    absoluteUrlOptions = _.extend({}, absoluteUrlOptions, {                                       // 48
      // For Cordova clients, redirect to the special Cordova root url                            // 49
      // (likely a local IP in development mode).                                                 // 50
      rootUrl: rootUrl                                                                            // 51
    });                                                                                           // 52
  }                                                                                               // 53
                                                                                                  // 54
  return URL._constructUrl(                                                                       // 55
    Meteor.absoluteUrl('_oauth/' + serviceName, absoluteUrlOptions),                              // 56
    query,                                                                                        // 57
    params);                                                                                      // 58
};                                                                                                // 59
                                                                                                  // 60
////////////////////////////////////////////////////////////////////////////////////////////////////

}).call(this);






(function(){

////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                //
// packages/oauth/deprecated.js                                                                   //
//                                                                                                //
////////////////////////////////////////////////////////////////////////////////////////////////////
                                                                                                  //
// XXX COMPAT WITH 0.8.0                                                                          // 1
                                                                                                  // 2
Oauth = OAuth;                                                                                    // 3
                                                                                                  // 4
////////////////////////////////////////////////////////////////////////////////////////////////////

}).call(this);


/* Exports */
if (typeof Package === 'undefined') Package = {};
Package.oauth = {
  OAuth: OAuth,
  OAuthTest: OAuthTest,
  Oauth: Oauth
};

})();

//# sourceMappingURL=oauth.js.map