-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
5adcac0
commit 72c0d07
Showing
9 changed files
with
207 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
from typing import Dict | ||
from customs.exceptions import UnauthorizedException | ||
from customs.strategies.oauth2_strategy import OAuth2Strategy | ||
|
||
from requests_oauthlib import OAuth2Session # type: ignore | ||
from requests_oauthlib.compliance_fixes import facebook_compliance_fix # type: ignore | ||
|
||
|
||
class FacebookStrategy(OAuth2Strategy): | ||
"""Authentication using Facebook as an OAuth2 provider.""" | ||
|
||
name = "facebook" | ||
scopes = ["email", "public_profile"] | ||
fields = ["id", "name", "first_name", "last_name", "picture", "email"] | ||
|
||
authorization_base_url = "https://www.facebook.com/dialog/oauth" | ||
token_url = "https://graph.facebook.com/oauth/access_token" | ||
refresh_url = "https://graph.facebook.com/oauth/access_token" | ||
user_profile_endpoint = "https://graph.facebook.com/me?" | ||
|
||
def get_user_info(self) -> Dict: | ||
"""Method to get user info for the logged in user. | ||
Raises: | ||
UnauthorizedException: When the user is not authenticated | ||
Returns: | ||
Dict: The user profile | ||
""" | ||
|
||
try: | ||
|
||
# Get the token | ||
token = self.token | ||
|
||
# Helper method to update the token in the session | ||
def token_updater(token): | ||
self.token = token | ||
|
||
# Get a session with auto-refresh of the token | ||
client = OAuth2Session( | ||
self.client_id, | ||
token=token, | ||
auto_refresh_kwargs={ | ||
"client_id": self.client_id, | ||
"client_secret": self.client_secret, | ||
}, | ||
auto_refresh_url=self.refresh_url, | ||
token_updater=token_updater, | ||
) | ||
|
||
if self.name == "facebook": | ||
facebook_compliance_fix(client) | ||
|
||
# Return the user info | ||
return client.get( | ||
self.user_profile_endpoint + "fields=" + ",".join(self.fields) | ||
).json() | ||
|
||
except Exception: | ||
raise UnauthorizedException() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
import os | ||
|
||
from typing import Any, Dict | ||
from flask import Flask | ||
from flask.templating import render_template | ||
|
||
from customs import Customs | ||
from customs.helpers import set_redirect_url | ||
from customs.strategies import FacebookStrategy | ||
from customs.exceptions import UnauthorizedException | ||
|
||
|
||
# App credentials for Facebook | ||
client_id = os.environ.get("FACEBOOK_CLIENT_ID") | ||
client_secret = os.environ.get("FACEBOOK_CLIENT_SECRET") | ||
|
||
# Create the Flask app | ||
app = Flask(__name__) | ||
app.secret_key = "d2cc6f0a-0d9e-414c-a3ca-6b75455d6332" | ||
|
||
# Create customs to protect the app, no need for sessions as we're using JWT tokens | ||
customs = Customs(app, unauthorized_redirect_url="/login") | ||
|
||
# Mock in-memory database (NOTE: Will forget everything on restart!) | ||
DATABASE: Dict[str, Dict] = {} | ||
|
||
|
||
# Create an implementation of the strategy (tell the strategy how to interact with our database for example) | ||
class FacebookAuthentication(FacebookStrategy): | ||
|
||
scopes = ["email", "public_profile"] | ||
fields = ["id", "name", "first_name", "last_name", "picture", "email"] # Which fields to fetch from the users profile | ||
|
||
def get_or_create_user(self, user: Dict) -> Dict: | ||
|
||
user_id = str(user.get("id")) | ||
|
||
# Create the user if not in the database (so registration is open to everyone) | ||
if user_id not in DATABASE: | ||
DATABASE[user_id] = user | ||
|
||
# Return the user from the database | ||
return DATABASE[user_id] | ||
|
||
def serialize_user(self, user: Any) -> Dict: | ||
|
||
# Keep only the user ID on the session for speed and safety | ||
user_id = str(user.get("id")) | ||
return {"id": user_id} | ||
|
||
def deserialize_user(self, data: Dict) -> Any: | ||
|
||
# Get the user ID from the session | ||
user_id = str(data.get("id")) | ||
|
||
# Conver the ID back to the user data from the database | ||
if user_id is not None and user_id in DATABASE: | ||
return {"id": str(data.get("id")), **DATABASE[str(data.get("id"))]} | ||
else: | ||
raise UnauthorizedException() | ||
|
||
|
||
# Create an instance of the strategy | ||
facebook = FacebookAuthentication( | ||
client_id=client_id, | ||
client_secret=client_secret, | ||
enable_insecure=True, # For testing purposes only, don't use in production! | ||
) | ||
|
||
# ----------------------- # | ||
# Define some open routes # | ||
# ----------------------- # | ||
|
||
|
||
# Open to everyone | ||
@app.route("/") | ||
def index(): | ||
return render_template("index.html") | ||
|
||
|
||
@app.route("/login") | ||
def login(): | ||
|
||
# Store the URL of the page that redirected here and store | ||
# it so we can redirect to it after authentication | ||
set_redirect_url() | ||
return render_template("login.html") | ||
|
||
|
||
# ------------------------------ # | ||
# Define some (protected) routes # | ||
# ------------------------------ # | ||
|
||
|
||
@app.route("/profile") | ||
@customs.protect(strategies=[facebook]) | ||
def profile(user: Dict): | ||
print(user) | ||
return render_template("profile.html", user=user) | ||
|
||
|
||
if __name__ == "__main__": | ||
app.run() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
<html> | ||
<body> | ||
<h1>Home</h1> | ||
<p><a href="/profile">Profile</a></p> | ||
<p><a href="/auth/facebook/login">Facebook login</a></p> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
<html> | ||
<body> | ||
<h1>Login</h1> | ||
<a href="/auth/facebook/login">Facebook authentication</a> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<html> | ||
<body> | ||
<h1>Profile</h1> | ||
<table> | ||
<tbody> | ||
<tr> | ||
<td><img src="{{ user.picture.data.url }}"></td> | ||
<td>{{ user.name }}</td> | ||
<td>{{ user.email }}</td> | ||
</tr> | ||
</tbody> | ||
</table> | ||
</body> | ||
</html> |