Skip to content

Commit 268b3e0

Browse files
committed
Simplify popup calling code.
1 parent c393f40 commit 268b3e0

4 files changed

Lines changed: 84 additions & 169 deletions

File tree

src/__test__/popup.spec.js

Lines changed: 43 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,56 @@
11
// @flow
22
/* eslint-env jest */
3-
import {
4-
appOriginHandler,
5-
loginHandler,
6-
storageHandler,
7-
startPopupServer
8-
} from '../popup'
3+
import { obtainSession, popupHandler } from '../popup'
94
import { polyfillWindow, polyunfillWindow } from './spec-helpers'
105
import { defaultStorage } from '../storage'
116

127
beforeEach(polyfillWindow)
138

149
afterEach(polyunfillWindow)
1510

16-
describe('storageHandler', () => {
11+
describe('obtainSession', () => {
12+
it('resolves to the captured session once it captures the "foundSession" method', async () => {
13+
expect.assertions(1)
14+
const store = defaultStorage()
15+
const session = {
16+
idp: 'https://localhost',
17+
webId: 'https://localhost/profile#me'
18+
}
19+
const sessionPromise = obtainSession(store, window, {
20+
popupUri: 'https://app.biz/select-idp',
21+
callbackUri: 'https://app.biz/callback',
22+
storage: store
23+
})
24+
window.postMessage(
25+
{
26+
'solid-auth-client': {
27+
id: '12345',
28+
method: 'foundSession',
29+
args: [session]
30+
}
31+
},
32+
window.location.origin
33+
)
34+
const resolvedSession = await sessionPromise
35+
expect(resolvedSession).toEqual(session)
36+
})
37+
})
38+
39+
describe('popupHandler', () => {
1740
let store
1841
let handler
42+
const mockCallback = jest.fn()
43+
44+
const options = {
45+
popupUri: 'https://localhost/select-idp',
46+
callbackUri: 'https://localhost/callback',
47+
storage: defaultStorage()
48+
}
1949

2050
beforeEach(() => {
2151
store = defaultStorage()
22-
handler = storageHandler(store)
52+
handler = popupHandler(store, options, mockCallback)
53+
mockCallback.mockReset()
2354
})
2455

2556
it('implements getItem', async () => {
@@ -44,23 +75,8 @@ describe('storageHandler', () => {
4475
expect(await store.getItem('foo')).toEqual(null)
4576
})
4677

47-
it('ignores unknown methods', async () => {
48-
expect.assertions(1)
49-
const resp = await handler('unknown_method', 'a', 'b', 'c')
50-
expect(resp).toBeNull()
51-
})
52-
})
53-
54-
describe('loginHandler', () => {
55-
const options = {
56-
popupUri: 'https://localhost/select-idp',
57-
callbackUri: 'https://localhost/callback',
58-
storage: defaultStorage()
59-
}
60-
6178
it('returns the loginOptions', async () => {
6279
expect.assertions(1)
63-
const handler = loginHandler(options, () => {})
6480
const _options = await handler('getLoginOptions')
6581
expect(_options).toEqual({
6682
popupUri: options.popupUri,
@@ -70,76 +86,25 @@ describe('loginHandler', () => {
7086

7187
it('captures a found session', async () => {
7288
expect.assertions(3)
73-
const mockCallback = jest.fn()
74-
const handler = loginHandler(options, mockCallback)
7589
const session = {
7690
idp: 'https://example.com',
7791
webId: 'https://me.example.com/profile#me'
7892
}
7993
const _sessionResp = await handler('foundSession', session)
80-
expect(_sessionResp).toEqual(null)
94+
expect(_sessionResp).toBeUndefined()
8195
expect(mockCallback.mock.calls.length).toBe(1)
8296
expect(mockCallback.mock.calls[0][0]).toEqual(session)
8397
})
8498

85-
it('ignores unknown methods', async () => {
86-
expect.assertions(1)
87-
const handler = loginHandler(options, () => {})
88-
const resp = await handler('unknown_method', 'a', 'b', 'c')
89-
expect(resp).toBeNull()
90-
})
91-
})
92-
93-
describe('appOriginHandler', () => {
9499
it('responds with the window origin', async () => {
95100
expect.assertions(1)
96-
const resp = await appOriginHandler('getAppOrigin')
101+
const resp = await handler('getAppOrigin')
97102
expect(resp).toEqual('https://app.biz')
98103
})
99104

100105
it('ignores unknown methods', async () => {
101106
expect.assertions(1)
102-
const resp = await appOriginHandler('unknown_method')
103-
expect(resp).toBeNull()
104-
})
105-
})
106-
107-
describe('startPopupServer', () => {
108-
it('rejects if loginOptions does not include both popupUri and callbackUri', async () => {
109-
expect.assertions(1)
110-
const store = defaultStorage()
111-
await expect(
112-
startPopupServer(store, window, {
113-
popupUri: '',
114-
callbackUri: '',
115-
storage: store
116-
})
117-
).rejects.toBeInstanceOf(Error)
118-
})
119-
120-
it('resolves to the captured session once it captures the "foundSession" method', async () => {
121-
expect.assertions(1)
122-
const store = defaultStorage()
123-
const session = {
124-
idp: 'https://localhost',
125-
webId: 'https://localhost/profile#me'
126-
}
127-
const sessionPromise = startPopupServer(store, window, {
128-
popupUri: 'https://app.biz/select-idp',
129-
callbackUri: 'https://app.biz/callback',
130-
storage: store
131-
})
132-
window.postMessage(
133-
{
134-
'solid-auth-client': {
135-
id: '12345',
136-
method: 'foundSession',
137-
args: [session]
138-
}
139-
},
140-
window.location.origin
141-
)
142-
const resolvedSession = await sessionPromise
143-
expect(resolvedSession).toEqual(session)
107+
const resp = await handler('unknown_method')
108+
expect(resp).toBeUndefined()
144109
})
145110
})

src/ipc.js

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,3 @@ export class Client {
105105
})
106106
}
107107
}
108-
109-
export const combineHandlers = (...handlers: handler[]) => (
110-
method: string,
111-
...args: any[]
112-
): ?Promise<any> =>
113-
handlers
114-
.map(handler => handler(method, ...args))
115-
.find(promise => promise !== null)

src/popup.js

Lines changed: 38 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,29 @@
11
// @flow
22
import type { loginOptions } from './solid-auth-client'
3-
import { combineHandlers, Server } from './ipc'
3+
import { Server } from './ipc'
44
import type { Session } from './session'
55
import type { AsyncStorage } from './storage'
66
import { originOf } from './url-util'
77

8-
const popupAppRequestHandler = (
9-
store: AsyncStorage,
10-
options: loginOptions,
11-
foundSessionCb: Session => void
12-
) =>
13-
combineHandlers(
14-
storageHandler(store),
15-
loginHandler(options, foundSessionCb),
16-
appOriginHandler
17-
)
18-
19-
export const storageHandler = (store: AsyncStorage) => (
20-
method: string,
21-
...args: any[]
22-
): ?Promise<any> => {
23-
switch (method) {
24-
case 'storage/getItem':
25-
return store.getItem(...args)
26-
case 'storage/setItem':
27-
return store.setItem(...args)
28-
case 'storage/removeItem':
29-
return store.removeItem(...args)
30-
default:
31-
return null
32-
}
33-
}
34-
35-
export const loginHandler = (
36-
options: loginOptions,
37-
foundSessionCb: Session => void
38-
) => (method: string, ...args: any[]): ?Promise<any> => {
39-
switch (method) {
40-
case 'getLoginOptions':
41-
return Promise.resolve({
42-
popupUri: options.popupUri,
43-
callbackUri: options.callbackUri
44-
})
45-
case 'foundSession':
46-
foundSessionCb(...args)
47-
return Promise.resolve(null)
48-
default:
49-
return null
50-
}
51-
}
52-
53-
export const appOriginHandler = (method: string): ?Promise<any> => {
54-
return method === 'getAppOrigin'
55-
? Promise.resolve(window.location.origin)
56-
: null
8+
export function openIdpPopup(popupUri: string): window {
9+
const width = 650
10+
const height = 400
11+
const left = window.screenX + (window.innerWidth - width) / 2
12+
const top = window.screenY + (window.innerHeight - height) / 2
13+
const settings = `width=${width},height=${height},left=${left},top=${top}`
14+
return window.open(popupUri, 'solid-auth-client', settings)
5715
}
5816

59-
export const startPopupServer = (
17+
export function obtainSession(
6018
store: AsyncStorage,
61-
childWindow: window,
19+
popup: window,
6220
options: loginOptions
63-
): Promise<?Session> => {
21+
): Promise<?Session> {
6422
return new Promise((resolve, reject) => {
65-
if (!(options.popupUri && options.callbackUri)) {
66-
return reject(
67-
new Error(
68-
'Cannot serve a popup without both "options.popupUri" and "options.callbackUri"'
69-
)
70-
)
71-
}
7223
const popupServer = new Server(
73-
childWindow,
24+
popup,
7425
originOf(options.popupUri || ''),
75-
popupAppRequestHandler(store, options, (session: Session) => {
26+
popupHandler(store, options, (session: Session) => {
7627
popupServer.stop()
7728
resolve(session)
7829
})
@@ -81,19 +32,30 @@ export const startPopupServer = (
8132
})
8233
}
8334

84-
export const openIdpSelector = (options: loginOptions): window => {
85-
if (!(options.popupUri && options.callbackUri)) {
86-
throw new Error(
87-
'Cannot open IDP select UI. Must provide both "options.popupUri" and "options.callbackUri".'
88-
)
35+
export function popupHandler(
36+
store: AsyncStorage,
37+
{ popupUri, callbackUri }: loginOptions,
38+
foundSessionCb: Session => void
39+
) {
40+
return async (method: string, ...args: any[]) => {
41+
switch (method) {
42+
// Origin
43+
case 'getAppOrigin':
44+
return window.location.origin
45+
46+
// Storage
47+
case 'storage/getItem':
48+
return store.getItem(...args)
49+
case 'storage/setItem':
50+
return store.setItem(...args)
51+
case 'storage/removeItem':
52+
return store.removeItem(...args)
53+
54+
// Login
55+
case 'getLoginOptions':
56+
return { popupUri, callbackUri }
57+
case 'foundSession':
58+
foundSessionCb(...args)
59+
}
8960
}
90-
const width = 650
91-
const height = 400
92-
const w = window.open(
93-
options.popupUri,
94-
'_blank',
95-
`width=${width},height=${height},left=${(window.innerWidth - width) /
96-
2},top=${(window.innerHeight - height) / 2}`
97-
)
98-
return w
9961
}

src/solid-auth-client.js

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
/* global fetch */
33
import EventEmitter from 'events'
44
import { authnFetch } from './authn-fetch'
5-
import { openIdpSelector, startPopupServer } from './popup'
5+
import { openIdpPopup, obtainSession } from './popup'
66
import type { Session } from './session'
77
import { getSession, saveSession, clearSession } from './session'
88
import type { AsyncStorage } from './storage'
@@ -40,12 +40,8 @@ export default class SolidAuthClient extends EventEmitter {
4040
if (!options.callbackUri) {
4141
options.callbackUri = options.popupUri
4242
}
43-
const childWindow = openIdpSelector(options)
44-
const session = await startPopupServer(
45-
options.storage,
46-
childWindow,
47-
options
48-
)
43+
const popup = openIdpPopup(options.popupUri)
44+
const session = await obtainSession(options.storage, popup, options)
4945
this.emit('login', session)
5046
this.emit('session', session)
5147
return session

0 commit comments

Comments
 (0)