@@ -2,19 +2,86 @@ import type { WrapperProps } from '@docusaurus/types';
2
2
import useDocusaurusContext from '@docusaurus/useDocusaurusContext' ;
3
3
import type LayoutType from '@theme/Layout' ;
4
4
import Layout from '@theme-original/Layout' ;
5
- import { type ReactNode } from 'react' ;
5
+ import { type ReactNode , useCallback , useEffect } from 'react' ;
6
6
7
- import { useDebugLogger , useApiBaseUrl , useGoogleOneTapConfig , useAuthStatus } from './hooks' ;
7
+ import {
8
+ useDebugLogger ,
9
+ useApiBaseUrl ,
10
+ useGoogleOneTapConfig ,
11
+ useAuthStatus ,
12
+ useGoogleOneTapVerify ,
13
+ } from './hooks' ;
14
+ import type { GoogleOneTapCredentialResponse , GoogleOneTapVerifyResponse } from './types' ;
8
15
9
16
type Props = WrapperProps < typeof LayoutType > ;
10
17
11
18
export default function LayoutWrapper ( props : Props ) : ReactNode {
12
19
// Hooks must be called at the top level, outside of try-catch
13
20
const { siteConfig } = useDocusaurusContext ( ) ;
14
21
const debugLogger = useDebugLogger ( siteConfig ) ;
15
- const apiBaseUrl = useApiBaseUrl ( siteConfig ) ;
22
+ const { baseUrl : apiBaseUrl , authUrl , redirectUri } = useApiBaseUrl ( siteConfig ) ;
16
23
const config = useGoogleOneTapConfig ( apiBaseUrl , debugLogger ) ;
17
24
const { authStatus } = useAuthStatus ( siteConfig , debugLogger ) ;
25
+ const verifyGoogleOneTap = useGoogleOneTapVerify ( apiBaseUrl , debugLogger ) ;
26
+
27
+ // Function to manually build Logto sign-in URL
28
+ const buildSignInUrl = useCallback (
29
+ ( { oneTimeToken, email, isNewUser } : GoogleOneTapVerifyResponse ) => {
30
+ try {
31
+ const signInUrl = new URL ( authUrl ) ;
32
+
33
+ // Standard OIDC parameters: client_id
34
+ signInUrl . searchParams . set ( 'client_id' , 'admin-console' ) ;
35
+ signInUrl . searchParams . set ( 'redirect_uri' , redirectUri ) ;
36
+ signInUrl . searchParams . set ( 'first_screen' , isNewUser ? 'register' : 'sign_in' ) ;
37
+
38
+ // Add one-time token parameters
39
+ signInUrl . searchParams . set ( 'one_time_token' , oneTimeToken ) ;
40
+ signInUrl . searchParams . set ( 'login_hint' , email ) ;
41
+
42
+ return signInUrl . toString ( ) ;
43
+ } catch ( error ) {
44
+ debugLogger . error ( 'Failed to build sign-in URL:' , error ) ;
45
+ return null ;
46
+ }
47
+ } ,
48
+ [ authUrl , redirectUri , debugLogger ]
49
+ ) ;
50
+
51
+ const handleCredentialResponse = useCallback (
52
+ async ( response : GoogleOneTapCredentialResponse ) => {
53
+ const verifyData = await verifyGoogleOneTap ( response ) ;
54
+
55
+ if ( verifyData ) {
56
+ debugLogger . log ( 'Verification completed:' , verifyData ) ;
57
+
58
+ try {
59
+ // Build Logto sign-in URL with one-time token
60
+ const signInUrl = buildSignInUrl ( verifyData ) ;
61
+
62
+ if ( signInUrl ) {
63
+ // Open sign-in URL in new tab
64
+ window . open ( signInUrl , '_blank' , 'noopener,noreferrer' ) ;
65
+ debugLogger . log ( 'Logto sign-in URL opened in new tab with one-time token' ) ;
66
+ } else {
67
+ debugLogger . error ( 'Failed to build sign-in URL' ) ;
68
+ }
69
+ } catch ( error ) {
70
+ debugLogger . error ( 'Failed to open sign-in URL:' , error ) ;
71
+ }
72
+ }
73
+ } ,
74
+ [ verifyGoogleOneTap , debugLogger , buildSignInUrl ]
75
+ ) ;
76
+
77
+ // Make handleCredentialResponse globally available for Google One Tap callback
78
+ useEffect ( ( ) => {
79
+ if ( typeof window !== 'undefined' ) {
80
+ // eslint-disable-next-line @silverhand/fp/no-mutation, no-restricted-syntax
81
+ ( window as unknown as Record < string , unknown > ) . handleCredentialResponse =
82
+ handleCredentialResponse ;
83
+ }
84
+ } , [ handleCredentialResponse ] ) ;
18
85
19
86
// Safe logging with error handling
20
87
try {
0 commit comments