Feature/google oauth (#106)

google oauth
---------

Co-authored-by: Igor Lobanov <igor.lobanov@onetwotrip.com>
This commit is contained in:
Ilya Y 2023-11-08 21:12:55 +03:00 committed by GitHub
parent 2679b2c873
commit 0e9f0b0682
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 23 additions and 44 deletions

View File

@ -3,7 +3,6 @@ from hashlib import sha256
from jwt import DecodeError, ExpiredSignatureError from jwt import DecodeError, ExpiredSignatureError
from passlib.hash import bcrypt from passlib.hash import bcrypt
from sqlalchemy import or_
from auth.jwtcodec import JWTCodec from auth.jwtcodec import JWTCodec
from auth.tokenstorage import TokenStorage from auth.tokenstorage import TokenStorage
@ -11,7 +10,6 @@ from auth.tokenstorage import TokenStorage
# from base.exceptions import InvalidPassword, InvalidToken # from base.exceptions import InvalidPassword, InvalidToken
from base.orm import local_session from base.orm import local_session
from orm import User from orm import User
from validations.auth import AuthInput
class Password: class Password:
@ -65,20 +63,13 @@ class Identity:
return user return user
@staticmethod @staticmethod
def oauth(inp: AuthInput) -> User: def oauth(inp) -> User:
with local_session() as session: with local_session() as session:
user = ( user = session.query(User).filter(User.email == inp["email"]).first()
session.query(User)
.filter(or_(User.oauth == inp["oauth"], User.email == inp["email"]))
.first()
)
if not user: if not user:
user = User.create(**inp) user = User.create(**inp, emailConfirmed=True)
if not user.oauth:
user.oauth = inp["oauth"]
session.commit() session.commit()
user = User(**user.dict())
return user return user
@staticmethod @staticmethod

View File

@ -33,16 +33,25 @@ oauth.register(
oauth.register( oauth.register(
name="google", name="google",
client_id=OAUTH_CLIENTS["GOOGLE"]["id"], # client_id=OAUTH_CLIENTS["GOOGLE"]["id"],
client_secret=OAUTH_CLIENTS["GOOGLE"]["key"], # client_secret=OAUTH_CLIENTS["GOOGLE"]["key"],
client_id="648983473866-2hd6v2eqqk6hhqabfhuqq2slb2fkfvve.apps.googleusercontent.com",
client_secret="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",
server_metadata_url="https://accounts.google.com/.well-known/openid-configuration", server_metadata_url="https://accounts.google.com/.well-known/openid-configuration",
client_kwargs={"scope": "openid email profile"}, client_kwargs={"scope": "openid email profile"},
authorize_state="test",
) )
async def google_profile(client, request, token): async def google_profile(client, request, token):
profile = await client.parse_id_token(request, token) userinfo = token["userinfo"]
profile["id"] = profile["sub"]
profile = {"name": userinfo["name"], "email": userinfo["email"], "id": userinfo["sub"]}
if userinfo["picture"]:
userpic = userinfo["picture"].replace("=s96", "=s600")
profile["userpic"] = userpic
return profile return profile
@ -67,7 +76,8 @@ async def oauth_login(request):
provider = request.path_params["provider"] provider = request.path_params["provider"]
request.session["provider"] = provider request.session["provider"] = provider
client = oauth.create_client(provider) client = oauth.create_client(provider)
redirect_uri = "https://v2.discours.io/oauth-authorize" # redirect_uri = "http://v2.discours.io/oauth-authorize"
redirect_uri = "http://localhost:8080/oauth-authorize"
return await client.authorize_redirect(request, redirect_uri) return await client.authorize_redirect(request, redirect_uri)
@ -82,6 +92,7 @@ async def oauth_authorize(request):
"oauth": user_oauth_info, "oauth": user_oauth_info,
"email": profile["email"], "email": profile["email"],
"username": profile["name"], "username": profile["name"],
"userpic": profile["userpic"],
} }
user = Identity.oauth(user_input) user = Identity.oauth(user_input)
session_token = await TokenStorage.create_session(user) session_token = await TokenStorage.create_session(user)

View File

@ -16,7 +16,6 @@ from auth.oauth import oauth_authorize, oauth_login
from base.redis import redis from base.redis import redis
from base.resolvers import resolvers from base.resolvers import resolvers
from orm import init_tables from orm import init_tables
from resolvers.auth import confirm_email_handler
from resolvers.upload import upload_handler from resolvers.upload import upload_handler
from services.main import storages_init from services.main import storages_init
from services.notifications.notification_service import notification_service from services.notifications.notification_service import notification_service
@ -71,10 +70,8 @@ async def shutdown():
routes = [ routes = [
# Route("/messages", endpoint=sse_messages),
Route("/oauth/{provider}", endpoint=oauth_login), Route("/oauth/{provider}", endpoint=oauth_login),
Route("/oauth-authorize", endpoint=oauth_authorize), Route("/oauth-authorize", endpoint=oauth_authorize),
Route("/confirm/{token}", endpoint=confirm_email_handler),
Route("/upload", endpoint=upload_handler, methods=["POST"]), Route("/upload", endpoint=upload_handler, methods=["POST"]),
Route("/subscribe/{user_id}", endpoint=sse_subscribe_handler), Route("/subscribe/{user_id}", endpoint=sse_subscribe_handler),
] ]

View File

@ -3,7 +3,7 @@ aioredis~=2.0.1
alembic==1.11.3 alembic==1.11.3
ariadne>=0.17.0 ariadne>=0.17.0
asyncio~=3.4.3 asyncio~=3.4.3
authlib>=1.1.0 authlib==1.2.1
bcrypt>=4.0.0 bcrypt>=4.0.0
beautifulsoup4~=4.11.1 beautifulsoup4~=4.11.1
boto3~=1.28.2 boto3~=1.28.2

View File

@ -5,7 +5,6 @@ from datetime import datetime, timezone
from urllib.parse import quote_plus from urllib.parse import quote_plus
from graphql.type import GraphQLResolveInfo from graphql.type import GraphQLResolveInfo
from starlette.responses import RedirectResponse
from transliterate import translit from transliterate import translit
from auth.authenticate import login_required from auth.authenticate import login_required
@ -14,17 +13,11 @@ from auth.email import send_auth_email
from auth.identity import Identity, Password from auth.identity import Identity, Password
from auth.jwtcodec import JWTCodec from auth.jwtcodec import JWTCodec
from auth.tokenstorage import TokenStorage from auth.tokenstorage import TokenStorage
from base.exceptions import ( from base.exceptions import InvalidPassword, InvalidToken, ObjectNotExist, Unauthorized
BaseHttpException,
InvalidPassword,
InvalidToken,
ObjectNotExist,
Unauthorized,
)
from base.orm import local_session from base.orm import local_session
from base.resolvers import mutation, query from base.resolvers import mutation, query
from orm import Role, User from orm import Role, User
from settings import FRONTEND_URL, SESSION_TOKEN_HEADER from settings import SESSION_TOKEN_HEADER
@mutation.field("getSession") @mutation.field("getSession")
@ -64,19 +57,6 @@ async def confirm_email(_, info, token):
return {"error": "email is not confirmed"} return {"error": "email is not confirmed"}
async def confirm_email_handler(request):
token = request.path_params["token"] # one time
request.session["token"] = token
res = await confirm_email(None, {}, token)
print("[resolvers.auth] confirm_email request: %r" % request)
if "error" in res:
raise BaseHttpException(res["error"])
else:
response = RedirectResponse(url=FRONTEND_URL)
response.set_cookie("token", res["token"]) # session token
return response
def create_user(user_dict): def create_user(user_dict):
user = User(**user_dict) user = User(**user_dict)
with local_session() as session: with local_session() as session:

View File

@ -23,7 +23,7 @@ for provider in OAUTH_PROVIDERS:
"id": environ.get(provider + "_OAUTH_ID"), "id": environ.get(provider + "_OAUTH_ID"),
"key": environ.get(provider + "_OAUTH_KEY"), "key": environ.get(provider + "_OAUTH_KEY"),
} }
FRONTEND_URL = environ.get("FRONTEND_URL") or "http://localhost:3000" FRONTEND_URL = environ.get("FRONTEND_URL") or "https://localhost:3000"
SHOUTS_REPO = "content" SHOUTS_REPO = "content"
SESSION_TOKEN_HEADER = "Authorization" SESSION_TOKEN_HEADER = "Authorization"