diff --git a/spec/AuthenticationAdapters.spec.js b/spec/AuthenticationAdapters.spec.js index 6cc603a29d..f0cdfa2f92 100644 --- a/spec/AuthenticationAdapters.spec.js +++ b/spec/AuthenticationAdapters.spec.js @@ -15,6 +15,7 @@ const responses = { weibo: { uid: 'userId' }, qq: 'callback( {"openid":"userId"} );', // yes it's like that, run eval in the client :P phantauth: { sub: 'userId' }, + microsoft: { id: 'userId', mail: 'userMail' }, }; describe('AuthenticationProviders', function() { @@ -37,7 +38,8 @@ describe('AuthenticationProviders', function() { 'wechat', 'weibo', 'phantauth', - ].map(function(providerName) { + 'microsoft', + ].map(function (providerName) { it('Should validate structure of ' + providerName, done => { const provider = require('../lib/Adapters/Auth/' + providerName); jequal(typeof provider.validateAuthData, 'function'); @@ -1194,3 +1196,17 @@ describe('phant auth adapter', () => { } }); }); + +describe('microsoft graph auth adapter', () => { + const microsoft = require('../lib/Adapters/Auth/microsoft'); + const httpsRequest = require('../lib/Adapters/Auth/httpsRequest'); + + it('should use access_token for validation is passed and responds with id and mail', async () => { + spyOn(httpsRequest, 'get').and.callFake(() => { + return Promise.resolve({ id: 'userId', mail: 'userMail' }); + }); + await microsoft.validateAuthData( + { id: 'userId', access_token: 'the_token' } + ); + }); +}); diff --git a/spec/MicrosoftAuth.spec.js b/spec/MicrosoftAuth.spec.js new file mode 100644 index 0000000000..71d14e6bd0 --- /dev/null +++ b/spec/MicrosoftAuth.spec.js @@ -0,0 +1,18 @@ +const microsoft = require('../lib/Adapters/Auth/microsoft'); + +describe('Microsoft Auth', () => { + it('should fail to validate Microsoft Graph auth with bad token', done => { + const authData = { + id: 'fake-id', + mail: 'fake@mail.com', + access_token: 'very.long.bad.token', + }; + microsoft.validateAuthData(authData).then(done.fail, err => { + expect(err.code).toBe(101); + expect(err.message).toBe( + 'Microsoft Graph auth is invalid for this user.' + ); + done(); + }); + }); +}); diff --git a/src/Adapters/Auth/index.js b/src/Adapters/Auth/index.js index 9e4156624e..fa68d5ec50 100755 --- a/src/Adapters/Auth/index.js +++ b/src/Adapters/Auth/index.js @@ -20,6 +20,7 @@ const wechat = require('./wechat'); const weibo = require('./weibo'); const oauth2 = require('./oauth2'); const phantauth = require('./phantauth'); +const microsoft = require('./microsoft'); const anonymous = { validateAuthData: () => { @@ -51,6 +52,7 @@ const providers = { wechat, weibo, phantauth, + microsoft, }; function authDataValidator(adapter, appIds, options) { diff --git a/src/Adapters/Auth/microsoft.js b/src/Adapters/Auth/microsoft.js new file mode 100644 index 0000000000..1574045528 --- /dev/null +++ b/src/Adapters/Auth/microsoft.js @@ -0,0 +1,39 @@ +// Helper functions for accessing the microsoft graph API. +var Parse = require('parse/node').Parse; +const httpsRequest = require('./httpsRequest'); + +// Returns a promise that fulfills if this user mail is valid. +function validateAuthData(authData) { + return request('me', authData.access_token).then( + response => { + if (response && response.id && response.id == authData.id) { + return; + } + throw new Parse.Error( + Parse.Error.OBJECT_NOT_FOUND, + 'Microsoft Graph auth is invalid for this user.' + ); + } + ); +} + +// Returns a promise that fulfills if this app id is valid. +function validateAppId() { + return Promise.resolve(); +} + +// A promisey wrapper for api requests +function request(path, access_token) { + return httpsRequest.get({ + host: 'graph.microsoft.com', + path: '/v1.0/' + path, + headers: { + Authorization: 'Bearer ' + access_token, + }, + }); +} + +module.exports = { + validateAppId: validateAppId, + validateAuthData: validateAuthData, +};