11import React , { useState , useCallback , useEffect , useMemo } from "react" ;
22import { Box , Button , Card , CardContent , CircularProgress , IconButton , Stack , Typography } from "@mui/material" ;
33import { Link as LinkIcon , LinkOff as LinkOffIcon , Refresh as RefreshIcon , Add as AddIcon } from "@mui/icons-material" ;
4- import { Locale } from "@churchapps/apphelper" ;
4+ import { ApiHelper , Locale } from "@churchapps/apphelper" ;
55import { getProvider , getAvailableProviders , type IProvider , type DeviceAuthorizationResponse } from "@churchapps/content-providers" ;
66import { type ContentProviderAuthInterface } from "../../helpers" ;
77import { ContentProviderAuthHelper } from "../../helpers/ContentProviderAuthHelper" ;
@@ -31,7 +31,7 @@ export const ContentProviderAuthManager: React.FC<Props> = ({ ministryId, onAuth
3131 const [ pkceWindow , setPkceWindow ] = useState < Window | null > ( null ) ;
3232
3333 const availableProviders = useMemo ( ( ) => {
34- const providers = getAvailableProviders ( [ "lessonschurch" , "signpresenter" , "bibleproject" ] ) ;
34+ const providers = getAvailableProviders ( [ "lessonschurch" , "signpresenter" , "bibleproject" , "dropbox" , "jesusfilm" ] ) ;
3535 console . log ( "[ContentProviderAuthManager] availableProviders:" , providers ) ;
3636 return providers ;
3737 } , [ ] ) ;
@@ -201,15 +201,24 @@ export const ContentProviderAuthManager: React.FC<Props> = ({ ministryId, onAuth
201201 setAuthStatus ( "error" ) ;
202202 return ;
203203 }
204- // Generate code verifier
204+
205+ // Step 1: Create a relay session on the API
206+ const relayData = await ApiHelper . post ( "/oauth/relay/sessions" , { provider : providerId } , "MembershipApi" ) ;
207+ if ( ! relayData ?. sessionCode || ! relayData ?. redirectUri ) {
208+ setAuthError ( "Failed to create authorization session. Please try again." ) ;
209+ setAuthStatus ( "error" ) ;
210+ return ;
211+ }
212+
213+ const { sessionCode, redirectUri, expiresIn } = relayData ;
214+
215+ // Step 2: Generate code verifier and build auth URL using relay redirect
205216 const verifier = ( provider as any ) . generateCodeVerifier ( ) ;
206217 setCodeVerifier ( verifier ) ;
207218
208- // Build auth URL
209- const redirectUri = `${ window . location . origin } /oauth/callback` ;
210- const authResult = await ( provider as any ) . buildAuthUrl ( verifier , redirectUri ) ;
219+ const authResult = await ( provider as any ) . buildAuthUrl ( verifier , redirectUri , sessionCode ) ;
211220
212- // Open popup window
221+ // Step 3: Open popup window
213222 const width = 600 ;
214223 const height = 700 ;
215224 const left = window . screenX + ( window . outerWidth - width ) / 2 ;
@@ -230,72 +239,69 @@ export const ContentProviderAuthManager: React.FC<Props> = ({ ministryId, onAuth
230239 setPkceWindow ( popup ) ;
231240 setAuthStatus ( "pkce_waiting" ) ;
232241
233- // Listen for the callback
234- const handleMessage = async ( event : MessageEvent ) => {
235- if ( event . origin !== window . location . origin ) return ;
242+ // Step 4: Poll the relay for the auth code
243+ const expiresAt = Date . now ( ) + ( expiresIn || 300 ) * 1000 ;
236244
237- if ( event . data ?. type === "oauth_callback" && event . data ?. providerId === providerId ) {
238- window . removeEventListener ( "message" , handleMessage ) ;
245+ const poll = async ( ) => {
246+ if ( popup . closed ) {
247+ setAuthStatus ( "idle" ) ;
248+ setAuthProviderId ( null ) ;
249+ return ;
250+ }
239251
240- if ( event . data . error ) {
241- setAuthError ( event . data . error_description || event . data . error ) ;
242- setAuthStatus ( "error" ) ;
243- return ;
244- }
252+ if ( Date . now ( ) >= expiresAt ) {
253+ popup . close ( ) ;
254+ setAuthError ( "Authorization session expired. Please try again." ) ;
255+ setAuthStatus ( "error" ) ;
256+ return ;
257+ }
245258
246- if ( event . data . code ) {
247- try {
248- // Exchange code for tokens
249- const tokens = await ( provider as any ) . exchangeCodeForTokens (
250- event . data . code ,
251- verifier ,
252- redirectUri
253- ) ;
254-
255- if ( tokens ) {
256- await ContentProviderAuthHelper . storeAuth ( ministryId , providerId , tokens ) ;
257- setAuthStatus ( "success" ) ;
258- await loadLinkedProviders ( ) ;
259- if ( onAuthChange ) onAuthChange ( ) ;
260-
261- // Auto-close after success
262- setTimeout ( ( ) => {
263- setAuthProviderId ( null ) ;
264- setAuthStatus ( "idle" ) ;
265- setCodeVerifier ( null ) ;
266- } , 2000 ) ;
267- } else {
268- setAuthError ( "Failed to exchange code for tokens" ) ;
269- setAuthStatus ( "error" ) ;
270- }
271- } catch ( error ) {
272- console . error ( "Error exchanging code:" , error ) ;
273- setAuthError ( "Failed to complete authentication" ) ;
259+ try {
260+ const result = await ApiHelper . getAnonymous ( `/oauth/relay/sessions/${ sessionCode } ` , "MembershipApi" ) ;
261+
262+ if ( result ?. status === "completed" && result ?. authCode ) {
263+ popup . close ( ) ;
264+
265+ // Exchange code for tokens
266+ const tokens = await ( provider as any ) . exchangeCodeForTokens (
267+ result . authCode ,
268+ verifier ,
269+ redirectUri
270+ ) ;
271+
272+ if ( tokens ) {
273+ await ContentProviderAuthHelper . storeAuth ( ministryId , providerId , tokens ) ;
274+ setAuthStatus ( "success" ) ;
275+ await loadLinkedProviders ( ) ;
276+ if ( onAuthChange ) onAuthChange ( ) ;
277+
278+ setTimeout ( ( ) => {
279+ setAuthProviderId ( null ) ;
280+ setAuthStatus ( "idle" ) ;
281+ setCodeVerifier ( null ) ;
282+ } , 2000 ) ;
283+ } else {
284+ setAuthError ( "Failed to exchange code for tokens" ) ;
274285 setAuthStatus ( "error" ) ;
275286 }
287+ return ;
276288 }
289+
290+ // Still pending — poll again
291+ setTimeout ( poll , 3000 ) ;
292+ } catch ( error ) {
293+ console . error ( "Polling error:" , error ) ;
294+ setTimeout ( poll , 5000 ) ;
277295 }
278296 } ;
279297
280- window . addEventListener ( "message" , handleMessage ) ;
281-
282- // Check if popup is closed
283- const checkClosed = setInterval ( ( ) => {
284- if ( popup . closed ) {
285- clearInterval ( checkClosed ) ;
286- window . removeEventListener ( "message" , handleMessage ) ;
287- if ( authStatus === "pkce_waiting" ) {
288- setAuthStatus ( "idle" ) ;
289- setAuthProviderId ( null ) ;
290- }
291- }
292- } , 500 ) ;
298+ setTimeout ( poll , 3000 ) ;
293299 } catch ( error ) {
294300 console . error ( "Error starting PKCE flow:" , error ) ;
295301 setAuthError ( "Failed to start authentication" ) ;
296302 setAuthStatus ( "error" ) ;
297303 }
298- } , [ ministryId , loadLinkedProviders , onAuthChange , authStatus ] ) ;
304+ } , [ ministryId , loadLinkedProviders , onAuthChange ] ) ;
299305
300306 // Handle link button click
301307 const handleLink = useCallback ( async ( providerId : string ) => {
0 commit comments