mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
Merge pull request #11723 from meteor/feature/github-fetch
Fix GitHub OAuth
This commit is contained in:
14
History.md
14
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
|
||||
|
||||
@@ -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 [];
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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']);
|
||||
|
||||
|
||||
@@ -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`);
|
||||
|
||||
Reference in New Issue
Block a user