Skip to content

Commit

Permalink
feat: use GitHub API to get user email
Browse files Browse the repository at this point in the history
fix: missing items in bind_user & fix logger

Signed-off-by: jingfelix <[email protected]>

feat: get user email by GitHub API

Signed-off-by: jingfelix <[email protected]>
  • Loading branch information
jingfelix committed Jan 2, 2024
1 parent 13eb33b commit 27f00c5
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 44 deletions.
3 changes: 3 additions & 0 deletions server/model/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@ class BindUser(Base):
email = db.Column(db.String(128), nullable=True, comment="邮箱")
name = db.Column(db.String(128), nullable=True, comment="用户名")
avatar = db.Column(db.String(128), nullable=True, comment="头像")
access_token = db.Column(
db.String(128), nullable=True, comment="GitHub access_token"
)
extra = db.Column(
JSONStr(1024), nullable=True, server_default=text("'{}'"), comment="用户其他字段"
)
Expand Down
13 changes: 8 additions & 5 deletions server/routes/github.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import logging
import os

from app import app
from flask import Blueprint, abort, redirect, request, session
from model.schema import Team
from utils.auth import authenticated
from utils.github.common import get_installation_token, get_jwt, verify_github_signature
from utils.github.application import (
get_installation_token,
get_jwt,
verify_github_signature,
)
from utils.user import register

bp = Blueprint("github", __name__, url_prefix="/api/github")
Expand All @@ -25,7 +28,7 @@ def github_install():
)


@bp.route("/register", methods=["GET"])
@bp.route("/oauth", methods=["GET"])
def github_register():
"""GitHub OAuth register.
Expand Down Expand Up @@ -57,9 +60,9 @@ def github_hook():

x_github_event = request.headers.get("x-github-event", None)

logging.info(x_github_event)
app.logger.info(x_github_event)

logging.debug(request.json)
app.logger.debug(request.json)

return "Receive Success!"

Expand Down
69 changes: 69 additions & 0 deletions server/utils/github/account.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import httpx
from app import app


def get_user_info(access_token: str) -> dict | None:
"""Get user info by access token.
Args:
access_token (str): The user access token.
Returns:
dict: User info.
"""

with httpx.Client() as client:
response = client.get(
"https://api.github.com/user",
headers={
"Accept": "application/vnd.github.v3+json",
"Authorization": f"token {access_token}",
},
)
if response.status_code != 200:
app.logger.debug(f"Failed to get user info. {response.text}")
return None

user_info = response.json()
return user_info

app.logger.debug("Failed to get user info.")
return None


def get_email(access_token: str) -> str | None:
"""Get user email by access token.
Args:
access_token (str): The user access token.
Returns:
str: User email.
"""

with httpx.Client() as client:
response = client.get(
"https://api.github.com/user/emails",
headers={
"Accept": "application/vnd.github.v3+json",
"Authorization": f"Bearer {access_token}",
"X-GitHub-Api-Version": "2022-11-28",
},
)
if response.status_code != 200:
app.logger.debug(f"Failed to get user email. {response.text}")
return None

user_emails = response.json()
if len(user_emails) == 0:
app.logger.debug("Failed to get user email.")
return None

for user_email in user_emails:
if user_email["primary"]:
return user_email["email"]

return user_emails[0]["email"]

app.logger.debug("Failed to get user email.")
return None
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import hashlib
import hmac
import logging
import os
import time
from functools import wraps
from urllib.parse import parse_qs

import httpx
from app import app
from flask import abort, request
from jwt import JWT, jwk_from_pem

Expand Down Expand Up @@ -63,7 +63,7 @@ def get_installation_token(jwt: str, installation_id: str) -> str | None:
},
)
if response.status_code != 200:
logging.debug(f"Failed to get installation token. {response.text}")
app.logger.debug(f"Failed to get installation token. {response.text}")
return None

installation_token = response.json().get("token", None)
Expand Down Expand Up @@ -92,12 +92,13 @@ def oauth_by_code(code: str) -> dict | None:
},
)
if response.status_code != 200:
app.logger.debug(f"Failed to get access token. {response.text}")
return None

try:
oauth_info = parse_qs(response.text)
except Exception as e:
logging.debug(e)
app.logger.debug(e)
return None

return oauth_info
Expand Down Expand Up @@ -132,42 +133,14 @@ def wrapper(*args, **kwargs):
)
expected_signature = "sha256=" + hash_object.hexdigest()

logging.debug(f"{expected_signature} {signature}")
app.logger.debug(f"{expected_signature} {signature}")

if not hmac.compare_digest(expected_signature, signature):
logging.debug("Invalid signature.")
app.logger.debug("Invalid signature.")
abort(403, "Invalid signature.")

return func(*args, **kwargs)

return wrapper

return decorator


def get_user_info(access_token: str):
"""Get user info by access token.
Args:
access_token (str): The user access token.
Returns:
dict: User info.
"""

with httpx.Client() as client:
response = client.get(
"https://api.github.com/user",
headers={
"Accept": "application/vnd.github.v3+json",
"Authorization": f"token {access_token}",
},
)
if response.status_code != 200:
logging.debug(f"Failed to get user info. {response.text}")
return None

user_info = response.json()
return user_info

return None
18 changes: 12 additions & 6 deletions server/utils/user.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from app import app, db
from flask import abort
from model.schema import BindUser, ObjID, User
from utils.github.common import get_user_info, oauth_by_code
from utils.github.account import get_email, get_user_info
from utils.github.application import oauth_by_code


def register(code: str) -> str | None:
Expand All @@ -10,6 +12,8 @@ def register(code: str) -> str | None:
"""

oauth_info = oauth_by_code(code) # 获取 access token
if oauth_info is None:
abort(500)

access_token = oauth_info.get("access_token", None)[0] # 这里要考虑取哪个,为什么会有多个?

Expand All @@ -23,26 +27,28 @@ def register(code: str) -> str | None:
if user is not None:
return user.id

email = get_email(access_token)

new_user = User(
id=ObjID.new_id(),
github_id=github_id,
email=user_info.get(
"email", None
), # 这里的邮箱其实是公开邮箱,可能会获取不到 TODO: 换成使用用户邮箱 API 来获取
email=email, # 这里的邮箱其实是公开邮箱,可能会获取不到 TODO: 换成使用用户邮箱 API 来获取
name=user_info.get("login", None),
avatar=user_info.get("avatar_url", None),
extra=user_info,
)

db.session.add(new_user)
db.session.commit()
db.session.flush()

new_bind_user = BindUser(
id=ObjID.new_id(),
user_id=new_user.id,
platform="github",
email=user_info.get("email", None),
email=email,
name=user_info.get("login", None),
avatar=user_info.get("avatar_url", None),
access_token=access_token,
extra=oauth_info,
)

Expand Down

0 comments on commit 27f00c5

Please sign in to comment.