You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. 'use client'
  2. import { useRouter, useSearchParams } from 'next/navigation'
  3. import type { FC } from 'react'
  4. import React, { useCallback, useEffect } from 'react'
  5. import { useTranslation } from 'react-i18next'
  6. import Toast from '@/app/components/base/toast'
  7. import { removeAccessToken, setAccessToken } from '@/app/components/share/utils'
  8. import { useGlobalPublicStore } from '@/context/global-public-context'
  9. import Loading from '@/app/components/base/loading'
  10. import AppUnavailable from '@/app/components/base/app-unavailable'
  11. import NormalForm from './normalForm'
  12. import { AccessMode } from '@/models/access-control'
  13. import ExternalMemberSsoAuth from './components/external-member-sso-auth'
  14. import { fetchAccessToken } from '@/service/share'
  15. const WebSSOForm: FC = () => {
  16. const { t } = useTranslation()
  17. const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)
  18. const webAppAccessMode = useGlobalPublicStore(s => s.webAppAccessMode)
  19. const searchParams = useSearchParams()
  20. const router = useRouter()
  21. const redirectUrl = searchParams.get('redirect_url')
  22. const tokenFromUrl = searchParams.get('web_sso_token')
  23. const message = searchParams.get('message')
  24. const getSigninUrl = useCallback(() => {
  25. const params = new URLSearchParams(searchParams)
  26. params.delete('message')
  27. return `/webapp-signin?${params.toString()}`
  28. }, [searchParams])
  29. const backToHome = useCallback(() => {
  30. removeAccessToken()
  31. const url = getSigninUrl()
  32. router.replace(url)
  33. }, [getSigninUrl, router])
  34. const showErrorToast = (msg: string) => {
  35. Toast.notify({
  36. type: 'error',
  37. message: msg,
  38. })
  39. }
  40. const getAppCodeFromRedirectUrl = useCallback(() => {
  41. const appCode = redirectUrl?.split('/').pop()
  42. if (!appCode)
  43. return null
  44. return appCode
  45. }, [redirectUrl])
  46. useEffect(() => {
  47. (async () => {
  48. if (message)
  49. return
  50. const appCode = getAppCodeFromRedirectUrl()
  51. if (appCode && tokenFromUrl && redirectUrl) {
  52. localStorage.setItem('webapp_access_token', tokenFromUrl)
  53. const tokenResp = await fetchAccessToken({ appCode, webAppAccessToken: tokenFromUrl })
  54. await setAccessToken(appCode, tokenResp.access_token)
  55. router.replace(redirectUrl)
  56. return
  57. }
  58. if (appCode && redirectUrl && localStorage.getItem('webapp_access_token')) {
  59. const tokenResp = await fetchAccessToken({ appCode, webAppAccessToken: localStorage.getItem('webapp_access_token') })
  60. await setAccessToken(appCode, tokenResp.access_token)
  61. router.replace(redirectUrl)
  62. }
  63. })()
  64. }, [getAppCodeFromRedirectUrl, redirectUrl, router, tokenFromUrl, message])
  65. useEffect(() => {
  66. if (webAppAccessMode && webAppAccessMode === AccessMode.PUBLIC && redirectUrl)
  67. router.replace(redirectUrl)
  68. }, [webAppAccessMode, router, redirectUrl])
  69. if (tokenFromUrl) {
  70. return <div className='flex h-full items-center justify-center'>
  71. <Loading />
  72. </div>
  73. }
  74. if (message) {
  75. return <div className='flex h-full flex-col items-center justify-center gap-y-4'>
  76. <AppUnavailable className='h-auto w-auto' code={t('share.common.appUnavailable')} unknownReason={message} />
  77. <span className='system-sm-regular cursor-pointer text-text-tertiary' onClick={backToHome}>{t('share.login.backToHome')}</span>
  78. </div>
  79. }
  80. if (!redirectUrl) {
  81. showErrorToast('redirect url is invalid.')
  82. return <div className='flex h-full items-center justify-center'>
  83. <AppUnavailable code={t('share.common.appUnavailable')} unknownReason='redirect url is invalid.' />
  84. </div>
  85. }
  86. if (webAppAccessMode && webAppAccessMode === AccessMode.PUBLIC) {
  87. return <div className='flex h-full items-center justify-center'>
  88. <Loading />
  89. </div>
  90. }
  91. if (!systemFeatures.webapp_auth.enabled) {
  92. return <div className="flex h-full items-center justify-center">
  93. <p className='system-xs-regular text-text-tertiary'>{t('login.webapp.disabled')}</p>
  94. </div>
  95. }
  96. if (webAppAccessMode && (webAppAccessMode === AccessMode.ORGANIZATION || webAppAccessMode === AccessMode.SPECIFIC_GROUPS_MEMBERS)) {
  97. return <div className='w-full max-w-[400px]'>
  98. <NormalForm />
  99. </div>
  100. }
  101. if (webAppAccessMode && webAppAccessMode === AccessMode.EXTERNAL_MEMBERS)
  102. return <ExternalMemberSsoAuth />
  103. return <div className='flex h-full flex-col items-center justify-center gap-y-4'>
  104. <AppUnavailable className='h-auto w-auto' isUnknownReason={true} />
  105. <span className='system-sm-regular cursor-pointer text-text-tertiary' onClick={backToHome}>{t('share.login.backToHome')}</span>
  106. </div>
  107. }
  108. export default React.memo(WebSSOForm)