async def handle_spotify_callback(query_params: Dict, config: Config): if query_params.get("error"): raise err.failed_dependency( "Please allow access to your Spotify account to proceed") token = query_params.get("state") if not token: raise err.bad_request( "'state' query parameter required but not provided") try: claims = jwt.get_claims_from_jwt(token, config.jwt_key(), jwt.Aud.API) user_id = int(claims["sub"]) except Exception as e: logger.debug("Failed to decode state as JWT: %s", e) raise err.bad_request("Invalid state from Spotify callback") code = query_params.get("code") if not code: raise err.failed_dependency( "Spotify authorization failed; no valid code returned") request = { "grant_type": "authorization_code", "code": code, "redirect_uri": config.spotify_redirect(), "client_id": config.spotify_client_id(), "client_secret": config.spotify_secret() } try: session = config.get_session() tokens_response = await config.get_spotify_api().request_tokens(request ) access_token = tokens_response["access_token"] refresh_token = tokens_response["refresh_token"] created_at = datetime.utcnow() expires_in = tokens_response["expires_in"] spotify_user_data = await config.get_spotify_api().spotify_user_data( access_token) spotify_user_id = spotify_user_data["id"] except Exception as e: logger.exception("Failed to authorize user(id=%s): %s", user_id, e) raise err.internal_server_error( "Something went wrong logging in with Spotify") try: async with config.get_database_connection() as db: await spotify_token.SpotifyTokenPersistence(db).upsert_token( user_id, spotify_user_id, access_token, refresh_token, created_at, expires_in) except Exception as e: logger.exception("Failed to upsert Spotify token for user(id=%s): %s", user_id, e) raise err.internal_server_error() return {"success": True}
def _handle_token_result(token_result: Union[str, GetTokenError]) -> str: if isinstance(token_result, GetTokenError): if token_result == GetTokenError.EXPIRED: raise err.failed_dependency("Spotify token has expired") elif token_result == GetTokenError.NOT_AUTHED: raise err.failed_dependency( "User must be authorized with Spotify to perform this operation" ) elif token_result == GetTokenError.API_ERROR: raise err.internal_server_error(token_result.value) else: raise err.internal_server_error() return token_result
async def register_user(first_name: str, last_name: str, email: str, password: str, config: Config): if not check_password(password): raise err.bad_request( "Please provide a password at least 8 characters long, containing letters and symbols" ) password_hash = bcrypt.hashpw(password.encode(), bcrypt.gensalt()) try: async with config.get_database_connection() as db: user_persistence = users.UsersPersistence(db) created_user_id = await user_persistence.create_user( first_name, last_name, email, password_hash) user = await user_persistence.get_user_by_id(created_user_id) except IntegrityError as e: logger.debug("IntegrityError registering user with email %s: %s", email, e) raise err.bad_request("Email address is already in use") except Exception as e: logger.exception("Failed to get user with email %s from db: %s", email, e) raise err.internal_server_error() return { "token": jwt.generate_jwt(user["user_id"], jwt.Aud.AUTH, config.jwt_key()) }
async def login(email: str, password: str, config: Config) -> Dict: """ Get user from DB, check password hash matches and return a JWT token if so """ try: async with config.get_database_connection() as db: user = await users.UsersPersistence(db).get_user_by_email(email) except Exception as e: logger.exception("Failed to get user with email %s from db: %s", email, e) raise err.internal_server_error() auth_failed = err.unauthorized( "Failed to log you in with the provided credentials; please try again") if not user: logger.debug("User not found with email %s", email) raise auth_failed if not bcrypt.checkpw(password.encode(), user["password_hash"]): logger.debug("Incorrect password for user(id=%s)", user["user_id"]) raise auth_failed return { "token": jwt.generate_jwt(user["user_id"], jwt.Aud.AUTH, config.jwt_key()) }
async def me(user_id: int, config: Config) -> Dict: try: async with config.get_database_connection() as db: get_user = users.UsersPersistence(db).get_user_by_id(user_id) get_joined_rooms = rooms.RoomPersistence( db).get_joined_rooms_by_user(user_id) get_token = spotify.get_valid_token_for_user(user_id, config) (user, joined_rooms, token) = await asyncio.gather(get_user, get_joined_rooms, get_token, return_exceptions=False) except Exception as e: logger.exception("Failed to get user(id=%s) from db: %s", user_id, e) raise err.internal_server_error() del user["password_hash"] user["rooms"] = joined_rooms user["authed_with_spotify"] = isinstance(token, str) return user