@@ -9,33 +9,31 @@ import {
9
9
RuntimeContext ,
10
10
RuntimeEnvironment ,
11
11
createComponent ,
12
+ ExposableError ,
12
13
} from '@gitbook/runtime' ;
13
14
14
15
const logger = Logger ( 'oidc.visitor-auth' ) ;
15
16
16
- type OIDCRuntimeEnvironment = RuntimeEnvironment < { } , OIDCSiteOrSpaceInstallationConfiguration > ;
17
+ type OIDCRuntimeEnvironment = RuntimeEnvironment < { } , OIDCSiteInstallationConfiguration > ;
17
18
18
19
type OIDCRuntimeContext = RuntimeContext < OIDCRuntimeEnvironment > ;
19
20
20
- type OIDCSiteOrSpaceInstallationConfiguration = {
21
+ type OIDCSiteInstallationConfiguration = {
21
22
client_id ?: string ;
22
23
authorization_endpoint ?: string ;
23
24
access_token_endpoint ?: string ;
24
25
client_secret ?: string ;
25
26
scope ?: string ;
26
27
} ;
27
28
28
- type OIDCState = OIDCSiteOrSpaceInstallationConfiguration ;
29
+ type OIDCState = OIDCSiteInstallationConfiguration ;
29
30
30
31
type OIDCProps = {
31
32
installation : {
32
33
configuration ?: IntegrationInstallationConfiguration ;
33
34
} ;
34
- spaceInstallation : {
35
- configuration ?: OIDCSiteOrSpaceInstallationConfiguration ;
36
- } ;
37
35
siteInstallation : {
38
- configuration ?: OIDCSiteOrSpaceInstallationConfiguration ;
36
+ configuration ?: OIDCSiteInstallationConfiguration ;
39
37
} ;
40
38
} ;
41
39
@@ -55,64 +53,50 @@ const getDomainWithHttps = (url: string): string => {
55
53
const configBlock = createComponent < OIDCProps , OIDCState , OIDCAction , OIDCRuntimeContext > ( {
56
54
componentId : 'config' ,
57
55
initialState : ( props ) => {
58
- const siteOrSpaceInstallation = props . siteInstallation ?? props . spaceInstallation ;
56
+ const siteInstallation = props . siteInstallation ;
59
57
return {
60
- client_id : siteOrSpaceInstallation . configuration ?. client_id ?. toString ( ) || '' ,
61
- authorization_endpoint :
62
- siteOrSpaceInstallation . configuration ?. authorization_endpoint ?. toString ( ) || '' ,
63
- access_token_endpoint :
64
- siteOrSpaceInstallation . configuration ?. access_token_endpoint ?. toString ( ) || '' ,
65
- client_secret : siteOrSpaceInstallation . configuration ?. client_secret ?. toString ( ) || '' ,
66
- scope : siteOrSpaceInstallation . configuration ?. scope ?. toString ( ) || '' ,
58
+ client_id : siteInstallation . configuration ?. client_id || '' ,
59
+ authorization_endpoint : siteInstallation . configuration ?. authorization_endpoint || '' ,
60
+ access_token_endpoint : siteInstallation . configuration ?. access_token_endpoint || '' ,
61
+ client_secret : siteInstallation . configuration ?. client_secret || '' ,
62
+ scope : siteInstallation . configuration ?. scope || '' ,
67
63
} ;
68
64
} ,
69
65
action : async ( element , action , context ) => {
70
66
switch ( action . action ) {
71
67
case 'save.config' :
72
68
const { api, environment } = context ;
73
- const siteOrSpaceInstallation =
74
- environment . siteInstallation ?? environment . spaceInstallation ;
69
+
70
+ const siteInstallation = assertSiteInstallation ( environment ) ;
75
71
76
72
const configurationBody = {
77
- ...siteOrSpaceInstallation . configuration ,
73
+ ...siteInstallation . configuration ,
78
74
client_id : element . state . client_id ,
79
75
client_secret : element . state . client_secret ,
80
76
authorization_endpoint : getDomainWithHttps (
81
- element . state . authorization_endpoint ,
77
+ element . state . authorization_endpoint ?? '' ,
78
+ ) ,
79
+ access_token_endpoint : getDomainWithHttps (
80
+ element . state . access_token_endpoint ?? '' ,
82
81
) ,
83
- access_token_endpoint : getDomainWithHttps ( element . state . access_token_endpoint ) ,
84
82
scope : element . state . scope ,
85
83
} ;
86
- if ( 'site' in siteOrSpaceInstallation ) {
87
- await api . integrations . updateIntegrationSiteInstallation (
88
- siteOrSpaceInstallation . integration ,
89
- siteOrSpaceInstallation . installation ,
90
- siteOrSpaceInstallation . site ,
91
- {
92
- configuration : {
93
- ...configurationBody ,
94
- } ,
95
- } ,
96
- ) ;
97
- } else {
98
- await api . integrations . updateIntegrationSpaceInstallation (
99
- siteOrSpaceInstallation . integration ,
100
- siteOrSpaceInstallation . installation ,
101
- siteOrSpaceInstallation . space ,
102
- {
103
- configuration : {
104
- ...configurationBody ,
105
- } ,
84
+ await api . integrations . updateIntegrationSiteInstallation (
85
+ siteInstallation . integration ,
86
+ siteInstallation . installation ,
87
+ siteInstallation . site ,
88
+ {
89
+ configuration : {
90
+ ...configurationBody ,
106
91
} ,
107
- ) ;
108
- }
92
+ } ,
93
+ ) ;
109
94
return element ;
110
95
}
111
96
} ,
112
97
render : async ( element , context ) => {
113
- const siteOrSpaceInstallation =
114
- context . environment . siteInstallation ?? context . environment . spaceInstallation ;
115
- const VACallbackURL = `${ siteOrSpaceInstallation ?. urls ?. publicEndpoint } /visitor-auth/response` ;
98
+ const siteInstallation = context . environment . siteInstallation ;
99
+ const VACallbackURL = `${ siteInstallation ?. urls ?. publicEndpoint } /visitor-auth/response` ;
116
100
return (
117
101
< block >
118
102
< input
@@ -243,38 +227,50 @@ const configBlock = createComponent<OIDCProps, OIDCState, OIDCAction, OIDCRuntim
243
227
} ) ;
244
228
245
229
/**
246
- * Get the published content (site or space) related urls.
230
+ * Get the published content related urls.
247
231
*/
248
232
async function getPublishedContentUrls ( context : OIDCRuntimeContext ) {
249
- const organizationId = context . environment . installation ?. target ?. organization ;
250
- const siteOrSpaceInstallation =
251
- context . environment . siteInstallation ?? context . environment . spaceInstallation ;
252
- const publishedContentData =
253
- 'site' in siteOrSpaceInstallation
254
- ? await context . api . orgs . getSiteById ( organizationId , siteOrSpaceInstallation . site )
255
- : await context . api . spaces . getSpaceById ( siteOrSpaceInstallation . space ) ;
233
+ const organizationId = assertOrgId ( context . environment ) ;
234
+ const siteInstallation = assertSiteInstallation ( context . environment ) ;
235
+ const publishedContentData = await context . api . orgs . getSiteById (
236
+ organizationId ,
237
+ siteInstallation . site ,
238
+ ) ;
256
239
257
240
return publishedContentData . data . urls ;
258
241
}
259
242
243
+ function assertSiteInstallation ( environment : OIDCRuntimeEnvironment ) {
244
+ const siteInstallation = environment . siteInstallation ;
245
+ if ( ! siteInstallation ) {
246
+ throw new Error ( 'No site installation found' ) ;
247
+ }
248
+
249
+ return siteInstallation ;
250
+ }
251
+
252
+ function assertOrgId ( environment : OIDCRuntimeEnvironment ) {
253
+ const orgId = environment . installation ?. target ?. organization ! ;
254
+ if ( ! orgId ) {
255
+ throw new Error ( 'No org ID found' ) ;
256
+ }
257
+
258
+ return orgId ;
259
+ }
260
+
260
261
const handleFetchEvent : FetchEventCallback < OIDCRuntimeContext > = async ( request , context ) => {
261
262
const { environment } = context ;
262
- const siteOrSpaceInstallation = environment . siteInstallation ?? environment . spaceInstallation ;
263
- const installationURL = siteOrSpaceInstallation ? .urls ?. publicEndpoint ;
263
+ const siteInstallation = assertSiteInstallation ( environment ) ;
264
+ const installationURL = siteInstallation . urls ?. publicEndpoint ;
264
265
if ( installationURL ) {
265
266
const router = Router ( {
266
267
base : new URL ( installationURL ) . pathname ,
267
268
} ) ;
268
269
269
270
router . get ( '/visitor-auth/response' , async ( request ) => {
270
- if (
271
- ( 'site' in siteOrSpaceInstallation && siteOrSpaceInstallation . site ) ||
272
- ( 'space' in siteOrSpaceInstallation && siteOrSpaceInstallation . space )
273
- ) {
271
+ if ( 'site' in siteInstallation && siteInstallation . site ) {
274
272
const publishedContentUrls = await getPublishedContentUrls ( context ) ;
275
- const privateKey =
276
- context . environment . signingSecrets . siteInstallation ??
277
- context . environment . signingSecrets . spaceInstallation ;
273
+ const privateKey = context . environment . signingSecrets . siteInstallation ! ;
278
274
let token ;
279
275
try {
280
276
token = await sign (
@@ -287,11 +283,10 @@ const handleFetchEvent: FetchEventCallback<OIDCRuntimeContext> = async (request,
287
283
} ) ;
288
284
}
289
285
290
- const accessTokenEndpoint =
291
- siteOrSpaceInstallation . configuration . access_token_endpoint ;
292
- const clientId = siteOrSpaceInstallation . configuration . client_id ;
293
- const clientSecret = siteOrSpaceInstallation . configuration . client_secret ;
294
- if ( clientId && clientSecret ) {
286
+ const accessTokenEndpoint = siteInstallation . configuration . access_token_endpoint ;
287
+ const clientId = siteInstallation . configuration . client_id ;
288
+ const clientSecret = siteInstallation . configuration . client_secret ;
289
+ if ( clientId && clientSecret && accessTokenEndpoint ) {
295
290
const searchParams = new URLSearchParams ( {
296
291
grant_type : 'authorization_code' ,
297
292
client_id : clientId ,
@@ -317,13 +312,13 @@ const handleFetchEvent: FetchEventCallback<OIDCRuntimeContext> = async (request,
317
312
318
313
if ( 'access_token' in resp ) {
319
314
let url ;
320
- const state = request . query . state . toString ( ) ;
315
+ const state = request . query . state ! . toString ( ) ;
321
316
const location = state . substring ( state . indexOf ( '-' ) + 1 ) ;
322
317
if ( location ) {
323
318
url = new URL ( `${ publishedContentUrls ?. published } ${ location } ` ) ;
324
319
url . searchParams . append ( 'jwt_token' , token ) ;
325
320
} else {
326
- url = new URL ( publishedContentUrls ?. published ) ;
321
+ url = new URL ( publishedContentUrls ?. published ! ) ;
327
322
url . searchParams . append ( 'jwt_token' , token ) ;
328
323
}
329
324
if ( publishedContentUrls ?. published && token ) {
@@ -351,9 +346,12 @@ const handleFetchEvent: FetchEventCallback<OIDCRuntimeContext> = async (request,
351
346
) ;
352
347
}
353
348
} else {
354
- return new Response ( 'Error: Either ClientId or Client Secret is missing' , {
355
- status : 400 ,
356
- } ) ;
349
+ return new Response (
350
+ 'Error: Either ClientId or Client Secret or Access Token Endpoint is missing' ,
351
+ {
352
+ status : 400 ,
353
+ } ,
354
+ ) ;
357
355
}
358
356
}
359
357
} ) ;
@@ -383,14 +381,17 @@ export default createIntegration({
383
381
components : [ configBlock ] ,
384
382
fetch_visitor_authentication : async ( event , context ) => {
385
383
const { environment } = context ;
386
- const siteOrSpaceInstallation =
387
- environment . siteInstallation ?? environment . spaceInstallation ;
384
+ const siteInstallation = assertSiteInstallation ( environment ) ;
388
385
389
- const installationURL = siteOrSpaceInstallation ?. urls ?. publicEndpoint ;
386
+ const installationURL = siteInstallation . urls . publicEndpoint ;
387
+ const configuration = siteInstallation . configuration ;
390
388
391
- const authorizationEndpoint = siteOrSpaceInstallation ?. configuration . authorization_endpoint ;
392
- const clientId = siteOrSpaceInstallation ?. configuration . client_id ;
393
- const scope = siteOrSpaceInstallation ?. configuration . scope ;
389
+ const authorizationEndpoint = configuration . authorization_endpoint ;
390
+ const clientId = configuration . client_id ;
391
+ const scope = configuration . scope ;
392
+ if ( ! clientId || ! authorizationEndpoint || ! scope ) {
393
+ throw new ExposableError ( 'OIDC configuration is missing' ) ;
394
+ }
394
395
395
396
const location = event . location ? event . location : '' ;
396
397
@@ -401,12 +402,6 @@ export default createIntegration({
401
402
url . searchParams . append ( 'scope' , scope . toLowerCase ( ) ) ;
402
403
url . searchParams . append ( 'state' , `state-${ location } ` ) ;
403
404
404
- try {
405
- return Response . redirect ( url . toString ( ) ) ;
406
- } catch ( e ) {
407
- return new Response ( e . message , {
408
- status : e . status || 500 ,
409
- } ) ;
410
- }
405
+ return Response . redirect ( url . toString ( ) ) ;
411
406
} ,
412
407
} ) ;
0 commit comments