mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
Merge pull request #12407 from meteor/release-3.0-tests-oauth1
Release 3.0 making `oauth1` async
This commit is contained in:
@@ -28,12 +28,20 @@
|
||||
|
||||
* `oauth`:
|
||||
- `_endOfPopupResponseTemplate` and `_endOfRedirectResponseTemplate` are no longer a property but now a function that returns a promise of the same value as before
|
||||
- the following methods are now async:
|
||||
- the following server methods are now async:
|
||||
- `OAuth._renderOauthResults`
|
||||
- `OAuth._endOfLoginResponse`
|
||||
- `OAuth.renderEndOfLoginResponse`
|
||||
- `OAuth._storePendingCredential`
|
||||
- `OAuth._retrievePendingCredential`
|
||||
- `ensureConfigured`
|
||||
- `_cleanStaleResults`
|
||||
|
||||
* `oauth1`:
|
||||
- the following server methods are now async:
|
||||
- `OAuth._storeRequestToken`
|
||||
- `OAuth._retrieveRequestToken`
|
||||
|
||||
#### Internal API changes
|
||||
|
||||
|
||||
|
||||
@@ -156,7 +156,7 @@ const middleware = async (req, res, next) => {
|
||||
throw new Error(`Unexpected OAuth service ${serviceName}`);
|
||||
|
||||
// Make sure we're configured
|
||||
ensureConfigured(serviceName);
|
||||
await ensureConfigured(serviceName);
|
||||
|
||||
const handler = OAuth._requestHandlers[service.version];
|
||||
if (!handler)
|
||||
@@ -167,7 +167,6 @@ const middleware = async (req, res, next) => {
|
||||
} else {
|
||||
requestData = req.body;
|
||||
}
|
||||
|
||||
await handler(service, requestData, res);
|
||||
} catch (err) {
|
||||
// if we got thrown an error, save it off, it will get passed to
|
||||
@@ -179,7 +178,7 @@ const middleware = async (req, res, next) => {
|
||||
// style the error or react to it in any way.
|
||||
if (requestData?.state && err instanceof Error) {
|
||||
try { // catch any exceptions to avoid crashing runner
|
||||
OAuth._storePendingCredential(OAuth._credentialTokenFromQuery(requestData), err);
|
||||
await OAuth._storePendingCredential(OAuth._credentialTokenFromQuery(requestData), err);
|
||||
} catch (err) {
|
||||
// Ignore the error and just give up. If we failed to store the
|
||||
// error, then the login will just fail with a generic error.
|
||||
@@ -193,7 +192,7 @@ const middleware = async (req, res, next) => {
|
||||
// think to check server logs (we hope?)
|
||||
// Catch errors because any exception here will crash the runner.
|
||||
try {
|
||||
OAuth._endOfLoginResponse(res, {
|
||||
await OAuth._endOfLoginResponse(res, {
|
||||
query: requestData,
|
||||
loginStyle: OAuth._loginStyleFromQuery(requestData),
|
||||
error: err
|
||||
@@ -237,11 +236,14 @@ const oauthServiceName = req => {
|
||||
};
|
||||
|
||||
// Make sure we're configured
|
||||
const ensureConfigured = serviceName => {
|
||||
if (!ServiceConfiguration.configurations.findOne({service: serviceName})) {
|
||||
throw new ServiceConfiguration.ConfigError();
|
||||
}
|
||||
};
|
||||
const ensureConfigured =
|
||||
async serviceName => {
|
||||
const config =
|
||||
await ServiceConfiguration.configurations.findOne({ service: serviceName })
|
||||
if (!config) {
|
||||
throw new ServiceConfiguration.ConfigError();
|
||||
}
|
||||
};
|
||||
|
||||
const isSafe = value => {
|
||||
// This matches strings generated by `Random.secret` and
|
||||
|
||||
@@ -16,18 +16,22 @@ OAuth._pendingCredentials = new Mongo.Collection(
|
||||
_preventAutopublish: true
|
||||
});
|
||||
|
||||
OAuth._pendingCredentials.createIndex('key', { unique: true });
|
||||
OAuth._pendingCredentials.createIndex('credentialSecret');
|
||||
OAuth._pendingCredentials.createIndex('createdAt');
|
||||
// TODO[FIBERS]: I Need TLA
|
||||
async function init() {
|
||||
await OAuth._pendingCredentials.createIndex('key', { unique: true });
|
||||
await OAuth._pendingCredentials.createIndex('credentialSecret');
|
||||
await OAuth._pendingCredentials.createIndex('createdAt');
|
||||
}
|
||||
init()
|
||||
|
||||
|
||||
|
||||
// Periodically clear old entries that were never retrieved
|
||||
const _cleanStaleResults = () => {
|
||||
const _cleanStaleResults = async () => {
|
||||
// Remove credentials older than 1 minute
|
||||
const timeCutoff = new Date();
|
||||
timeCutoff.setMinutes(timeCutoff.getMinutes() - 1);
|
||||
OAuth._pendingCredentials.remove({ createdAt: { $lt: timeCutoff } });
|
||||
await OAuth._pendingCredentials.remove({ createdAt: { $lt: timeCutoff } });
|
||||
};
|
||||
const _cleanupHandle = Meteor.setInterval(_cleanStaleResults, 60 * 1000);
|
||||
|
||||
@@ -78,7 +82,6 @@ OAuth._retrievePendingCredential =
|
||||
key,
|
||||
credentialSecret,
|
||||
});
|
||||
|
||||
if (pendingCredential) {
|
||||
await OAuth._pendingCredentials.remove({ _id: pendingCredential._id });
|
||||
if (pendingCredential.credential.error)
|
||||
|
||||
@@ -47,13 +47,13 @@ const _cleanupHandle = Meteor.setInterval(_cleanStaleResults, 60 * 1000);
|
||||
// @param requestToken {string}
|
||||
// @param requestTokenSecret {string}
|
||||
//
|
||||
OAuth._storeRequestToken = (key, requestToken, requestTokenSecret) => {
|
||||
OAuth._storeRequestToken = async (key, requestToken, requestTokenSecret) => {
|
||||
check(key, String);
|
||||
|
||||
// We do an upsert here instead of an insert in case the user happens
|
||||
// to somehow send the same `state` parameter twice during an OAuth
|
||||
// login; we don't want a duplicate key error.
|
||||
OAuth._pendingRequestTokens.upsert({
|
||||
await OAuth._pendingRequestTokens.upsert({
|
||||
key,
|
||||
}, {
|
||||
key,
|
||||
@@ -69,12 +69,12 @@ OAuth._storeRequestToken = (key, requestToken, requestTokenSecret) => {
|
||||
//
|
||||
// @param key {string}
|
||||
//
|
||||
OAuth._retrieveRequestToken = key => {
|
||||
OAuth._retrieveRequestToken = async key => {
|
||||
check(key, String);
|
||||
|
||||
const pendingRequestToken = OAuth._pendingRequestTokens.findOne({ key: key });
|
||||
const pendingRequestToken = await OAuth._pendingRequestTokens.findOne({ key: key });
|
||||
if (pendingRequestToken) {
|
||||
OAuth._pendingRequestTokens.remove({ _id: pendingRequestToken._id });
|
||||
await OAuth._pendingRequestTokens.remove({ _id: pendingRequestToken._id });
|
||||
return {
|
||||
requestToken: OAuth.openSecret(pendingRequestToken.requestToken),
|
||||
requestTokenSecret: OAuth.openSecret(
|
||||
|
||||
@@ -26,7 +26,7 @@ OAuth._queryParamsWithAuthTokenUrl = (authUrl, oauthBinding, params = {}, whitel
|
||||
|
||||
// connect middleware
|
||||
OAuth._requestHandlers['1'] = async (service, query, res) => {
|
||||
const config = ServiceConfiguration.configurations.findOne({service: service.serviceName});
|
||||
const config = await ServiceConfiguration.configurations.findOne({service: service.serviceName});
|
||||
if (! config) {
|
||||
throw new ServiceConfiguration.ConfigError(service.serviceName);
|
||||
}
|
||||
@@ -48,7 +48,7 @@ OAuth._requestHandlers['1'] = async (service, query, res) => {
|
||||
await oauthBinding.prepareRequestToken(callbackUrl);
|
||||
|
||||
// Keep track of request token so we can verify it on the next step
|
||||
OAuth._storeRequestToken(
|
||||
await OAuth._storeRequestToken(
|
||||
OAuth._credentialTokenFromQuery(query),
|
||||
oauthBinding.requestToken,
|
||||
oauthBinding.requestTokenSecret);
|
||||
@@ -76,7 +76,7 @@ OAuth._requestHandlers['1'] = async (service, query, res) => {
|
||||
// and close the window to allow the login handler to proceed
|
||||
|
||||
// Get the user's request token so we can verify it and clear it
|
||||
const requestTokenInfo = OAuth._retrieveRequestToken(
|
||||
const requestTokenInfo = await OAuth._retrieveRequestToken(
|
||||
OAuth._credentialTokenFromQuery(query));
|
||||
|
||||
if (! requestTokenInfo) {
|
||||
@@ -102,7 +102,7 @@ OAuth._requestHandlers['1'] = async (service, query, res) => {
|
||||
|
||||
// Store the login result so it can be retrieved in another
|
||||
// browser tab by the result handler
|
||||
OAuth._storePendingCredential(credentialToken, {
|
||||
await OAuth._storePendingCredential(credentialToken, {
|
||||
serviceName: service.serviceName,
|
||||
serviceData: oauthResult.serviceData,
|
||||
options: oauthResult.options
|
||||
@@ -111,6 +111,6 @@ OAuth._requestHandlers['1'] = async (service, query, res) => {
|
||||
|
||||
// Either close the window, redirect, or render nothing
|
||||
// if all else fails
|
||||
OAuth._renderOauthResults(res, query, credentialSecret);
|
||||
await OAuth._renderOauthResults(res, query, credentialSecret);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -23,7 +23,7 @@ const testPendingCredential = async (test, method) => {
|
||||
this.accessTokenSecret = twitterfooAccessTokenSecret;
|
||||
};
|
||||
|
||||
ServiceConfiguration.configurations.insert({service: serviceName});
|
||||
await ServiceConfiguration.configurations.insert({service: serviceName});
|
||||
|
||||
try {
|
||||
// register a fake login service
|
||||
@@ -40,7 +40,7 @@ const testPendingCredential = async (test, method) => {
|
||||
}));
|
||||
|
||||
// simulate logging in using twitterfoo
|
||||
OAuth._storeRequestToken(credentialToken, twitterfooAccessToken);
|
||||
await OAuth._storeRequestToken(credentialToken, twitterfooAccessToken);
|
||||
|
||||
const req = {
|
||||
method,
|
||||
@@ -73,9 +73,8 @@ const testPendingCredential = async (test, method) => {
|
||||
};
|
||||
await OAuthTest.middleware(req, res);
|
||||
const credentialSecret = respData;
|
||||
|
||||
// Test that the result for the token is available
|
||||
let result = OAuth._retrievePendingCredential(credentialToken,
|
||||
let result = await OAuth._retrievePendingCredential(credentialToken,
|
||||
credentialSecret);
|
||||
const serviceData = OAuth.openSecrets(result.serviceData);
|
||||
test.equal(result.serviceName, serviceName);
|
||||
@@ -86,7 +85,7 @@ const testPendingCredential = async (test, method) => {
|
||||
test.equal(result.options.option1, twitterOption1);
|
||||
|
||||
// Test that pending credential is removed after being retrieved
|
||||
result = OAuth._retrievePendingCredential(credentialToken);
|
||||
result = await OAuth._retrievePendingCredential(credentialToken);
|
||||
test.isUndefined(result);
|
||||
|
||||
} finally {
|
||||
@@ -110,24 +109,24 @@ Tinytest.addAsync("oauth1 - pendingCredential is stored and can be retrieved (wi
|
||||
}
|
||||
});
|
||||
|
||||
Tinytest.add("oauth1 - duplicate key for request token", test => {
|
||||
Tinytest.addAsync("oauth1 - duplicate key for request token", async test => {
|
||||
const key = Random.id();
|
||||
const token = Random.id();
|
||||
const secret = Random.id();
|
||||
OAuth._storeRequestToken(key, token, secret);
|
||||
await OAuth._storeRequestToken(key, token, secret);
|
||||
const newToken = Random.id();
|
||||
const newSecret = Random.id();
|
||||
OAuth._storeRequestToken(key, newToken, newSecret);
|
||||
const result = OAuth._retrieveRequestToken(key);
|
||||
await OAuth._storeRequestToken(key, newToken, newSecret);
|
||||
const result = await OAuth._retrieveRequestToken(key);
|
||||
test.equal(result.requestToken, newToken);
|
||||
test.equal(result.requestTokenSecret, newSecret);
|
||||
});
|
||||
|
||||
Tinytest.add("oauth1 - null, undefined key for request token", test => {
|
||||
Tinytest.addAsync("oauth1 - null, undefined key for request token", async test => {
|
||||
const token = Random.id();
|
||||
const secret = Random.id();
|
||||
test.throws(() => OAuth._storeRequestToken(null, token, secret));
|
||||
test.throws(() => OAuth._storeRequestToken(undefined, token, secret));
|
||||
await test.throwsAsync(() => OAuth._storeRequestToken(null, token, secret));
|
||||
await test.throwsAsync(() => OAuth._storeRequestToken(undefined, token, secret));
|
||||
});
|
||||
|
||||
Tinytest.add("oauth1 - signature is built correctly", test => {
|
||||
|
||||
Reference in New Issue
Block a user