Skip to content

Commit c9c1a34

Browse files
hyriousBlackHole1
andauthoredApr 7, 2022
feat(login): support google login (#1465)
* feat(flat-web): google login * feat(desktop): google login * chore: remove unused code * chore: remove spell check since it already exist in lint * Update desktop/renderer-app/src/pages/LoginPage/googleLogin.ts Co-authored-by: Black-Hole <[email protected]> * chore: compact code Co-authored-by: Black-Hole <[email protected]>
1 parent c2088b0 commit c9c1a34

File tree

23 files changed

+215
-26
lines changed

23 files changed

+215
-26
lines changed
 

‎.husky/pre-commit

-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
#!/bin/sh
22
. "$(dirname "$0")/_/husky.sh"
33

4-
pnpm check-spelling
54
pnpm lint

‎config/America/.env.development

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ AGORA_APP_ID=931b86d6781e49a2a255db4ce6e8e804
33
GITHUB_CLIENT_ID=0ac608815326aead5db7
44
WECHAT_APP_ID=wx1133c2153a45e9b8
55
AGORA_OAUTH_CLIENT_ID=flat-dev
6+
GOOGLE_OAUTH_CLIENT_ID=273996094508-p97og69ojac5ja0khn1rvmi3tb7vgfgm.apps.googleusercontent.com
67

78
CLOUD_STORAGE_OSS_ALIBABA_ACCESS_KEY=LTAI5t9Gb6tzQzzLmB6cTVf7
89
CLOUD_STORAGE_OSS_ALIBABA_BUCKET=flat-storage

‎config/America/.env.production

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ AGORA_APP_ID=931b86d6781e49a2a255db4ce6e8e804
33
GITHUB_CLIENT_ID=da83d7e14217594fba46
44
WECHAT_APP_ID=wx96d522d69d384cce
55
AGORA_OAUTH_CLIENT_ID=flat
6+
GOOGLE_OAUTH_CLIENT_ID=273996094508-2rpraucen77a1o5dul5ftrua5k3og157.apps.googleusercontent.com
67

78
CLOUD_STORAGE_OSS_ALIBABA_ACCESS_KEY=LTAI5t9Gb6tzQzzLmB6cTVf7
89
CLOUD_STORAGE_OSS_ALIBABA_BUCKET=flat-storage

‎config/China/.env.development

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ AGORA_APP_ID=931b86d6781e49a2a255db4ce6e8e804
33
GITHUB_CLIENT_ID=9821657775fbc74773f1
44
WECHAT_APP_ID=wx1133c2153a45e9b8
55
AGORA_OAUTH_CLIENT_ID=flat-dev
6+
GOOGLE_OAUTH_CLIENT_ID=273996094508-p97og69ojac5ja0khn1rvmi3tb7vgfgm.apps.googleusercontent.com
67

78
CLOUD_STORAGE_OSS_ALIBABA_ACCESS_KEY=LTAI5t9Gb6tzQzzLmB6cTVf7
89
CLOUD_STORAGE_OSS_ALIBABA_BUCKET=flat-storage

‎config/China/.env.production

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ AGORA_APP_ID=931b86d6781e49a2a255db4ce6e8e804
33
GITHUB_CLIENT_ID=71a29285a437998bdfe0
44
WECHAT_APP_ID=wx96d522d69d384cce
55
AGORA_OAUTH_CLIENT_ID=flat
6+
GOOGLE_OAUTH_CLIENT_ID=273996094508-2rpraucen77a1o5dul5ftrua5k3og157.apps.googleusercontent.com
67

78
CLOUD_STORAGE_OSS_ALIBABA_ACCESS_KEY=LTAI5t9Gb6tzQzzLmB6cTVf7
89
CLOUD_STORAGE_OSS_ALIBABA_BUCKET=flat-storage

‎desktop/renderer-app/src/api-middleware/flatServer/constants.ts

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export const FLAT_SERVER_VERSIONS = {
88

99
export const FLAT_SERVER_LOGIN = {
1010
GITHUB_CALLBACK: `${FLAT_SERVER_VERSIONS.V1}/login/github/callback`,
11+
GOOGLE_CALLBACK: `${FLAT_SERVER_VERSIONS.V1}/login/google/callback`,
1112
WECHAT_CALLBACK: `${FLAT_SERVER_VERSIONS.V1}/login/weChat/web/callback`,
1213
} as const;
1314

‎desktop/renderer-app/src/constants/process.ts

+4
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ export const GITHUB = Object.freeze({
2424
CLIENT_ID: process.env.GITHUB_CLIENT_ID,
2525
});
2626

27+
export const GOOGLE = Object.freeze({
28+
CLIENT_ID: process.env.GOOGLE_OAUTH_CLIENT_ID,
29+
});
30+
2731
export const FLAT_SERVER_DOMAIN = process.env.FLAT_SERVER_DOMAIN;
2832
export const FLAT_WEB_DOMAIN = process.env.FLAT_WEB_DOMAIN;
2933

‎desktop/renderer-app/src/pages/HomePage/index.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,10 @@ export const HomePage = observer<HomePageProps>(function HomePage() {
7676

7777
try {
7878
await loginCheck();
79-
globalStore.lastLoginCheck = Date.now();
79+
globalStore.updateLastLoginCheck(Date.now());
8080
return true;
8181
} catch (e) {
82-
globalStore.lastLoginCheck = null;
82+
globalStore.updateLastLoginCheck(null);
8383
console.error(e);
8484
errorTips(e as Error);
8585
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { setAuthUUID, loginProcess } from "../../api-middleware/flatServer";
2+
import { v4 as uuidv4 } from "uuid";
3+
import { LoginExecutor } from "./utils";
4+
import { shell } from "electron";
5+
import { errorTips } from "../../components/Tips/ErrorTips";
6+
import { FLAT_SERVER_LOGIN } from "../../api-middleware/flatServer/constants";
7+
import { GOOGLE } from "../../constants/process";
8+
9+
// @TODO: migrate to new google login api before 2023. @hyrious
10+
// https://developers.google.com/identity/gsi/web
11+
export const googleLogin: LoginExecutor = onSuccess => {
12+
let timer = NaN;
13+
const authUUID = uuidv4();
14+
const scopes = ["openid", "https://www.googleapis.com/auth/userinfo.profile"];
15+
16+
function getGoogleURL(authUUID: string): string {
17+
const redirectURL = encodeURIComponent(FLAT_SERVER_LOGIN.GOOGLE_CALLBACK);
18+
const scope = encodeURIComponent(scopes.join(" "));
19+
return `https://accounts.google.com/o/oauth2/v2/auth?response_type=code&access_type=online&scope=${scope}&client_id=${GOOGLE.CLIENT_ID}&redirect_uri=${redirectURL}&state=${authUUID}`;
20+
}
21+
22+
void (async () => {
23+
try {
24+
await setAuthUUID(authUUID);
25+
} catch (err) {
26+
errorTips(err);
27+
return;
28+
}
29+
30+
void shell.openExternal(getGoogleURL(authUUID));
31+
32+
const googleLoginProcessRequest = async (): Promise<void> => {
33+
try {
34+
const data = await loginProcess(authUUID);
35+
36+
if (!data.name) {
37+
timer = window.setTimeout(googleLoginProcessRequest, 2000);
38+
return;
39+
}
40+
41+
onSuccess(data);
42+
} catch (err) {
43+
errorTips(err);
44+
}
45+
};
46+
47+
void googleLoginProcessRequest();
48+
})();
49+
50+
return () => {
51+
window.clearTimeout(timer);
52+
};
53+
};

‎desktop/renderer-app/src/pages/LoginPage/index.tsx

+26-10
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,26 @@ import "./index.less";
22

33
import React, { useCallback, useContext, useEffect, useRef, useState } from "react";
44
import { constants } from "flat-types";
5+
import { useTranslation } from "react-i18next";
56
import { observer } from "mobx-react-lite";
6-
import { ipcAsyncByMainWindow, ipcSyncByApp } from "../../utils/ipc";
7+
import { message } from "antd";
78
import { LoginPanel, LoginButton, LoginButtonProviderType } from "flat-components";
8-
import { LoginDisposer } from "./utils";
99
import { githubLogin } from "./githubLogin";
10+
import WeChatLogin from "./WeChatLogin";
11+
import { googleLogin } from "./googleLogin";
12+
import { ipcAsyncByMainWindow, ipcSyncByApp } from "../../utils/ipc";
13+
import { LoginDisposer } from "./utils";
1014
import { RouteNameType, usePushHistory } from "../../utils/routes";
1115
import { GlobalStoreContext } from "../../components/StoreProvider";
1216
import { AppUpgradeModal, AppUpgradeModalProps } from "../../components/AppUpgradeModal";
1317
import { runtime } from "../../utils/runtime";
1418
import { useSafePromise } from "../../utils/hooks/lifecycle";
15-
import { useTranslation } from "react-i18next";
1619
import {
1720
PRIVACY_URL_EN,
1821
PRIVACY_URL_CN,
1922
SERVICE_URL_EN,
2023
SERVICE_URL_CN,
2124
} from "../../constants/process";
22-
import { message } from "antd";
23-
import WeChatLogin from "./WeChatLogin";
2425

2526
export const LoginPage = observer(function LoginPage() {
2627
const { i18n } = useTranslation();
@@ -81,6 +82,13 @@ export const LoginPage = observer(function LoginPage() {
8182
});
8283
return;
8384
}
85+
case "google": {
86+
loginDisposer.current = googleLogin(async authData => {
87+
globalStore.updateUserInfo(authData);
88+
pushHistory(RouteNameType.HomePage);
89+
});
90+
return;
91+
}
8492
case "wechat": {
8593
setWeChatLogin(true);
8694
return;
@@ -105,11 +113,19 @@ export const LoginPage = observer(function LoginPage() {
105113
function renderButtonList(): React.ReactNode {
106114
return (
107115
<>
108-
<LoginButton
109-
provider="wechat"
110-
text={i18n.t("login-wechat")}
111-
onLogin={handleLogin}
112-
/>
116+
{process.env.FLAT_REGION === "America" ? (
117+
<LoginButton
118+
provider="google"
119+
text={i18n.t("login-google")}
120+
onLogin={handleLogin}
121+
/>
122+
) : (
123+
<LoginButton
124+
provider="wechat"
125+
text={i18n.t("login-wechat")}
126+
onLogin={handleLogin}
127+
/>
128+
)}
113129
<LoginButton
114130
provider="github"
115131
text={i18n.t("login-github")}

‎desktop/renderer-app/src/stores/global-store.ts

+4
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ export class GlobalStore {
5252
this.userInfo = userInfo;
5353
};
5454

55+
public updateLastLoginCheck = (val: number | null): void => {
56+
this.lastLoginCheck = val;
57+
};
58+
5559
public updateToken = (
5660
config: Partial<
5761
Pick<

‎desktop/renderer-app/typings/global.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ declare namespace NodeJS {
2525
AGORA_APP_ID: string;
2626

2727
GITHUB_CLIENT_ID: string;
28+
GOOGLE_OAUTH_CLIENT_ID: string;
2829

2930
WECHAT_APP_ID: string;
3031
FLAT_SERVER_DOMAIN: string;

‎packages/flat-components/src/components/LoginPage/LoginButton/index.less

+18-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252

5353
.login-channel-github {
5454
margin-bottom: 12px;
55-
55+
5656
&.ant-btn {
5757
background: #24292e;
5858

@@ -64,3 +64,20 @@
6464
}
6565
}
6666
}
67+
68+
.login-channel-google {
69+
margin-bottom: 12px;
70+
71+
&.ant-btn {
72+
background: #fff;
73+
color: #1b1f22;
74+
border: 1px solid #595d60;
75+
76+
&:hover {
77+
border: 1px solid #1b1f22;
78+
}
79+
&:active {
80+
border: 1px solid #1b1f22;
81+
}
82+
}
83+
}

‎packages/flat-components/src/components/LoginPage/LoginButton/index.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ import React from "react";
33
import wechatSVG from "./icons/wechat.svg";
44
import agoraSVG from "./icons/agora.svg";
55
import githubSVG from "./icons/github.svg";
6+
import googleSVG from "./icons/google.svg";
67
import "./index.less";
78

8-
export type LoginButtonProviderType = "wechat" | "github" | "agora";
9+
export type LoginButtonProviderType = "wechat" | "github" | "agora" | "google";
910

1011
export type LoginButtonProps = {
1112
provider: LoginButtonProviderType;
@@ -17,6 +18,7 @@ const svgDict: Record<LoginButtonProviderType, string> = {
1718
wechat: wechatSVG,
1819
agora: agoraSVG,
1920
github: githubSVG,
21+
google: googleSVG,
2022
};
2123

2224
export const LoginButton: React.FC<LoginButtonProps> = ({ provider, onLogin, text }) => {

‎packages/flat-i18n/locales/en.json

+1
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@
110110
"join-and-book-by-room-uuid": "Can join and book by room ID",
111111
"copy": "Copy",
112112
"login-github": "Sign In with GitHub",
113+
"login-google": "Sign In with Google",
113114
"login-wechat": "Sign In with WeChat",
114115
"login-agora": "Sign In with Agora",
115116
"agree-terms": "Please agree to the terms of service first",

‎packages/flat-i18n/locales/zh-CN.json

+1
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@
112112
"copy": "复制",
113113
"login-wechat": "微信登录",
114114
"login-github": "GitHub 登录",
115+
"login-google": "Google 登录",
115116
"login-agora": "声网登录",
116117
"app-welcome": "欢迎使用 {{appName}}",
117118
"online-interaction-to-synchronize-ideas": "在线互动,让想法同步",

‎web/flat-web/src/api-middleware/flatServer/constants.ts

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export const FLAT_SERVER_VERSIONS = {
99
export const FLAT_SERVER_LOGIN = {
1010
AGORA_CALLBACK: `${FLAT_SERVER_VERSIONS.V1}/login/agora/callback`,
1111
GITHUB_CALLBACK: `${FLAT_SERVER_VERSIONS.V1}/login/github/callback?platform=web`,
12+
GOOGLE_CALLBACK: `${FLAT_SERVER_VERSIONS.V1}/login/google/callback`,
1213
WECHAT_CALLBACK: `${FLAT_SERVER_VERSIONS.V1}/login/weChat/web/callback`,
1314
} as const;
1415

‎web/flat-web/src/constants/process.ts

+4
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ export const GITHUB = Object.freeze({
2828
CLIENT_ID: process.env.GITHUB_CLIENT_ID,
2929
});
3030

31+
export const GOOGLE = Object.freeze({
32+
CLIENT_ID: process.env.GOOGLE_OAUTH_CLIENT_ID,
33+
});
34+
3135
export const FLAT_SERVER_DOMAIN = process.env.FLAT_SERVER_DOMAIN;
3236
export const FLAT_WEB_DOMAIN = process.env.FLAT_WEB_DOMAIN;
3337

‎web/flat-web/src/pages/HomePage/index.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,10 @@ export const HomePage = observer(function HomePage() {
3535

3636
try {
3737
await loginCheck();
38-
globalStore.lastLoginCheck = Date.now();
38+
globalStore.updateLastLoginCheck(Date.now());
3939
return true;
4040
} catch (e) {
41-
globalStore.lastLoginCheck = null;
41+
globalStore.updateLastLoginCheck(null);
4242
console.error(e);
4343
errorTips(e as Error);
4444
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { setAuthUUID, loginProcess } from "../../api-middleware/flatServer";
2+
import { v4 as uuidv4 } from "uuid";
3+
import { LoginExecutor } from "./utils";
4+
import { errorTips } from "../../components/Tips/ErrorTips";
5+
import { FLAT_SERVER_LOGIN } from "../../api-middleware/flatServer/constants";
6+
import { GOOGLE } from "../../constants/process";
7+
8+
// @TODO: migrate to new google login api before 2023
9+
// https://developers.google.com/identity/gsi/web
10+
export const googleLogin: LoginExecutor = onSuccess => {
11+
let timer = NaN;
12+
const authUUID = uuidv4();
13+
const scopes = ["openid", "https://www.googleapis.com/auth/userinfo.profile"];
14+
15+
function getGoogleURL(authUUID: string): string {
16+
const redirectURL = encodeURIComponent(FLAT_SERVER_LOGIN.GOOGLE_CALLBACK);
17+
const scope = encodeURIComponent(scopes.join(" "));
18+
return `https://accounts.google.com/o/oauth2/v2/auth?response_type=code&access_type=online&scope=${scope}&client_id=${GOOGLE.CLIENT_ID}&redirect_uri=${redirectURL}&state=${authUUID}`;
19+
}
20+
21+
void (async () => {
22+
try {
23+
await setAuthUUID(authUUID);
24+
} catch (err) {
25+
errorTips(err);
26+
return;
27+
}
28+
29+
void window.open(getGoogleURL(authUUID));
30+
31+
const googleLoginProcessRequest = async (): Promise<void> => {
32+
try {
33+
const data = await loginProcess(authUUID);
34+
35+
if (!data.name) {
36+
timer = window.setTimeout(googleLoginProcessRequest, 2000);
37+
return;
38+
}
39+
40+
onSuccess(data);
41+
} catch (err) {
42+
errorTips(err);
43+
}
44+
};
45+
46+
void googleLoginProcessRequest();
47+
})();
48+
49+
return () => {
50+
window.clearTimeout(timer);
51+
};
52+
};

‎web/flat-web/src/pages/LoginPage/index.tsx

+33-9
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
import "./style.less";
22

33
import React, { useCallback, useContext, useEffect, useRef, useState } from "react";
4+
import { useTranslation } from "react-i18next";
45
import { observer } from "mobx-react-lite";
6+
import { message } from "antd";
57
import { LoginButton, LoginButtonProviderType, LoginPanel } from "flat-components";
68
import { LoginDisposer } from "./utils";
79
import { githubLogin } from "./githubLogin";
10+
import { WeChatLogin } from "./WeChatLogin";
11+
import { agoraLogin } from "./agoraLogin";
12+
import { googleLogin } from "./googleLogin";
813
import { RouteNameType, usePushHistory, useURLParams } from "../../utils/routes";
914
import { GlobalStoreContext } from "../../components/StoreProvider";
10-
import { WeChatLogin } from "./WeChatLogin";
1115
import { joinRoomHandler } from "../utils/join-room-handler";
1216
import { PRIVACY_URL, PRIVACY_URL_CN, SERVICE_URL, SERVICE_URL_CN } from "../../constants/process";
13-
import { useTranslation } from "react-i18next";
14-
import { agoraLogin } from "./agoraLogin";
15-
import { message } from "antd";
1617
import { useSafePromise } from "../../utils/hooks/lifecycle";
1718
import { agoraSSOLoginCheck, loginCheck } from "../../api-middleware/flatServer";
1819

@@ -96,6 +97,21 @@ export const LoginPage = observer(function LoginPage() {
9697
});
9798
return;
9899
}
100+
case "google": {
101+
loginDisposer.current = googleLogin(async authData => {
102+
globalStore.updateUserInfo(authData);
103+
if (!roomUUID) {
104+
pushHistory(RouteNameType.HomePage);
105+
return;
106+
}
107+
if (globalStore.isTurnOffDeviceTest) {
108+
await joinRoomHandler(roomUUID, pushHistory);
109+
} else {
110+
pushHistory(RouteNameType.DevicesTestPage, { roomUUID });
111+
}
112+
});
113+
return;
114+
}
99115
case "wechat": {
100116
setWeChatLogin(true);
101117
return;
@@ -131,11 +147,19 @@ export const LoginPage = observer(function LoginPage() {
131147
} else {
132148
return (
133149
<>
134-
<LoginButton
135-
provider="wechat"
136-
text={i18n.t("login-wechat")}
137-
onLogin={handleLogin}
138-
/>
150+
{process.env.FLAT_REGION === "America" ? (
151+
<LoginButton
152+
provider="google"
153+
text={i18n.t("login-google")}
154+
onLogin={handleLogin}
155+
/>
156+
) : (
157+
<LoginButton
158+
provider="wechat"
159+
text={i18n.t("login-wechat")}
160+
onLogin={handleLogin}
161+
/>
162+
)}
139163
<LoginButton
140164
provider="github"
141165
text={i18n.t("login-github")}

‎web/flat-web/src/stores/GlobalStore.ts

+4
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ export class GlobalStore {
6464
this.agoraSSOLoginID = val ?? null;
6565
};
6666

67+
public updateLastLoginCheck = (val: number | null): void => {
68+
this.lastLoginCheck = val;
69+
};
70+
6771
public updateToken = (
6872
config: Partial<
6973
Pick<

‎web/flat-web/typings/global.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ declare namespace NodeJS {
2222
AGORA_OAUTH_CLIENT_ID: string;
2323

2424
GITHUB_CLIENT_ID: string;
25+
GOOGLE_OAUTH_CLIENT_ID: string;
2526

2627
WECHAT_APP_ID: string;
2728
FLAT_SERVER_DOMAIN: string;

0 commit comments

Comments
 (0)
Please sign in to comment.