mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
Merge branch 'sso-long-random' into devel
This commit is contained in:
@@ -15,6 +15,9 @@
|
||||
* The oplog observe driver handles errors communicating with Mongo better and
|
||||
knows to re-poll all queries during Mongo failovers.
|
||||
|
||||
* Add `Random.secret()` for generating security-critical secrets like
|
||||
login tokens.
|
||||
|
||||
* Upgraded dependencies:
|
||||
- Node.js from 0.10.25 to 0.10.26.
|
||||
- MongoDB driver from 1.3.19 to 1.4.1
|
||||
|
||||
@@ -10,9 +10,18 @@ servers that don't have enough entropy to seed the cryptographically strong
|
||||
generator).
|
||||
|
||||
<dl class="callbacks">
|
||||
{{#dtdd "Random.id()"}}
|
||||
Returns a unique identifier, such as `"Jjwjg6gouWLXhMGKW"`, that is likely to
|
||||
be unique in the whole world.
|
||||
{{#dtdd "Random.id([n])"}}
|
||||
Returns a unique identifier, such as `"Jjwjg6gouWLXhMGKW"`, that is
|
||||
likely to be unique in the whole world. The optional argument `n`
|
||||
specifies the length of the identifier in characters and defaults to 17.
|
||||
{{/dtdd}}
|
||||
|
||||
{{#dtdd "Random.secret([n])"}}
|
||||
Returns a random string of printable characters with 6 bits of
|
||||
entropy per character. The optional argument `n` specifies the length of
|
||||
the secret string and defaults to 43 characters, or 256 bits of
|
||||
entropy. Use `Random.secret` for security-critical secrets that are
|
||||
intended for machine, rather than human, consumption.
|
||||
{{/dtdd}}
|
||||
|
||||
{{#dtdd "Random.fraction()"}}
|
||||
|
||||
@@ -760,7 +760,7 @@ Accounts.registerLoginHandler("resume", function(options) {
|
||||
// (Also used by Meteor Accounts server and tests).
|
||||
//
|
||||
Accounts._generateStampedLoginToken = function () {
|
||||
return {token: Random.id(), when: (new Date)};
|
||||
return {token: Random.secret(), when: (new Date)};
|
||||
};
|
||||
|
||||
///
|
||||
|
||||
@@ -263,7 +263,7 @@ Accounts.sendResetPasswordEmail = function (userId, email) {
|
||||
if (!email || !_.contains(_.pluck(user.emails || [], 'address'), email))
|
||||
throw new Error("No such email for user.");
|
||||
|
||||
var token = Random.id();
|
||||
var token = Random.secret();
|
||||
var when = new Date();
|
||||
Meteor.users.update(userId, {$set: {
|
||||
"services.password.reset": {
|
||||
@@ -312,7 +312,7 @@ Accounts.sendEnrollmentEmail = function (userId, email) {
|
||||
throw new Error("No such email for user.");
|
||||
|
||||
|
||||
var token = Random.id();
|
||||
var token = Random.secret();
|
||||
var when = new Date();
|
||||
Meteor.users.update(userId, {$set: {
|
||||
"services.password.reset": {
|
||||
@@ -435,7 +435,7 @@ Accounts.sendVerificationEmail = function (userId, address) {
|
||||
|
||||
|
||||
var tokenRecord = {
|
||||
token: Random.id(),
|
||||
token: Random.secret(),
|
||||
address: address,
|
||||
when: new Date()};
|
||||
Meteor.users.update(
|
||||
|
||||
@@ -19,7 +19,7 @@ Facebook.requestCredential = function (options, credentialRequestCompleteCallbac
|
||||
return;
|
||||
}
|
||||
|
||||
var credentialToken = Random.id();
|
||||
var credentialToken = Random.secret();
|
||||
var mobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|Windows Phone/i.test(navigator.userAgent);
|
||||
var display = mobile ? 'touch' : 'popup';
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ Github.requestCredential = function (options, credentialRequestCompleteCallback)
|
||||
credentialRequestCompleteCallback && credentialRequestCompleteCallback(new ServiceConfiguration.ConfigError("Service not configured"));
|
||||
return;
|
||||
}
|
||||
var credentialToken = Random.id();
|
||||
var credentialToken = Random.secret();
|
||||
|
||||
var scope = (options && options.requestPermissions) || [];
|
||||
var flatScope = _.map(scope, encodeURIComponent).join('+');
|
||||
|
||||
@@ -20,7 +20,7 @@ Google.requestCredential = function (options, credentialRequestCompleteCallback)
|
||||
return;
|
||||
}
|
||||
|
||||
var credentialToken = Random.id();
|
||||
var credentialToken = Random.secret();
|
||||
|
||||
// always need this to get user id from google.
|
||||
var requiredScope = ['profile'];
|
||||
|
||||
@@ -16,7 +16,7 @@ Meetup.requestCredential = function (options, credentialRequestCompleteCallback)
|
||||
credentialRequestCompleteCallback && credentialRequestCompleteCallback(new ServiceConfiguration.ConfigError("Service not configured"));
|
||||
return;
|
||||
}
|
||||
var credentialToken = Random.id();
|
||||
var credentialToken = Random.secret();
|
||||
|
||||
var scope = (options && options.requestPermissions) || [];
|
||||
var flatScope = _.map(scope, encodeURIComponent).join('+');
|
||||
|
||||
@@ -16,7 +16,7 @@ var requestCredential = function (credentialRequestCompleteCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
var credentialToken = Random.id();
|
||||
var credentialToken = Random.secret();
|
||||
|
||||
var loginUrl =
|
||||
METEOR_DEVELOPER_URL + "/oauth2/authorize?" +
|
||||
|
||||
@@ -85,7 +85,7 @@ OAuth1Binding.prototype._buildHeader = function(headers) {
|
||||
var self = this;
|
||||
return _.extend({
|
||||
oauth_consumer_key: self._config.consumerKey,
|
||||
oauth_nonce: Random.id().replace(/\W/g, ''),
|
||||
oauth_nonce: Random.secret().replace(/\W/g, ''),
|
||||
oauth_signature_method: 'HMAC-SHA1',
|
||||
oauth_timestamp: (new Date().valueOf()/1000).toFixed().toString(),
|
||||
oauth_version: '1.0'
|
||||
|
||||
@@ -86,6 +86,8 @@ var Alea = function () {
|
||||
};
|
||||
|
||||
var UNMISTAKABLE_CHARS = "23456789ABCDEFGHJKLMNPQRSTWXYZabcdefghijkmnopqrstuvwxyz";
|
||||
var BASE64_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" +
|
||||
"0123456789-_";
|
||||
|
||||
// If seeds are provided, then the alea PRNG will be used, since cryptographic
|
||||
// PRNGs (Node crypto and window.crypto.getRandomValues) don't allow us to
|
||||
@@ -139,17 +141,36 @@ RandomGenerator.prototype.hexString = function (digits) {
|
||||
return hexDigits.join('');
|
||||
}
|
||||
};
|
||||
RandomGenerator.prototype.id = function () {
|
||||
var digits = [];
|
||||
|
||||
RandomGenerator.prototype._randomString = function (charsCount,
|
||||
alphabet) {
|
||||
var self = this;
|
||||
// Length of 17 preserves around 96 bits of entropy, which is the
|
||||
// amount of state in the Alea PRNG.
|
||||
for (var i = 0; i < 17; i++) {
|
||||
digits[i] = self.choice(UNMISTAKABLE_CHARS);
|
||||
var digits = [];
|
||||
for (var i = 0; i < charsCount; i++) {
|
||||
digits[i] = self.choice(alphabet);
|
||||
}
|
||||
return digits.join("");
|
||||
};
|
||||
|
||||
RandomGenerator.prototype.id = function (charsCount) {
|
||||
var self = this;
|
||||
// 17 characters is around 96 bits of entropy, which is the amount of
|
||||
// state in the Alea PRNG.
|
||||
if (charsCount === undefined)
|
||||
charsCount = 17;
|
||||
|
||||
return self._randomString(charsCount, UNMISTAKABLE_CHARS);
|
||||
};
|
||||
|
||||
RandomGenerator.prototype.secret = function (charsCount) {
|
||||
var self = this;
|
||||
// Default to 256 bits of entropy, or 43 characters at 6 bits per
|
||||
// character.
|
||||
if (charsCount === undefined)
|
||||
charsCount = 43;
|
||||
return self._randomString(charsCount, BASE64_CHARS);
|
||||
};
|
||||
|
||||
RandomGenerator.prototype.choice = function (arrayOrString) {
|
||||
var index = Math.floor(this.fraction() * arrayOrString.length);
|
||||
if (typeof arrayOrString === "string")
|
||||
|
||||
@@ -19,6 +19,7 @@ Tinytest.add('random', function (test) {
|
||||
Tinytest.add('random - format', function (test) {
|
||||
var idLen = 17;
|
||||
test.equal(Random.id().length, idLen);
|
||||
test.equal(Random.id(29).length, 29);
|
||||
var numDigits = 9;
|
||||
var hexStr = Random.hexString(numDigits);
|
||||
test.equal(hexStr.length, numDigits);
|
||||
@@ -26,6 +27,9 @@ Tinytest.add('random - format', function (test) {
|
||||
var frac = Random.fraction();
|
||||
test.isTrue(frac < 1.0);
|
||||
test.isTrue(frac >= 0.0);
|
||||
|
||||
test.equal(Random.secret().length, 43);
|
||||
test.equal(Random.secret(13).length, 13);
|
||||
});
|
||||
|
||||
Tinytest.add('random - Alea is last resort', function (test) {
|
||||
|
||||
@@ -15,8 +15,8 @@ SRP = {};
|
||||
SRP.generateVerifier = function (password, options) {
|
||||
var params = paramsFromOptions(options);
|
||||
|
||||
var identity = (options && options.identity) || Random.id();
|
||||
var salt = (options && options.salt) || Random.id();
|
||||
var identity = (options && options.identity) || Random.secret();
|
||||
var salt = (options && options.salt) || Random.secret();
|
||||
|
||||
var x = params.hash(salt + params.hash(identity + ":" + password));
|
||||
var xi = new BigInteger(x, 16);
|
||||
|
||||
@@ -18,7 +18,7 @@ Twitter.requestCredential = function (options, credentialRequestCompleteCallback
|
||||
return;
|
||||
}
|
||||
|
||||
var credentialToken = Random.id();
|
||||
var credentialToken = Random.secret();
|
||||
// We need to keep credentialToken across the next two 'steps' so we're adding
|
||||
// a credentialToken parameter to the url and the callback url that we'll be returned
|
||||
// to by oauth provider
|
||||
|
||||
@@ -18,7 +18,7 @@ Weibo.requestCredential = function (options, credentialRequestCompleteCallback)
|
||||
return;
|
||||
}
|
||||
|
||||
var credentialToken = Random.id();
|
||||
var credentialToken = Random.secret();
|
||||
// XXX need to support configuring access_type and scope
|
||||
var loginUrl =
|
||||
'https://api.weibo.com/oauth2/authorize' +
|
||||
|
||||
Reference in New Issue
Block a user