-
Notifications
You must be signed in to change notification settings - Fork 10
Add google authentication #108
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,3 +15,4 @@ node_modules/ | |
| .vscode | ||
| *.DS_Store | ||
| kernelboard/static/v2 | ||
| uv.lock | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| const GoogleIconSvg = | ||
| "https://cdn.jsdelivr.net/gh/simple-icons/simple-icons/icons/google.svg"; | ||
|
|
||
| export function GoogleIcon({ size = 18 }: { size?: number }) { | ||
| return ( | ||
| <img | ||
| src={GoogleIconSvg} | ||
| alt="Google" | ||
| width={size} | ||
| height={size} | ||
| style={{ display: "block" }} | ||
| /> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,6 +12,7 @@ import { ConstrainedContainer } from "../../components/app-layout/ConstrainedCon | |
| import { useSearchParams } from "react-router-dom"; | ||
| import { useCallback, useEffect, useState } from "react"; | ||
| import { DiscordIcon } from "../../components/common/DiscordDefaultIcon"; | ||
| import { GoogleIcon } from "../../components/common/GoogleIcon"; | ||
| import AlertBar from "../../components/alert/AlertBar"; | ||
|
|
||
| export default function Login() { | ||
|
|
@@ -20,6 +21,10 @@ export default function Login() { | |
| const loginDiscrodHref = `/api/auth/discord?next=/v2/`; | ||
| return loginDiscrodHref; | ||
| }; | ||
| const googleLoginUrl = () => { | ||
| const loginGoogleHref = `/api/auth/google?next=/v2/`; | ||
| return loginGoogleHref; | ||
| }; | ||
| const error = params.get("error"); | ||
|
|
||
| const [err, setErr] = useState<{ | ||
|
|
@@ -102,6 +107,15 @@ export default function Login() { | |
| > | ||
| Continue with Discord | ||
| </Button> | ||
| <Button | ||
| variant="outlined" | ||
| href={googleLoginUrl()} | ||
| size="small" | ||
| sx={{ borderRadius: 999 }} | ||
| startIcon={<GoogleIcon />} | ||
| > | ||
| Continue with Gmail | ||
msaroufim marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| </Button> | ||
|
Comment on lines
+110
to
+118
|
||
| <Divider>or</Divider> | ||
| <Stack spacing={1}> | ||
| <Typography | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -66,7 +66,18 @@ def providers(): | |
| "identity": lambda json: json["id"], | ||
| }, | ||
| "scopes": ["identify"], | ||
| } | ||
| }, | ||
| "google": { | ||
| "client_id": os.getenv("GOOGLE_CLIENT_ID"), | ||
| "client_secret": os.getenv("GOOGLE_CLIENT_SECRET"), | ||
| "authorize_url": "https://accounts.google.com/o/oauth2/v2/auth", | ||
| "token_url": "https://oauth2.googleapis.com/token", | ||
| "userinfo": { | ||
| "url": "https://www.googleapis.com/oauth2/v2/userinfo", | ||
| "identity": lambda json: json["id"], | ||
| }, | ||
| "scopes": ["openid", "email", "profile"], | ||
| }, | ||
|
Comment on lines
+70
to
+80
|
||
| } | ||
|
|
||
|
|
||
|
|
@@ -86,6 +97,35 @@ def _discord_avatar_url(uid: str, avatar_hash: str | None) -> str | None: | |
| return f"https://cdn.discordapp.com/avatars/{uid}/{avatar_hash}.{ext}" | ||
|
|
||
|
|
||
| def _google_avatar_url(picture_url: str | None) -> str | None: | ||
| """ | ||
| Return Google's picture URL directly (already a full URL). | ||
| """ | ||
| return picture_url if picture_url else None | ||
|
|
||
|
|
||
| def _get_username_from_provider(provider: str, data: dict) -> str: | ||
| """ | ||
| Extract username from provider-specific user data. | ||
| """ | ||
| if provider == "discord": | ||
| return data.get("global_name") or data.get("username") or "unknown" | ||
| elif provider == "google": | ||
| return data.get("name") or data.get("email", "").split("@")[0] or "unknown" | ||
| return "unknown" | ||
|
|
||
|
|
||
| def _get_avatar_url_from_provider(provider: str, identity: str, data: dict) -> str | None: | ||
| """ | ||
| Extract avatar URL from provider-specific user data. | ||
| """ | ||
| if provider == "discord": | ||
| return _discord_avatar_url(identity, data.get("avatar")) | ||
| elif provider == "google": | ||
| return _google_avatar_url(data.get("picture")) | ||
| return None | ||
|
|
||
|
Comment on lines
+107
to
+127
|
||
|
|
||
| # ----- Routes ----- | ||
|
|
||
|
|
||
|
|
@@ -235,11 +275,11 @@ def callback(provider: str): | |
|
|
||
| data = me_res.json() or {} | ||
| identity = provider_data["userinfo"]["identity"](data) | ||
| username = data.get("global_name") or data.get("username") or "unknown" | ||
| username = _get_username_from_provider(provider, data) | ||
|
|
||
| # 4) Stash display-only info (safe for SPA header) | ||
| session["display_name"] = username | ||
| session["avatar_url"] = _discord_avatar_url(identity, data.get("avatar")) | ||
| session["avatar_url"] = _get_avatar_url_from_provider(provider, identity, data) | ||
|
|
||
| # ensure user exists and has web_auth_id | ||
| # if not, update the user with the new token | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think uv.lock should be in git