UNPKG

@ringcentral/sdk

Version:

- [Installation](#installation) - [Getting Started](#getting-started) - [API Calls](#api-calls) - [Advanced SDK Configuration & Polyfills](#advanced-sdk-configuration--polyfills) - [Making telephony calls](#making-telephony-calls) - [Call mana

1,302 lines (1,108 loc) 67.7 kB
import {version} from '../core/Constants'; import { apiCall, asyncTest, authentication, cleanFetchMock, createSdk, expect, expectThrows, getExternalDiscoveryMockData, getInitialDiscoveryMockData, logout, spy, tokenRefresh, } from '../test/test'; const globalAny: any = global; const windowAny: any = typeof window !== 'undefined' ? window : global; describe('RingCentral.platform.Platform', () => { describe('isTokenValid', () => { it( 'is not authenticated when token has expired', asyncTest(async sdk => { const platform = sdk.platform(); await platform.auth().cancelAccessToken(); expect(await platform.auth().accessTokenValid()).toEqual(false); }), ); it( 'is not authenticated after logout', asyncTest(async sdk => { logout(); const platform = sdk.platform(); await platform.logout(); expect(await platform.auth().accessTokenValid()).toEqual(false); }), ); }); describe('X-User-Agent', () => { it( 'is added with default value', asyncTest(async sdk => { const platform = sdk.platform(); const client = sdk.client(); const path = `/restapi/v1.0/foo/get`; apiCall('get', path, {foo: 'bar'}); let request; client.on(client.events.requestSuccess, (_, r) => { request = r; }); await platform.get(path, null); expect(request.headers.get('x-user-agent')).toEqual(`RCJSSDK/${version}`); expect(platform.userAgent).toEqual(`RCJSSDK/${version}`); }), ); it( 'is added with app name and version', asyncTest( async sdk => { const platform = sdk.platform(); const client = sdk.client(); const path = `/restapi/v1.0/foo/get`; apiCall('get', path, {foo: 'bar'}); let request; client.on(client.events.requestSuccess, (_, r) => { request = r; }); await platform.get(path, null); expect(request.headers.get('x-user-agent')).toContain('TestApp/1.0.0 '); }, { appName: 'TestApp', appVersion: '1.0.0', }, ), ); it( 'is added with app name', asyncTest( async sdk => { const platform = sdk.platform(); const client = sdk.client(); const path = `/restapi/v1.0/foo/get`; apiCall('get', path, {foo: 'bar'}); let request; client.on(client.events.requestSuccess, (_, r) => { request = r; }); await platform.get(path, null); expect(request.headers.get('x-user-agent')).toContain('TestApp '); }, { appName: 'TestApp', }, ), ); it( 'is added with additional user agent', asyncTest( async sdk => { const platform = sdk.platform(); const client = sdk.client(); const path = `/restapi/v1.0/foo/get`; apiCall('get', path, {foo: 'bar'}); let request; client.on(client.events.requestSuccess, (_, r) => { request = r; }); await platform.get(path, null); expect(request.headers.get('x-user-agent')).toContain(' (build.1000; rev.149f00000)'); }, { additionalUserAgent: '(build.1000; rev.149f00000)', }, ), ); it( 'is added with app name, version and additional user agent', asyncTest( async sdk => { const platform = sdk.platform(); const client = sdk.client(); const path = `/restapi/v1.0/foo/get`; apiCall('get', path, {foo: 'bar'}); let request; client.on(client.events.requestSuccess, (_, r) => { request = r; }); await platform.get(path, null); expect(request.headers.get('x-user-agent')).toContain('TestApp/1.0.0 '); expect(request.headers.get('x-user-agent')).toContain(' (build.1000; rev.149f00000)'); }, { appName: 'TestApp', appVersion: '1.0.0', additionalUserAgent: '(build.1000; rev.149f00000)', }, ), ); }); describe('authorized', () => { it( 'initiates refresh if not authorized', asyncTest(async sdk => { tokenRefresh(); const platform = sdk.platform(); expect((await platform.auth().data()).access_token).not.toEqual('ACCESS_TOKEN_FROM_REFRESH'); await platform.auth().cancelAccessToken(); await platform.loggedIn(); expect((await platform.auth().data()).access_token).toEqual('ACCESS_TOKEN_FROM_REFRESH'); }), ); }); describe('login', () => { it( 'login with code', asyncTest(async sdk => { const platform = sdk.platform(); await platform.auth().cancelAccessToken(); authentication(); await platform.login({ code: 'foo', access_token_ttl: 100, refresh_token_ttl: 100, }); expect((await platform.auth().data()).access_token).toEqual('ACCESS_TOKEN'); expect(await platform.auth().accessTokenValid()).toEqual(true); expect(await platform.auth().refreshTokenValid()).toEqual(true); expect((await platform.auth().data()).expire_time > Date.now() + 30 * 60 * 1000).toEqual(true); expect( (await platform.auth().data()).refresh_token_expire_time > Date.now() + 6 * 24 * 60 * 60 * 1000, ).toEqual(true); }), ); it( 'login with JWT', asyncTest(async sdk => { const platform = sdk.platform(); await platform.auth().cancelAccessToken(); authentication(); await platform.login({ jwt: 'foo', }); expect((await platform.auth().data()).access_token).toEqual('ACCESS_TOKEN'); expect(await platform.auth().accessTokenValid()).toEqual(true); }), ); it( 'login with username/password', asyncTest(async sdk => { const platform = sdk.platform(); await platform.auth().cancelAccessToken(); authentication(); await platform.login({ username: 'foo', password: 'foo', extension: 'foo', }); expect((await platform.auth().data()).access_token).toEqual('ACCESS_TOKEN'); expect(await platform.auth().accessTokenValid()).toEqual(true); }), ); it( 'login with username/password without extension', asyncTest(async sdk => { const platform = sdk.platform(); await platform.auth().cancelAccessToken(); authentication(); await platform.login({ username: 'foo', password: 'foo', }); expect((await platform.auth().data()).access_token).toEqual('ACCESS_TOKEN'); expect(await platform.auth().accessTokenValid()).toEqual(true); }), ); it( 'login with code from usePKCE flow without client secret', asyncTest( async sdk => { const platform = sdk.platform(); await platform.auth().cancelAccessToken(); platform.loginUrl({usePKCE: true}); authentication(); await platform.login({ code: 'foo', access_token_ttl: 100, refresh_token_ttl: 100, }); const authData = await platform.auth().data(); expect(authData.access_token).toEqual('ACCESS_TOKEN'); expect(await platform.auth().accessTokenValid()).toEqual(true); expect(authData.code_verifier.length > 0).toEqual(true); }, { clientSecret: '', }, ), ); it( 'login with code and code_verifier', asyncTest( async sdk => { authentication(); const platform = sdk.platform(); await platform.login({code: 'test', code_verifier: 'test_code_verifier'}); const authData = await platform.auth().data(); expect(authData.code_verifier).toEqual('test_code_verifier'); expect(await platform.auth().accessTokenValid()).toEqual(true); }, { clientSecret: '', }, ), ); it( 'login with code and code_verifier with client secret', asyncTest(async sdk => { authentication(); const platform = sdk.platform(); const client = sdk.client(); let request; client.on(client.events.requestSuccess, (_, r) => { request = r; }); await platform.login({code: 'test', code_verifier: 'test_code_verifier'}); expect(request.headers.get('authorization')).not.toEqual(null); const authData = await platform.auth().data(); expect(authData.access_token).toEqual('ACCESS_TOKEN'); expect(authData.code_verifier).toEqual('test_code_verifier'); expect(await platform.auth().accessTokenValid()).toEqual(true); }), ); it( 'login with code without clientSecret', asyncTest( async sdk => { authentication(); const platform = sdk.platform(); const client = sdk.client(); let request; client.on(client.events.requestSuccess, (_, r) => { request = r; }); await platform.login({code: 'test'}); expect(request.headers.get('authorization')).toEqual(null); expect(request.originalBody || request.body).toContain('client_id=whatever'); const authData = await platform.auth().data(); expect(authData.access_token).toEqual('ACCESS_TOKEN'); }, { clientSecret: '', }, ), ); it( 'login with access_token', asyncTest(async sdk => { const platform = sdk.platform(); await platform.auth().cancelAccessToken(); authentication(); await platform.login({access_token: 'foo'}); expect((await platform.auth().data()).access_token).toEqual('foo'); }), ); it( 'login error', asyncTest(async sdk => { const platform = sdk.platform(); await platform.auth().cancelAccessToken(); apiCall('POST', '/restapi/oauth/token', {message: 'expected'}, 400); await expectThrows(async () => platform.login({code: 'foo'}), 'expected'); }), ); }); describe('loggedIn', () => { it( 'returns false if refresh failed', asyncTest(async sdk => { const platform = sdk.platform(); await platform.auth().cancelAccessToken(); apiCall('POST', '/restapi/oauth/token', {message: 'expected'}, 400); const res = await platform.loggedIn(); expect(res).toEqual(false); }), ); }); describe('sendRequest', () => { it( 'refreshes token when token was expired', asyncTest(async sdk => { const platform = sdk.platform(); const path = '/restapi/xxx'; const refreshSpy = spy(() => {}); tokenRefresh(); apiCall('GET', path, {}); expect((await platform.auth().data()).access_token).not.toEqual('ACCESS_TOKEN_FROM_REFRESH'); await platform.auth().cancelAccessToken(); await platform.on(platform.events.refreshSuccess, refreshSpy).get(path); expect(refreshSpy.mock.calls.length === 1).toEqual(true); expect((await platform.auth().data()).access_token).toEqual('ACCESS_TOKEN_FROM_REFRESH'); }), ); it( 'tries to refresh the token if Platform returns 401 Unauthorized and re-executes the request', asyncTest(async sdk => { const platform = sdk.platform(); const path = '/restapi/xxx'; const response = {foo: 'bar'}; const refreshSpy = spy(() => { apiCall('GET', path, response, 200); }); apiCall('GET', path, {message: 'time not in sync'}, 401, 'Time Not In Sync'); tokenRefresh(); platform.on(platform.events.refreshSuccess, refreshSpy); const res = await platform.get(path); expect(refreshSpy.mock.calls.length === 1).toEqual(true); expect(await res.json()).toEqual(response); expect((await platform.auth().data()).access_token).toEqual('ACCESS_TOKEN_FROM_REFRESH'); }), ); it( 'fails if ajax has status other than 2xx', asyncTest(async sdk => { const platform = sdk.platform(); const path = '/restapi/xxx'; apiCall('GET', path, {description: 'Fail'}, 400, 'Bad Request'); await expectThrows(async () => platform.get(path), 'Fail'); }), ); it( 'handles rate limit 429', asyncTest(async sdk => { const platform = sdk.platform(); const path = '/restapi/xxx'; const response = {foo: 'bar'}; const rateLimitSpy = spy(() => { apiCall('GET', path, response, 200); }); apiCall('GET', path, {message: 'expected'}, 429, 'Rate Limit Exceeded'); platform.on(platform.events.rateLimitError, rateLimitSpy); const res = await platform.get(path, null, {handleRateLimit: 0.01}); expect(rateLimitSpy.mock.calls.length === 1).toEqual(true); const e = rateLimitSpy.mock.calls[0][0]; expect(e.message).toEqual('expected'); expect(e.retryAfter).toEqual(10); expect(await res.json()).toEqual(response); }), ); it( 'handles default rate limit 429', asyncTest(async sdk => { const platform = sdk.platform(); const path = '/restapi/xxx'; const response = {foo: 'bar'}; const rateLimitSpy = spy(() => { apiCall('GET', path, response, 200); }); platform['_handleRateLimit'] = 0.01; apiCall('GET', path, {message: 'expected'}, 429, 'Rate Limit Exceeded'); platform.on(platform.events.rateLimitError, rateLimitSpy); const res = await platform.get(path); expect(rateLimitSpy.mock.calls.length === 1).toEqual(true); const e = rateLimitSpy.mock.calls[0][0]; expect(e.message).toEqual('expected'); expect(e.retryAfter).toEqual(10); expect(await res.json()).toEqual(response); }), ); it( 'emits rate limit 429 errors if they are not handled', asyncTest(async sdk => { const platform = sdk.platform(); const path = '/restapi/xxx'; const rateLimitSpy = spy(() => {}); apiCall('GET', path, {message: 'expected'}, 429, 'Rate Limit Exceeded'); platform.on(platform.events.rateLimitError, rateLimitSpy); await expectThrows(async () => platform.get(path), '', err => { expect(rateLimitSpy.mock.calls.length === 1).toEqual(true); const e = rateLimitSpy.mock.calls[0][0]; expect(e.message).toEqual('expected'); expect(e.retryAfter).toEqual(60000); expect(err).toEqual(e); }); }), ); }); describe('refresh', () => { it( 'handles error in queued AJAX after unsuccessful refresh when token is killed', asyncTest(async sdk => { const platform = sdk.platform(); const path = '/restapi/xxx'; const successSpy = spy(() => {}); const errorSpy = spy(() => {}); tokenRefresh(true); await platform.auth().cancelAccessToken(); await expectThrows( async () => platform .on(platform.events.refreshSuccess, successSpy) .on(platform.events.refreshError, errorSpy) .get(path), 'Wrong token', ); expect(errorSpy.mock.calls.length === 1).toEqual(true); expect(successSpy.mock.calls.length === 1).toBe(false); }), ); it( 'handles subsequent refreshes', asyncTest(async sdk => { const platform = sdk.platform(); tokenRefresh(); tokenRefresh(); tokenRefresh(); await platform.refresh(); // first await platform.refresh(); // second await Promise.all([ platform.refresh(), // third combined for two platform.refresh(), ]); }), ); it( 'returns error if response is malformed', asyncTest(async sdk => { const platform = sdk.platform(); apiCall( 'POST', '/restapi/oauth/token', { message: 'Wrong token', error_description: 'Wrong token', description: 'Wrong token', }, 240, ); // This weird status was caught on client's machine await platform.auth().cancelAccessToken(); await expectThrows(async () => platform.refresh(), 'Wrong token', (e: any) => { expect(e.originalMessage).toEqual('Malformed OAuth response'); }); }), ); it( 'issues only one refresh request', asyncTest(async sdk => { tokenRefresh(); apiCall('GET', '/restapi/v1.0/foo/1', {increment: 1}); apiCall('GET', '/restapi/v1.0/foo/2', {increment: 2}); apiCall('GET', '/restapi/v1.0/foo/3', {increment: 3}); const platform = sdk.platform(); await platform.auth().cancelAccessToken(); const res = await Promise.all( (await Promise.all([ platform.get('/restapi/v1.0/foo/1'), platform.get('/restapi/v1.0/foo/2'), platform.get('/restapi/v1.0/foo/3'), ])).map(r => r.json()), ); expect((await platform.auth().data()).access_token).toEqual('ACCESS_TOKEN_FROM_REFRESH'); expect(res[0].increment).toEqual(1); expect(res[1].increment).toEqual(2); expect(res[2].increment).toEqual(3); }), ); it( 'not skip auth header when auth data with clientSecret', asyncTest(async sdk => { tokenRefresh(); const platform = sdk.platform(); const client = sdk.client(); await platform.auth().cancelAccessToken(); let request; client.on(client.events.requestSuccess, (_, r) => { request = r; }); await platform.refresh(); expect(request.headers.get('authorization')).not.toEqual(null); expect((await platform.auth().data()).access_token).toEqual('ACCESS_TOKEN_FROM_REFRESH'); expect(await platform.auth().accessTokenValid()).toEqual(true); expect(await platform.auth().refreshTokenValid()).toEqual(true); }), ); it( 'skip auth header when auth data without client secret', asyncTest( async sdk => { tokenRefresh(); const platform = sdk.platform(); const client = sdk.client(); await platform.auth().cancelAccessToken(); let request; client.on(client.events.requestSuccess, (_, r) => { request = r; }); await platform.refresh(); expect(request.headers.get('authorization')).toEqual(null); expect(request.originalBody || request.body).toContain('client_id=whatever'); const authData = await platform.auth().data(); expect(authData.access_token).toEqual('ACCESS_TOKEN_FROM_REFRESH'); }, { clientSecret: '', }, ), ); }); describe('get, post, put, patch, delete', () => { it( 'sends request using appropriate method', asyncTest(async sdk => { const platform = sdk.platform(); const test = async method => { const path = `/restapi/v1.0/foo/${method}`; apiCall(method, path, {foo: 'bar'}); const res = await platform[method](path); expect((await res.json()).foo).toEqual('bar'); }; await test('get'); await test('post'); await test('put'); await test('patch'); await test('delete'); }), ); it( 'send request with user agent option', asyncTest(async sdk => { const platform = sdk.platform(); const client = sdk.client(); const path = `/restapi/v1.0/foo/get`; apiCall('get', path, {foo: 'bar'}); let request; client.on(client.events.requestSuccess, (_, r) => { request = r; }); await platform.get(path, null, {userAgent: 'TestAgent'}); expect(request.headers.get('x-user-agent')).toContain('TestAgent'); }), ); }); describe('createUrl', () => { it( 'builds the URL', asyncTest(async sdk => { const platform = sdk.platform(); expect(platform.createUrl('/restapi/v1.0/foo')).toEqual('/restapi/v1.0/foo'); expect(platform.createUrl('/restapi/v1.0/foo', {addServer: true})).toEqual( 'http://whatever/restapi/v1.0/foo', ); expect( await platform.signUrl( platform.createUrl('/restapi/v1.0/foo', { addServer: true, }), ), ).toEqual('http://whatever/restapi/v1.0/foo?access_token=ACCESS_TOKEN'); expect( await platform.signUrl( platform.createUrl('/restapi/v1.0/foo?bar', { addServer: true, }), ), ).toEqual('http://whatever/restapi/v1.0/foo?bar&access_token=ACCESS_TOKEN'); expect( await platform.signUrl( platform.createUrl('/restapi/v1.0/foo?bar', { addServer: true, addMethod: 'POST', }), ), ).toEqual('http://whatever/restapi/v1.0/foo?bar&_method=POST&access_token=ACCESS_TOKEN'); expect( await platform.signUrl( platform.createUrl('/rcvideo/v1/foo?bar', { addServer: true, }), ), ).toEqual('http://whatever/rcvideo/v1/foo?bar&access_token=ACCESS_TOKEN'); }), ); }); describe('parseLoginRedirect', () => { describe('Authorization Code Flow', () => { it( 'parses url correctly', asyncTest(async sdk => { const platform = sdk.platform(); expect(platform.parseLoginRedirect('?code=foo')).toEqual({code: 'foo'}); }), ); }); describe('Implicit Grant Flow', () => { it( 'parses url correctly', asyncTest(async sdk => { const platform = sdk.platform(); expect(platform.parseLoginRedirect('#access_token=foo')).toEqual({access_token: 'foo'}); }), ); }); }); describe('loginUrl', () => { it( 'simple usage', asyncTest(async sdk => { const platform = sdk.platform(); expect( platform.loginUrl({ implicit: true, state: 'foo', brandId: 'foo', display: 'foo', prompt: 'foo', }), ).toEqual( 'http://whatever/restapi/oauth/authorize?response_type=token&redirect_uri=http%3A%2F%2Ffoo&client_id=whatever&state=foo&brand_id=foo&display=foo&prompt=foo&ui_options=&ui_locales=&localeId=', ); expect( platform.loginUrl({ implicit: false, state: 'foo', brandId: 'foo', display: 'foo', prompt: 'foo', }), ).toEqual( 'http://whatever/restapi/oauth/authorize?response_type=code&redirect_uri=http%3A%2F%2Ffoo&client_id=whatever&state=foo&brand_id=foo&display=foo&prompt=foo&ui_options=&ui_locales=&localeId=', ); expect( platform.loginUrl({ implicit: false, }), ).toEqual( 'http://whatever/restapi/oauth/authorize?response_type=code&redirect_uri=http%3A%2F%2Ffoo&client_id=whatever&state=&brand_id=&display=&prompt=&ui_options=&ui_locales=&localeId=', ); expect( platform.loginUrl({ usePKCE: true, }), ).toContain('code_challenge'); expect( platform.loginUrl.bind(platform, { implicit: true, usePKCE: true, }), ).toThrow('PKCE only works with Authorization Code Flow'); expect( platform.loginUrl({ implicit: false, uiOptions: ['foo', 'bar'], responseHint: ['baz', 'quux'], }), ).toEqual( 'http://whatever/restapi/oauth/authorize?response_type=code&redirect_uri=http%3A%2F%2Ffoo&client_id=whatever&state=&brand_id=&display=&prompt=&ui_options=foo&ui_options=bar&ui_locales=&localeId=&response_hint=baz&response_hint=quux', ); expect( platform.loginUrl({ implicit: false, uiOptions: 'foo', responseHint: 'bar', }), ).toEqual( 'http://whatever/restapi/oauth/authorize?response_type=code&redirect_uri=http%3A%2F%2Ffoo&client_id=whatever&state=&brand_id=&display=&prompt=&ui_options=foo&ui_locales=&localeId=&response_hint=bar', ); /** * Test with loginHin parameter in loginUrl method */ expect( platform.loginUrl({ implicit: false, uiOptions: ['foo', 'bar'], responseHint: ['baz', 'quux'], loginHint: ['rc@xyz.com','rc1@xyz.com'], }), ).toEqual( 'http://whatever/restapi/oauth/authorize?response_type=code&redirect_uri=http%3A%2F%2Ffoo&client_id=whatever&state=&brand_id=&display=&prompt=&ui_options=foo&ui_options=bar&ui_locales=&localeId=&response_hint=baz&response_hint=quux&login_hint=rc%40xyz.com&login_hint=rc1%40xyz.com', ); /** * Test with loginHin parameter in loginUrl method */ expect( platform.loginUrl({ implicit: false, uiOptions: 'foo', responseHint: 'bar', loginHint: 'rc@xyz.com', }), ).toEqual( 'http://whatever/restapi/oauth/authorize?response_type=code&redirect_uri=http%3A%2F%2Ffoo&client_id=whatever&state=&brand_id=&display=&prompt=&ui_options=foo&ui_locales=&localeId=&response_hint=bar&login_hint=rc%40xyz.com', ); }), ); }); describe('loginWindow', () => { const isNode = typeof window !== 'undefined'; if (!isNode) { globalAny.window = { screenLeft: 0, screenTop: 0, location: { origin: '', }, }; globalAny.screen = { left: 0, top: 0, width: 0, height: 0, }; globalAny.document = { documentElement: { clientWidth: 0, clientHeight: 0, }, }; } window.addEventListener = (eventName, cb, bubble) => { windowAny.triggerEvent = mock => { cb(mock); }; }; it( 'simple usage', asyncTest(async sdk => { const platform = sdk.platform(); const close = spy(); const focus = spy(); const openSpy = spy(() => ({ close, focus, })); window.open = openSpy; window.removeEventListener = spy(); setTimeout(() => { windowAny.triggerEvent({origin: 'bar'}); windowAny.triggerEvent({origin: 'foo', data: {foo: 'bar'}}); windowAny.triggerEvent({origin: 'foo', data: {RCAuthorizationResponse: '#access_token=foo'}}); }, 10); const res = await platform.loginWindow({ url: 'foo', origin: 'foo', }); expect(res.access_token).toEqual('foo'); expect(close.mock.calls.length === 1).toEqual(true); expect(focus.mock.calls.length === 1).toEqual(true); expect(openSpy.mock.calls.length === 1).toEqual(true); }), ); it( 'throws an exception if no code and token', asyncTest(async sdk => { const platform = sdk.platform(); const openSpy = spy(() => ({close: spy()})); window.open = openSpy; setTimeout(() => { windowAny.triggerEvent({origin: 'foo', data: {RCAuthorizationResponse: '#bar=foo'}}); }, 10); await expectThrows(async () => { await platform.loginWindow({ url: 'foo', origin: 'foo', }); }, 'No authorization code or token'); expect(openSpy.mock.calls.length === 1).toEqual(true); }), ); it( 'throws an exception if window cannot be open', asyncTest(async sdk => { const platform = sdk.platform(); const openSpy = spy(() => null); window.open = openSpy; await expectThrows(async () => { await platform.loginWindow({ url: 'foo', origin: 'foo', }); }, 'Could not open login window. Please allow popups for this site'); expect(openSpy.mock.calls.length === 1).toEqual(true); }), ); it( 'throws an exception if window is closed', asyncTest(async sdk => { const platform = sdk.platform(); const openWindow = {closed: false}; const openSpy = spy(() => openWindow); window.open = openSpy; setTimeout(() => { openWindow.closed = true; }, 3000); await expectThrows(async () => { await platform.loginWindow({ url: 'foo', origin: 'foo', }); }, 'Login window is closed'); expect(openSpy.mock.calls.length === 1).toEqual(true); }), ); it( 'login success when call loginWindow twice', asyncTest(async sdk => { const platform = sdk.platform(); const close = spy(); const focus = spy(); const openSpy = spy(() => ({ close, focus, })); window.open = openSpy; window.removeEventListener = spy(); setTimeout(() => { windowAny.triggerEvent({origin: 'foo', data: {RCAuthorizationResponse: '#access_token=foo'}}); }, 1000); platform.loginWindow({ url: 'foo', origin: 'foo', }); const res = await platform.loginWindow({ url: 'foo', origin: 'foo', }); expect(res.access_token).toEqual('foo'); }), ); }); describe('parseLoginRedirect', () => { it( 'parses redirect URIs with hash', asyncTest(async sdk => { const platform = sdk.platform(); expect(platform.parseLoginRedirect('#access_token=foo').access_token).toEqual('foo'); }), ); it( 'parses redirect URIs with query', asyncTest(async sdk => { const platform = sdk.platform(); expect(platform.parseLoginRedirect('?access_token=foo').access_token).toEqual('foo'); }), ); it( 'parses redirect URIs with errors', asyncTest(async sdk => { const platform = sdk.platform(); expect(() => { platform.parseLoginRedirect('?error_description=foo'); }).toThrow('foo'); expect(() => { platform.parseLoginRedirect('?error=foo'); }).toThrow('foo'); expect(() => { platform.parseLoginRedirect('xxx'); }).toThrow('Unable to parse response'); }), ); }); describe('logout', () => { it( 'should skip auth header when auth without client secret', asyncTest( async sdk => { logout(); const platform = sdk.platform(); const client = sdk.client(); await platform.auth().setData({ code_verifier: '1212121', }); expect(await platform.auth().accessTokenValid()).toEqual(true); expect(await platform.auth().refreshTokenValid()).toEqual(true); let request; client.on(client.events.requestSuccess, (_, r) => { request = r; }); await platform.logout(); expect(request.headers.get('authorization')).toEqual(null); expect(request.originalBody || request.body).toContain('client_id=whatever'); expect(await platform.auth().accessTokenValid()).toEqual(false); }, {clientSecret: ''}, ), ); }); describe('discovery initial', () => { let sdk; beforeEach(() => { cleanFetchMock(); }); afterEach(async () => { await sdk.cache().clean(); }); it('should fetch initial discovery and set auth endpoint on init', async () => { const initialDiscoveryData = getInitialDiscoveryMockData(); apiCall('GET', '/.well-known/entry-points/initial?clientId=whatever', initialDiscoveryData); sdk = createSdk({enableDiscovery: true, discoveryServer: 'http://whatever', server: ''}); const platform = sdk.platform(); if (platform.discoveryInitPromise) { await platform.discoveryInitPromise; } const loginUrl = platform.loginUrl(); expect(loginUrl.indexOf(initialDiscoveryData.authApi.authorizationUri)).toEqual(0); expect(loginUrl.indexOf('discovery=true') > -1).toEqual(true); expect((await platform.discovery().initialData()).discoveryApi.defaultExternalUri).toEqual( initialDiscoveryData.discoveryApi.defaultExternalUri, ); }); it('should throw error when client id is blank', async () => { const initialDiscoveryData = getInitialDiscoveryMockData(); apiCall('GET', '/.well-known/entry-points/initial?clientId=whatever', initialDiscoveryData); sdk = createSdk({ enableDiscovery: true, discoveryServer: 'http://whatever', discoveryAutoInit: false, server: '', clientId: '', }); const platform = sdk.platform(); let error; try { await platform.initDiscovery(); } catch (e) { error = e; } expect(error.message).toEqual('Client Id is required for discovery'); }); it('should emit initialFetchError error after 3 retry', async function() { apiCall('GET', '/.well-known/entry-points/initial?clientId=whatever', {description: 'Fail'}, 500); apiCall('GET', '/.well-known/entry-points/initial?clientId=whatever', {description: 'Fail'}, 500); apiCall('GET', '/.well-known/entry-points/initial?clientId=whatever', {description: 'Fail'}, 500); sdk = createSdk({enableDiscovery: true, discoveryServer: 'http://whatever', server: ''}); const platform = sdk.platform(); const requestErrorSpy = spy(() => {}); sdk.client().on(sdk.client().events.requestError, requestErrorSpy); if (platform.discoveryInitPromise) { try { await platform.discoveryInitPromise; } catch (e) { // ignore } } const discovery = platform.discovery(); expect(discovery.initialized).toEqual(false); expect(requestErrorSpy.mock.calls.length === 3).toEqual(true); let loginUrlError = false; try { platform.loginUrl(); } catch (e) { loginUrlError = true; } expect(loginUrlError).toEqual(true); }); it('should fetch initial discovery on loginUrlWithDiscovery', async () => { const initialDiscoveryData = getInitialDiscoveryMockData(); apiCall('GET', '/.well-known/entry-points/initial?clientId=whatever', initialDiscoveryData); sdk = createSdk({enableDiscovery: true, discoveryServer: 'http://whatever', server: ''}); const platform = sdk.platform(); expect( (await platform.loginUrlWithDiscovery()).indexOf(initialDiscoveryData.authApi.authorizationUri), ).toEqual(0); }); it('should not throw error when fetch initial discovery error with cache data on loginUrlWithDiscovery', async function() { const initialDiscoveryData = getInitialDiscoveryMockData(); apiCall('GET', '/.well-known/entry-points/initial?clientId=whatever', initialDiscoveryData); sdk = createSdk({enableDiscovery: true, discoveryServer: 'http://whatever', server: ''}); const platform = sdk.platform(); if (platform.discoveryInitPromise) { await platform.discoveryInitPromise; } apiCall('GET', '/.well-known/entry-points/initial?clientId=whatever', {description: 'Fail'}, 500); apiCall('GET', '/.well-known/entry-points/initial?clientId=whatever', {description: 'Fail'}, 500); apiCall('GET', '/.well-known/entry-points/initial?clientId=whatever', {description: 'Fail'}, 500); expect( (await platform.loginUrlWithDiscovery()).indexOf(initialDiscoveryData.authApi.authorizationUri), ).toEqual(0); }); it('should throw error when fetch initial discovery error without cache data on loginUrlWithDiscovery', async function() { const initialDiscoveryData = getInitialDiscoveryMockData(); apiCall('GET', '/.well-known/entry-points/initial?clientId=whatever', initialDiscoveryData); sdk = createSdk({enableDiscovery: true, discoveryServer: 'http://whatever', server: ''}); const platform = sdk.platform(); if (platform.discoveryInitPromise) { await platform.discoveryInitPromise; } await platform.discovery().removeInitialData(); apiCall('GET', '/.well-known/entry-points/initial?clientId=whatever', {description: 'Fail'}, 500); apiCall('GET', '/.well-known/entry-points/initial?clientId=whatever', {description: 'Fail'}, 500); apiCall('GET', '/.well-known/entry-points/initial?clientId=whatever', {description: 'Fail'}, 500); let error; try { await platform.loginUrlWithDiscovery(); } catch (e) { error = e; } expect(!!error).toEqual(true); }); it('should fetch external discovery when login with discovery_uri and token_uri', async () => { // mock const initialDiscoveryData = getInitialDiscoveryMockData(); const externalDiscoveryData = getExternalDiscoveryMockData(); apiCall('GET', '/.well-known/entry-points/initial?clientId=whatever', initialDiscoveryData); apiCall('GET', '/.well-known/entry-points/external', externalDiscoveryData); authentication(); sdk = createSdk({enableDiscovery: true, discoveryServer: 'http://whatever', server: ''}); const platform = sdk.platform(); await platform.login({ code: 'whatever', discovery_uri: 'http://whatever/.well-known/entry-points/external', token_uri: 'http://whatever/restapi/oauth/token', }); const externalData = await platform.discovery().externalData(); expect(externalData.coreApi.baseUri).toEqual(externalDiscoveryData.coreApi.baseUri); expect(await platform.discovery().externalDataExpired()).toEqual(false); }); it('should fetch external discovery when login with discovery_uri and token_uri when discoveryInitPromise finished', async () => { // mock const initialDiscoveryData = getInitialDiscoveryMockData(); const externalDiscoveryData = getExternalDiscoveryMockData(); apiCall('GET', '/.well-known/entry-points/initial?clientId=whatever', initialDiscoveryData); apiCall('GET', '/.well-known/entry-points/external', externalDiscoveryData); authentication(); sdk = createSdk({enableDiscovery: true, discoveryServer: 'http://whatever', server: ''}); const platform = sdk.platform(); await platform.login({ code: 'whatever', discovery_uri: 'http://whatever/.well-known/entry-points/external', token_uri: 'http://whatever/restapi/oauth/token', }); const externalData = await platform.discovery().externalData(); expect(externalData.coreApi.baseUri).toEqual(externalDiscoveryData.coreApi.baseUri); expect(await platform.discovery().externalDataExpired()).toEqual(false); }); it('should fetch external discovery successfully when login without initDiscovery data', async () => { // mock const initialDiscoveryData = getInitialDiscoveryMockData(); const externalDiscoveryData = getExternalDiscoveryMockData(); apiCall('GET', '/.well-known/entry-points/initial?clientId=whatever', initialDiscoveryData); apiCall('GET', '/.well-known/entry-points/external', externalDiscoveryData); authentication(); sdk = createSdk({ enableDiscovery: true, discoveryServer: 'http://whatever', discoveryAutoInit: false, server: '', }); const platform = sdk.platform(); await platform.login({ code: 'whatever', }); const externalData = await platform.discovery().externalData(); expect(externalData.coreApi.baseUri).toEqual(externalDiscoveryData.coreApi.baseUri); expect(await platform.discovery().externalDataExpired()).toEqual(false); }); it('should fetch external discovery when login without discovery_uri and token_uri', async () => { // mock const initialDiscoveryData = getInitialDiscoveryMockData(); const externalDiscoveryData = getExternalDiscoveryMockData(); apiCall('GET', '/.well-known/entry-points/initial?clientId=whatever', initialDiscoveryData); apiCall('GET', '/.well-known/entry-points/external', externalDiscoveryData); authentication(); sdk = createSdk({enableDiscovery: true, discoveryServer: 'http://whatever', server: ''}); const platform = sdk.platform(); await platform.login({code: 'whatever'}); const externalData = await platform.discovery().externalData(); expect(externalData.coreApi.baseUri).toEqual(externalDiscoveryData.coreApi.baseUri); expect(await platform.discovery().externalDataExpired()).toEqual(false); });