mirror of
				https://github.com/strapi/strapi.git
				synced 2025-10-31 09:56:44 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			293 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			293 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 'use strict';
 | |
| 
 | |
| const { strict: assert } = require('assert');
 | |
| const jwt = require('jsonwebtoken');
 | |
| 
 | |
| const getInitialProviders = ({ purest }) => ({
 | |
|   async discord({ accessToken }) {
 | |
|     const discord = purest({ provider: 'discord' });
 | |
|     return discord
 | |
|       .get('users/@me')
 | |
|       .auth(accessToken)
 | |
|       .request()
 | |
|       .then(({ body }) => {
 | |
|         // Combine username and discriminator because discord username is not unique
 | |
|         const username = `${body.username}#${body.discriminator}`;
 | |
|         return {
 | |
|           username,
 | |
|           email: body.email,
 | |
|         };
 | |
|       });
 | |
|   },
 | |
|   async cognito({ query }) {
 | |
|     // get the id_token
 | |
|     const idToken = query.id_token;
 | |
|     // decode the jwt token
 | |
|     const tokenPayload = jwt.decode(idToken);
 | |
|     if (!tokenPayload) {
 | |
|       throw new Error('unable to decode jwt token');
 | |
|     } else {
 | |
|       return {
 | |
|         username: tokenPayload['cognito:username'],
 | |
|         email: tokenPayload.email,
 | |
|       };
 | |
|     }
 | |
|   },
 | |
|   async facebook({ accessToken }) {
 | |
|     const facebook = purest({ provider: 'facebook' });
 | |
| 
 | |
|     return facebook
 | |
|       .get('me')
 | |
|       .auth(accessToken)
 | |
|       .qs({ fields: 'name,email' })
 | |
|       .request()
 | |
|       .then(({ body }) => ({
 | |
|         username: body.name,
 | |
|         email: body.email,
 | |
|       }));
 | |
|   },
 | |
|   async google({ accessToken }) {
 | |
|     const google = purest({ provider: 'google' });
 | |
| 
 | |
|     return google
 | |
|       .query('oauth')
 | |
|       .get('tokeninfo')
 | |
|       .qs({ accessToken })
 | |
|       .request()
 | |
|       .then(({ body }) => ({
 | |
|         username: body.email.split('@')[0],
 | |
|         email: body.email,
 | |
|       }));
 | |
|   },
 | |
|   async github({ accessToken }) {
 | |
|     const github = purest({
 | |
|       provider: 'github',
 | |
|       defaults: {
 | |
|         headers: {
 | |
|           'user-agent': 'strapi',
 | |
|         },
 | |
|       },
 | |
|     });
 | |
| 
 | |
|     const { body: userBody } = await github.get('user').auth(accessToken).request();
 | |
| 
 | |
|     // This is the public email on the github profile
 | |
|     if (userBody.email) {
 | |
|       return {
 | |
|         username: userBody.login,
 | |
|         email: userBody.email,
 | |
|       };
 | |
|     }
 | |
|     // Get the email with Github's user/emails API
 | |
|     const { body: emailBody } = await github.get('user/emails').auth(accessToken).request();
 | |
| 
 | |
|     return {
 | |
|       username: userBody.login,
 | |
|       email: Array.isArray(emailBody)
 | |
|         ? emailBody.find((email) => email.primary === true).email
 | |
|         : null,
 | |
|     };
 | |
|   },
 | |
|   async microsoft({ accessToken }) {
 | |
|     const microsoft = purest({ provider: 'microsoft' });
 | |
| 
 | |
|     return microsoft
 | |
|       .get('me')
 | |
|       .auth(accessToken)
 | |
|       .request()
 | |
|       .then(({ body }) => ({
 | |
|         username: body.userPrincipalName,
 | |
|         email: body.userPrincipalName,
 | |
|       }));
 | |
|   },
 | |
|   async twitter({ accessToken, query, providers }) {
 | |
|     const twitter = purest({
 | |
|       provider: 'twitter',
 | |
|       defaults: {
 | |
|         oauth: {
 | |
|           consumer_key: providers.twitter.key,
 | |
|           consumer_secret: providers.twitter.secret,
 | |
|         },
 | |
|       },
 | |
|     });
 | |
| 
 | |
|     return twitter
 | |
|       .get('account/verify_credentials')
 | |
|       .auth(accessToken, query.access_secret)
 | |
|       .qs({ screen_name: query['raw[screen_name]'], include_email: 'true' })
 | |
|       .request()
 | |
|       .then(({ body }) => ({
 | |
|         username: body.screen_name,
 | |
|         email: body.email,
 | |
|       }));
 | |
|   },
 | |
|   async instagram({ accessToken }) {
 | |
|     const instagram = purest({ provider: 'instagram' });
 | |
| 
 | |
|     return instagram
 | |
|       .get('me')
 | |
|       .auth(accessToken)
 | |
|       .qs({ fields: 'id,username' })
 | |
|       .request()
 | |
|       .then(({ body }) => ({
 | |
|         username: body.username,
 | |
|         email: `${body.username}@strapi.io`, // dummy email as Instagram does not provide user email
 | |
|       }));
 | |
|   },
 | |
|   async vk({ accessToken, query }) {
 | |
|     const vk = purest({ provider: 'vk' });
 | |
| 
 | |
|     return vk
 | |
|       .get('users.get')
 | |
|       .auth(accessToken)
 | |
|       .qs({ id: query.raw.user_id, v: '5.122' })
 | |
|       .request()
 | |
|       .then(({ body }) => ({
 | |
|         username: `${body.response[0].last_name} ${body.response[0].first_name}`,
 | |
|         email: query.raw.email,
 | |
|       }));
 | |
|   },
 | |
|   async twitch({ accessToken, providers }) {
 | |
|     const twitch = purest({
 | |
|       provider: 'twitch',
 | |
|       config: {
 | |
|         twitch: {
 | |
|           default: {
 | |
|             origin: 'https://api.twitch.tv',
 | |
|             path: 'helix/{path}',
 | |
|             headers: {
 | |
|               Authorization: 'Bearer {auth}',
 | |
|               'Client-Id': '{auth}',
 | |
|             },
 | |
|           },
 | |
|         },
 | |
|       },
 | |
|     });
 | |
| 
 | |
|     return twitch
 | |
|       .get('users')
 | |
|       .auth(accessToken, providers.twitch.key)
 | |
|       .request()
 | |
|       .then(({ body }) => ({
 | |
|         username: body.data[0].login,
 | |
|         email: body.data[0].email,
 | |
|       }));
 | |
|   },
 | |
|   async linkedin({ accessToken }) {
 | |
|     const linkedIn = purest({ provider: 'linkedin' });
 | |
|     const {
 | |
|       body: { localizedFirstName },
 | |
|     } = await linkedIn.get('me').auth(accessToken).request();
 | |
|     const {
 | |
|       body: { elements },
 | |
|     } = await linkedIn
 | |
|       .get('emailAddress?q=members&projection=(elements*(handle~))')
 | |
|       .auth(accessToken)
 | |
|       .request();
 | |
| 
 | |
|     const email = elements[0]['handle~'];
 | |
| 
 | |
|     return {
 | |
|       username: localizedFirstName,
 | |
|       email: email.emailAddress,
 | |
|     };
 | |
|   },
 | |
|   async reddit({ accessToken }) {
 | |
|     const reddit = purest({
 | |
|       provider: 'reddit',
 | |
|       config: {
 | |
|         reddit: {
 | |
|           default: {
 | |
|             origin: 'https://oauth.reddit.com',
 | |
|             path: 'api/{version}/{path}',
 | |
|             version: 'v1',
 | |
|             headers: {
 | |
|               Authorization: 'Bearer {auth}',
 | |
|               'user-agent': 'strapi',
 | |
|             },
 | |
|           },
 | |
|         },
 | |
|       },
 | |
|     });
 | |
| 
 | |
|     return reddit
 | |
|       .get('me')
 | |
|       .auth(accessToken)
 | |
|       .request()
 | |
|       .then(({ body }) => ({
 | |
|         username: body.name,
 | |
|         email: `${body.name}@strapi.io`, // dummy email as Reddit does not provide user email
 | |
|       }));
 | |
|   },
 | |
|   async auth0({ accessToken, providers }) {
 | |
|     const auth0 = purest({ provider: 'auth0' });
 | |
| 
 | |
|     return auth0
 | |
|       .get('userinfo')
 | |
|       .subdomain(providers.auth0.subdomain)
 | |
|       .auth(accessToken)
 | |
|       .request()
 | |
|       .then(({ body }) => {
 | |
|         const username = body.username || body.nickname || body.name || body.email.split('@')[0];
 | |
|         const email = body.email || `${username.replace(/\s+/g, '.')}@strapi.io`;
 | |
| 
 | |
|         return {
 | |
|           username,
 | |
|           email,
 | |
|         };
 | |
|       });
 | |
|   },
 | |
|   async cas({ accessToken, providers }) {
 | |
|     const cas = purest({ provider: 'cas' });
 | |
| 
 | |
|     return cas
 | |
|       .get('oidc/profile')
 | |
|       .subdomain(providers.cas.subdomain)
 | |
|       .auth(accessToken)
 | |
|       .request()
 | |
|       .then(({ body }) => {
 | |
|         // CAS attribute may be in body.attributes or "FLAT", depending on CAS config
 | |
|         const username = body.attributes
 | |
|           ? body.attributes.strapiusername || body.id || body.sub
 | |
|           : body.strapiusername || body.id || body.sub;
 | |
|         const email = body.attributes
 | |
|           ? body.attributes.strapiemail || body.attributes.email
 | |
|           : body.strapiemail || body.email;
 | |
|         if (!username || !email) {
 | |
|           strapi.log.warn(
 | |
|             `CAS Response Body did not contain required attributes: ${JSON.stringify(body)}`
 | |
|           );
 | |
|         }
 | |
|         return {
 | |
|           username,
 | |
|           email,
 | |
|         };
 | |
|       });
 | |
|   },
 | |
| });
 | |
| 
 | |
| module.exports = () => {
 | |
|   const purest = require('purest');
 | |
| 
 | |
|   const providersCallbacks = getInitialProviders({ purest });
 | |
| 
 | |
|   return {
 | |
|     register(providerName, provider) {
 | |
|       assert(typeof providerName === 'string', 'Provider name must be a string');
 | |
|       assert(typeof provider === 'function', 'Provider callback must be a function');
 | |
| 
 | |
|       providersCallbacks[providerName] = provider({ purest });
 | |
|     },
 | |
| 
 | |
|     async run({ provider, accessToken, query, providers }) {
 | |
|       if (!providersCallbacks[provider]) {
 | |
|         throw new Error('Unknown provider.');
 | |
|       }
 | |
| 
 | |
|       const providerCb = providersCallbacks[provider];
 | |
| 
 | |
|       return providerCb({ accessToken, query, providers });
 | |
|     },
 | |
|   };
 | |
| };
 | 
