iT邦幫忙

2024 iThome 鐵人賽

DAY 27
0
Modern Web

從 0 到 1:30 篇文章帶你玩轉 Electron 與 React系列 第 27

實作單點登入 (SSO) 登入功能 (下)

  • 分享至 

  • xImage
  •  

上一篇 實作單點登入 (SSO) 登入功能 (上) 我們介紹了 SSO 的流程以及 Electron & SSO 的登入流程及機制,接下來筆者會示範一個簡單的範例,呈現如何在 Electron 建立一個 window 並載入 React 元件,此元件包含一個登入按鈕,按下後會導向 SSO 的登入網址,進行登入流程。

實作流程

我們的實作流程會參考 Postman 的流程,按下主頁登入按鈕後開啟系統瀏覽器,此處會使用 Google Auth 還做示範

步驟

  1. 實作一個視窗並有一個 React 元件
  2. 元件內實作一個按鈕會開啟系統瀏覽器並導向 Google Auth 網址
  3. 登入 Google
  4. 使用 自定義協定 傳遞 token 回到應用程式
  5. Electron 程式收到 token 後使用 Electron Sotre 儲存 token

實作一個視窗並有一個 React 元件

先前的文章有提到如何開啟一個 window,此處主要呈現元件的部分

const LoginPage = (): JSX.Element => {
  const [isFirstLogin, _setIsFirstLogin] = useState<boolean>(true)
  const { toastState, showToast, setErrorToast } = useToast()
  const [isLoading, setIsLoading] = useState<boolean>(false)

  const goToDirectPage = async (): Promise<void> => {
    const data: AuthPkceResp = await api.auth.getAuthPkce()
    const { codeChallenge, codeChallengeMethod } = data

    const url = `${import.meta.env.VITE_IDENTITY_DOMAIN}`
    await window.openExternal(url)
  }

  const handleLogin = async (): Promise<void> => {
    try {
      setIsLoading(true)
      goToDirectPage()
    } catch (error) {
      setErrorToast('Error! Please try again.')
    } finally {
      setIsLoading(false)
    }
  }

  return (
    <$.Wrapper>
      {showToast && <Toast message={toastState.message} status={toastState.status} />}
      <CloseButton />
      <$.Logo src={logoIcon} alt='Logo' />
      {isFirstLogin ? <FirstLogin onLogin={handleLogin} isLoading={isLoading} /> : <ReLogin onLogin={handleLogin} />}
    </$.Wrapper>
  )
}

export default LoginPage

window.openExternal(url)

在全域的 window 註冊 openExternal API 給 renderer 使用

declare global {
  interface Window {
    electron: ElectronAPI
    injectData: any
    windowBounds?: {
      x: number
      y: number
      width: number
      height: number
    }
    ipcInvoke: (channel: string, ...args: any[]) => Promise<any>
    ipcOn: (channel: string, listener: (event: any, data: any) => void) => void
    ipcSend: (channel: string, data?: any) => void
    openExternal: (url: string, options?: Electron.OpenExternalOption) => Promise<void>
    reduxtron: PreloadReduxBridgeReturn<State, Action>['handlers']
    dataStore: { get: (key: string) => any; set: (key: string, val: any) => void }
  }
}

元件內實作一個按鈕會開啟系統瀏覽器並導向 Google Auth 網址

const FirstLogin = ({ onLogin, isLoading = false }): JSX.Element => {
  return (
    <>
      <$.LoginButton loading={isLoading} colorType={BUTTON_TYPE.BLUE} onClick={onLogin}>
        Log in with browser
      </$.LoginButton>
    </>
  )
}

登入 Google

實作登入 google 的網頁並放在 VITE_IDENTITY_DOMAIN env file

使用 自定義協定傳遞 token 回到應用程式

在 main 主程序加上以下程式

app.setAsDefaultProtocolClient('googleLogin')

/* Later */

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

setAsDefaultProtocolClient('{app_name}')

設定自定義協定的主程式名稱,當網頁回應用程式時會抓取這個名稱的應用程式

Electron 程式收到 token 後使用 Electron Sotre 儲存 token

app.setAsDefaultProtocolClient('googleLogin')

app.on('open-url', (_event, url) => {
  const accessTokenRegex = /accessToken=([^&]+)/
  const accessTokenMatch = url.match(accessTokenRegex)
  const accessToken = accessTokenMatch ? accessTokenMatch[1] : null

  electronStore.set('accessToken', accessToken)

  showDialogWindow()
})

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

open-url event

當導回來應用程式的時候會觸發這個事件並取得字串

此處會帶 token 回應用程式並進行字串處理並儲存至 Electron Store

總結

此篇文章實作了一個簡易的 Google Login 流程,當使用者登入 google 後可以回到應用程式,透過這個方式,桌面應用程式可以透過外部瀏覽器進行 SSO 登入


上一篇
實作單點登入 (SSO) 登入功能 (上)
下一篇
Eelxtron 中如何撰寫單元 & 整合測試
系列文
從 0 到 1:30 篇文章帶你玩轉 Electron 與 React30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言