313 lines
9.7 KiB
JavaScript
313 lines
9.7 KiB
JavaScript
|
|
const LinkedInAdapter = require('../../../lib/Adapters/Auth/linkedin').default;
|
|
describe('LinkedInAdapter', function () {
|
|
let adapter;
|
|
const validOptions = {
|
|
clientId: 'validClientId',
|
|
clientSecret: 'validClientSecret',
|
|
enableInsecureAuth: false,
|
|
};
|
|
|
|
beforeEach(function () {
|
|
adapter = new LinkedInAdapter.constructor();
|
|
});
|
|
|
|
describe('Test configuration errors', function () {
|
|
it('should throw error for missing options', function () {
|
|
const invalidOptions = [null, undefined, {}, { clientId: 'validClientId' }];
|
|
|
|
for (const options of invalidOptions) {
|
|
expect(() => {
|
|
adapter.validateOptions(options);
|
|
}).toThrow();
|
|
}
|
|
});
|
|
|
|
it('should validate options successfully with valid parameters', function () {
|
|
expect(() => {
|
|
adapter.validateOptions(validOptions);
|
|
}).not.toThrow();
|
|
expect(adapter.clientId).toBe(validOptions.clientId);
|
|
expect(adapter.clientSecret).toBe(validOptions.clientSecret);
|
|
expect(adapter.enableInsecureAuth).toBe(validOptions.enableInsecureAuth);
|
|
});
|
|
});
|
|
|
|
describe('Test beforeFind', function () {
|
|
it('should throw error for invalid payload', async function () {
|
|
adapter.enableInsecureAuth = true;
|
|
|
|
const payloads = [{}, { access_token: null }];
|
|
|
|
for (const payload of payloads) {
|
|
await expectAsync(adapter.beforeFind(payload)).toBeRejectedWith(
|
|
new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'LinkedIn auth is invalid for this user.')
|
|
);
|
|
}
|
|
});
|
|
|
|
it('should process secure payload and set auth data', async function () {
|
|
spyOn(adapter, 'getAccessTokenFromCode').and.returnValue(
|
|
Promise.resolve('validToken')
|
|
);
|
|
spyOn(adapter, 'getUserFromAccessToken').and.returnValue(
|
|
Promise.resolve({ id: 'validUserId' })
|
|
);
|
|
|
|
const authData = { code: 'validCode', redirect_uri: 'http://example.com', is_mobile_sdk: false };
|
|
|
|
await adapter.beforeFind(authData);
|
|
|
|
expect(authData.access_token).toBe('validToken');
|
|
expect(authData.id).toBe('validUserId');
|
|
});
|
|
|
|
it('should validate insecure auth and match user id', async function () {
|
|
adapter.enableInsecureAuth = true;
|
|
spyOn(adapter, 'getUserFromAccessToken').and.returnValue(
|
|
Promise.resolve({ id: 'validUserId' })
|
|
);
|
|
|
|
const authData = { access_token: 'validToken', id: 'validUserId', is_mobile_sdk: false };
|
|
|
|
await expectAsync(adapter.beforeFind(authData)).toBeResolved();
|
|
});
|
|
|
|
it('should throw error if insecure auth user id does not match', async function () {
|
|
adapter.enableInsecureAuth = true;
|
|
spyOn(adapter, 'getUserFromAccessToken').and.returnValue(
|
|
Promise.resolve({ id: 'invalidUserId' })
|
|
);
|
|
|
|
const authData = { access_token: 'validToken', id: 'validUserId', is_mobile_sdk: false };
|
|
|
|
await expectAsync(adapter.beforeFind(authData)).toBeRejectedWith(
|
|
new Error('LinkedIn auth is invalid for this user.')
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('Test getUserFromAccessToken', function () {
|
|
it('should fetch user successfully', async function () {
|
|
global.fetch = jasmine.createSpy().and.returnValue(
|
|
Promise.resolve({
|
|
ok: true,
|
|
json: () => Promise.resolve({ id: 'validUserId' }),
|
|
})
|
|
);
|
|
|
|
const user = await adapter.getUserFromAccessToken('validToken', false);
|
|
|
|
expect(global.fetch).toHaveBeenCalledWith('https://api.linkedin.com/v2/me', {
|
|
headers: {
|
|
Authorization: `Bearer validToken`,
|
|
'x-li-format': 'json',
|
|
'x-li-src': undefined,
|
|
},
|
|
});
|
|
expect(user).toEqual({ id: 'validUserId' });
|
|
});
|
|
|
|
it('should throw error for invalid response', async function () {
|
|
global.fetch = jasmine.createSpy().and.returnValue(
|
|
Promise.resolve({ ok: false })
|
|
);
|
|
|
|
await expectAsync(adapter.getUserFromAccessToken('invalidToken', false)).toBeRejectedWith(
|
|
new Error('LinkedIn API request failed.')
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('Test getAccessTokenFromCode', function () {
|
|
it('should fetch token successfully', async function () {
|
|
global.fetch = jasmine.createSpy().and.returnValue(
|
|
Promise.resolve({
|
|
ok: true,
|
|
json: () => Promise.resolve({ access_token: 'validToken' }),
|
|
})
|
|
);
|
|
|
|
const tokenResponse = await adapter.getAccessTokenFromCode('validCode', 'http://example.com');
|
|
|
|
expect(global.fetch).toHaveBeenCalledWith('https://www.linkedin.com/oauth/v2/accessToken', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
body: jasmine.any(URLSearchParams),
|
|
});
|
|
expect(tokenResponse).toEqual('validToken');
|
|
});
|
|
|
|
it('should throw error for invalid response', async function () {
|
|
global.fetch = jasmine.createSpy().and.returnValue(
|
|
Promise.resolve({ ok: false })
|
|
);
|
|
|
|
await expectAsync(
|
|
adapter.getAccessTokenFromCode('invalidCode', 'http://example.com')
|
|
).toBeRejectedWith(new Error('LinkedIn API request failed.'));
|
|
});
|
|
});
|
|
|
|
describe('Test validate methods', function () {
|
|
const authData = { id: 'validUserId', access_token: 'validToken' };
|
|
|
|
it('validateLogin should return user id', function () {
|
|
const result = adapter.validateLogin(authData);
|
|
expect(result).toEqual({ id: 'validUserId' });
|
|
});
|
|
|
|
it('validateSetUp should return user id', function () {
|
|
const result = adapter.validateSetUp(authData);
|
|
expect(result).toEqual({ id: 'validUserId' });
|
|
});
|
|
|
|
it('validateUpdate should return user id', function () {
|
|
const result = adapter.validateUpdate(authData);
|
|
expect(result).toEqual({ id: 'validUserId' });
|
|
});
|
|
|
|
it('afterFind should return user id', function () {
|
|
const result = adapter.afterFind(authData);
|
|
expect(result).toEqual({ id: 'validUserId' });
|
|
});
|
|
});
|
|
|
|
describe('LinkedInAdapter E2E Test', function () {
|
|
beforeEach(async function () {
|
|
await reconfigureServer({
|
|
auth: {
|
|
linkedin: {
|
|
clientId: 'validClientId',
|
|
clientSecret: 'validClientSecret',
|
|
},
|
|
},
|
|
});
|
|
});
|
|
|
|
it('should log in user using LinkedIn adapter successfully (secure)', async function () {
|
|
mockFetch([
|
|
{
|
|
url: 'https://www.linkedin.com/oauth/v2/accessToken',
|
|
method: 'POST',
|
|
response: {
|
|
ok: true,
|
|
json: () =>
|
|
Promise.resolve({
|
|
access_token: 'mockAccessToken123',
|
|
}),
|
|
},
|
|
},
|
|
{
|
|
url: 'https://api.linkedin.com/v2/me',
|
|
method: 'GET',
|
|
response: {
|
|
ok: true,
|
|
json: () =>
|
|
Promise.resolve({
|
|
id: 'mockUserId',
|
|
}),
|
|
},
|
|
},
|
|
]);
|
|
|
|
const authData = { code: 'validCode', redirect_uri: 'https://example.com/callback' };
|
|
const user = await Parse.User.logInWith('linkedin', { authData });
|
|
|
|
expect(user.id).toBeDefined();
|
|
expect(global.fetch).toHaveBeenCalledWith(
|
|
'https://www.linkedin.com/oauth/v2/accessToken',
|
|
jasmine.any(Object)
|
|
);
|
|
expect(global.fetch).toHaveBeenCalledWith(
|
|
'https://api.linkedin.com/v2/me',
|
|
jasmine.any(Object)
|
|
);
|
|
});
|
|
|
|
it('should handle error when LinkedIn returns invalid user data', async function () {
|
|
mockFetch([
|
|
{
|
|
url: 'https://www.linkedin.com/oauth/v2/accessToken',
|
|
method: 'POST',
|
|
response: {
|
|
ok: true,
|
|
json: () =>
|
|
Promise.resolve({
|
|
access_token: 'mockAccessToken123',
|
|
}),
|
|
},
|
|
},
|
|
{
|
|
url: 'https://api.linkedin.com/v2/me',
|
|
method: 'GET',
|
|
response: {
|
|
ok: false,
|
|
statusText: 'Unauthorized',
|
|
},
|
|
},
|
|
]);
|
|
|
|
const authData = { code: 'validCode', redirect_uri: 'https://example.com/callback' };
|
|
|
|
await expectAsync(Parse.User.logInWith('linkedin', { authData })).toBeRejectedWithError(
|
|
'LinkedIn API request failed.'
|
|
);
|
|
|
|
expect(global.fetch).toHaveBeenCalledWith(
|
|
'https://www.linkedin.com/oauth/v2/accessToken',
|
|
jasmine.any(Object)
|
|
);
|
|
expect(global.fetch).toHaveBeenCalledWith(
|
|
'https://api.linkedin.com/v2/me',
|
|
jasmine.any(Object)
|
|
);
|
|
});
|
|
|
|
it('secure does not support insecure payload if not enabled', async function () {
|
|
mockFetch();
|
|
const authData = { id: 'mockUserId', access_token: 'mockAccessToken123' };
|
|
await expectAsync(Parse.User.logInWith('linkedin', { authData })).toBeRejectedWithError(
|
|
'LinkedIn code is required.'
|
|
);
|
|
|
|
expect(global.fetch).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('insecure mode supports insecure payload if enabled', async function () {
|
|
await reconfigureServer({
|
|
auth: {
|
|
linkedin: {
|
|
clientId: 'validClientId',
|
|
clientSecret: 'validClientSecret',
|
|
enableInsecureAuth: true,
|
|
},
|
|
},
|
|
});
|
|
|
|
mockFetch([
|
|
{
|
|
url: 'https://api.linkedin.com/v2/me',
|
|
method: 'GET',
|
|
response: {
|
|
ok: true,
|
|
json: () =>
|
|
Promise.resolve({
|
|
id: 'mockUserId',
|
|
}),
|
|
},
|
|
},
|
|
]);
|
|
|
|
const authData = { id: 'mockUserId', access_token: 'mockAccessToken123' };
|
|
const user = await Parse.User.logInWith('linkedin', { authData });
|
|
|
|
expect(user.id).toBeDefined();
|
|
expect(global.fetch).toHaveBeenCalledWith(
|
|
'https://api.linkedin.com/v2/me',
|
|
jasmine.any(Object)
|
|
);
|
|
});
|
|
});
|
|
});
|