上一篇 實作單點登入 (SSO) 登入功能 (上) 我們介紹了 SSO 的流程以及 Electron & SSO 的登入流程及機制,接下來筆者會示範一個簡單的範例,呈現如何在 Electron 建立一個 window 並載入 React 元件,此元件包含一個登入按鈕,按下後會導向 SSO 的登入網址,進行登入流程。
我們的實作流程會參考 Postman 的流程,按下主頁登入按鈕後開啟系統瀏覽器,此處會使用 Google Auth 還做示範
先前的文章有提到如何開啟一個 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 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 }
}
}
const FirstLogin = ({ onLogin, isLoading = false }): JSX.Element => {
return (
<>
<$.LoginButton loading={isLoading} colorType={BUTTON_TYPE.BLUE} onClick={onLogin}>
Log in with browser
</$.LoginButton>
</>
)
}
實作登入 google 的網頁並放在 VITE_IDENTITY_DOMAIN env file
在 main 主程序加上以下程式
app.setAsDefaultProtocolClient('googleLogin')
/* Later */
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
設定自定義協定的主程式名稱,當網頁回應用程式時會抓取這個名稱的應用程式
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()
}
})
當導回來應用程式的時候會觸發這個事件並取得字串
此處會帶 token 回應用程式並進行字串處理並儲存至 Electron Store
此篇文章實作了一個簡易的 Google Login 流程,當使用者登入 google 後可以回到應用程式,透過這個方式,桌面應用程式可以透過外部瀏覽器進行 SSO 登入