diff --git a/History.md b/History.md index 81771d0cb9..b63e454505 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,17 @@ +## v2.6, 2021-11- + +#### Highlights + +#### Breaking Changes + +#### Meteor Version Release + +#### Independent Releases + +* `github-oauth@1.3.2` + - Migrate from `http` to `fetch` + - Fix GitHub login params to adhere to changes in GitHub API + ## v2.5, 2021-10-21 #### Highlights diff --git a/packages/github-oauth/github_server.js b/packages/github-oauth/github_server.js index e86940eb8d..2991fdb0c9 100644 --- a/packages/github-oauth/github_server.js +++ b/packages/github-oauth/github_server.js @@ -1,11 +1,13 @@ Github = {}; -OAuth.registerService('github', 2, null, query => { - - const accessToken = getAccessToken(query); - const identity = getIdentity(accessToken); - const emails = getEmails(accessToken); - const primaryEmail = emails.find(email => email.primary); +OAuth.registerService('github', 2, null, (query) => { + const accessTokenCall = Meteor.wrapAsync(getAccessToken); + const accessToken = accessTokenCall(query); + const identityCall = Meteor.wrapAsync(getIdentity); + const identity = identityCall(accessToken); + const emailsCall = Meteor.wrapAsync(getEmails); + const emails = emailsCall(accessToken); + const primaryEmail = emails.find((email) => email.primary); return { serviceData: { @@ -13,72 +15,101 @@ OAuth.registerService('github', 2, null, query => { accessToken: OAuth.sealSecret(accessToken), email: identity.email || (primaryEmail && primaryEmail.email) || '', username: identity.login, - emails, + emails }, - options: {profile: {name: identity.name}} + options: { profile: { name: identity.name } } }; }); // http://developer.github.com/v3/#user-agent-required -let userAgent = "Meteor"; -if (Meteor.release) - userAgent += `/${Meteor.release}`; +let userAgent = 'Meteor'; +if (Meteor.release) userAgent += `/${Meteor.release}`; -const getAccessToken = query => { - const config = ServiceConfiguration.configurations.findOne({service: 'github'}); - if (!config) - throw new ServiceConfiguration.ConfigError(); +const getAccessToken = async (query, callback) => { + const config = ServiceConfiguration.configurations.findOne({ + service: 'github' + }); + if (!config) throw new ServiceConfiguration.ConfigError(); let response; try { - response = HTTP.post( - "https://github.com/login/oauth/access_token", { + const content = new URLSearchParams({ + client_id: config.clientId, + client_secret: config.secret, + code: query.code, + redirect_uri: OAuth._redirectUri( + 'github', + config + ) + }); + const request = await fetch( + `https://github.com/login/oauth/access_token?${content.toString()}`, + { + method: 'POST', headers: { Accept: 'application/json', - "User-Agent": userAgent - }, - params: { - code: query.code, - client_id: config.clientId, - client_secret: OAuth.openSecret(config.secret), - redirect_uri: OAuth._redirectUri('github', config), - state: query.state + 'User-Agent': userAgent } - }); + } + ); + response = await request.json(); } catch (err) { throw Object.assign( - new Error(`Failed to complete OAuth handshake with Github. ${err.message}`), - { response: err.response }, + new Error( + `Failed to complete OAuth handshake with Github. ${err.message}` + ), + { response: err.response } ); } - if (response.data.error) { // if the http response was a json object with an error attribute - throw new Error(`Failed to complete OAuth handshake with GitHub. ${response.data.error}`); + if (response.error) { + callback(response.error); + // if the http response was a json object with an error attribute + throw new Error( + `Failed to complete OAuth handshake with GitHub. ${response.error}` + ); } else { - return response.data.access_token; + callback(null, response.access_token); + return response.access_token; } }; -const getIdentity = accessToken => { +const getIdentity = async (accessToken, callback) => { try { - return HTTP.get( - "https://api.github.com/user", { - headers: {"User-Agent": userAgent, "Authorization": `token ${accessToken}`}, // http://developer.github.com/v3/#user-agent-required - }).data; + const request = await fetch('https://api.github.com/user', { + method: 'GET', + headers: { + Accept: 'application/json', + 'User-Agent': userAgent, + Authorization: `token ${accessToken}` + } // http://developer.github.com/v3/#user-agent-required + }); + const response = await request.json(); + callback(null, response); + return response; } catch (err) { + callback(err.message); throw Object.assign( new Error(`Failed to fetch identity from Github. ${err.message}`), - { response: err.response }, + { response: err.response } ); } }; -const getEmails = accessToken => { +const getEmails = async (accessToken, callback) => { try { - return HTTP.get( - "https://api.github.com/user/emails", { - headers: {"User-Agent": userAgent, "Authorization": `token ${accessToken}`}, // http://developer.github.com/v3/#user-agent-required - }).data; + const request = await fetch('https://api.github.com/user/emails', { + method: 'GET', + headers: { + 'User-Agent': userAgent, + Accept: 'application/json', + Authorization: `token ${accessToken}` + } // http://developer.github.com/v3/#user-agent-required + }); + const response = await request.json(); + callback(null, response); + return response; } catch (err) { + callback(err.message, []); return []; } }; diff --git a/packages/github-oauth/package.js b/packages/github-oauth/package.js index c9ca612e01..959a69a86b 100644 --- a/packages/github-oauth/package.js +++ b/packages/github-oauth/package.js @@ -1,13 +1,13 @@ Package.describe({ summary: 'GitHub OAuth flow', - version: '1.3.1' + version: '1.3.2' }); Package.onUse(api => { api.use('ecmascript', ['client', 'server']); api.use('oauth2', ['client', 'server']); api.use('oauth', ['client', 'server']); - api.use('http@1.4.4 || 2.0.0', 'server'); + api.use('fetch', 'server'); api.use('random', 'client'); api.use('service-configuration', ['client', 'server']); diff --git a/packages/oauth/oauth_server.js b/packages/oauth/oauth_server.js index 9f650a102c..6d7b0cb578 100644 --- a/packages/oauth/oauth_server.js +++ b/packages/oauth/oauth_server.js @@ -13,22 +13,23 @@ const registeredServices = {}; OAuth._requestHandlers = {}; -// Register a handler for an OAuth service. The handler will be called -// when we get an incoming http request on /_oauth/{serviceName}. This -// handler should use that information to fetch data about the user -// logging in. -// -// @param name {String} e.g. "google", "facebook" -// @param version {Number} OAuth version (1 or 2) -// @param urls For OAuth1 only, specify the service's urls -// @param handleOauthRequest {Function(oauthBinding|query)} -// - (For OAuth1 only) oauthBinding {OAuth1Binding} bound to the appropriate provider -// - (For OAuth2 only) query {Object} parameters passed in query string -// - return value is: -// - {serviceData:, (optional options:)} where serviceData should end -// up in the user's services[name] field -// - `null` if the user declined to give permissions -// +/** +/* Register a handler for an OAuth service. The handler will be called +/* when we get an incoming http request on /_oauth/{serviceName}. This +/* handler should use that information to fetch data about the user +/* logging in. +/* +/* @param name {String} e.g. "google", "facebook" +/* @param version {Number} OAuth version (1 or 2) +/* @param urls For OAuth1 only, specify the service's urls +/* @param handleOauthRequest {Function(oauthBinding|query)} +/* - (For OAuth1 only) oauthBinding {OAuth1Binding} bound to the appropriate provider +/* - (For OAuth2 only) query {Object} parameters passed in query string +/* - return value is: +/* - {serviceData:, (optional options:)} where serviceData should end +/* up in the user's services[name] field +/* - `null` if the user declined to give permissions +*/ OAuth.registerService = (name, version, urls, handleOauthRequest) => { if (registeredServices[name]) throw new Error(`Already registered the ${name} OAuth service`);