Skip to content

Commit 032ebec

Browse files
skanctc00kiemon5ter
authored andcommitted
Introduce three configuration options for the Facebook OAuth backend
Introduce three configuration options for the Facebook backend The Facebook backend does not request any scopes from Facebook. As a result, the default scope (public_profile) is assumed and the user is asked to give permission only for that. This has the effect that even though in the query to the Facebook graph API we might be asking for the user's e-mail, the graph API will return only the user data that are part of the public profile. Furthermore, if a user has given permission for some scopes, then the the user response is remembered by Facebook and even if the application changes its configuration and requests for more scopes, Facebook will not take this into account, unless the 'auth_type' parameter is set to 'rerequest' This commit introduces three new configuration options: - 'auth_type' expects list of auth_types. If none is provided, then the 'auth_type' parameter will not be sent to the authorization_endpoint. - 'scope' expects a list of scopes. If none is provided, then the scope element will not be added and 'public_profile' will be assumed (as it happens now) - 'graph_endpoint' expects the endpoint for the Facebook graph API. Up to now the value of the graph_endpoint was hardcoded in the actual code of the backend. In order to retain backwards compatibility if the configuration option for the 'graph_endpoint' is not set, then it is assumed to have the value that used to be hardcoded.
1 parent 79ca3d2 commit 032ebec

File tree

2 files changed

+47
-16
lines changed

2 files changed

+47
-16
lines changed

example/plugins/backends/facebook_backend.yaml.example

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,16 @@ config:
55
base_url: <base_url>
66
client_config:
77
client_id: <client_id>
8+
# See https://developers.facebook.com/docs/facebook-login for
9+
# information on valid values for auth_type
10+
auth_type: []
11+
scope: [public_profile, email]
812
fields: [id, name, first_name, last_name, middle_name, picture, email, verified, gender, timezone, locale, updated_time]
913
response_type: code
10-
server_info: {authorization_endpoint: 'https://www.facebook.com/dialog/oauth', token_endpoint: 'https://graph.facebook.com/v2.5/oauth/access_token'}
14+
server_info:
15+
authorization_endpoint: 'https://www.facebook.com/dialog/oauth'
16+
token_endpoint: 'https://graph.facebook.com/v3.3/oauth/access_token'
17+
graph_endpoint: 'https://graph.facebook.com/v3.3/me'
1118
entity_info:
1219
organization:
1320
display_name:

src/satosa/backends/oauth.py

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -74,15 +74,19 @@ def start_auth(self, context, internal_request, get_state=stateID):
7474
:type internal_request: satosa.internal.InternalData
7575
:rtype satosa.response.Redirect
7676
"""
77-
oauth_state = get_state(self.config["base_url"], rndstr().encode())
78-
79-
state_data = dict(state=oauth_state)
80-
context.state[self.name] = state_data
81-
82-
request_args = {"redirect_uri": self.redirect_url, "state": oauth_state}
77+
request_args = self.get_request_args(get_state=get_state)
78+
context.state[self.name] = {"state": request_args["state"]}
8379
cis = self.consumer.construct_AuthorizationRequest(request_args=request_args)
8480
return Redirect(cis.request(self.consumer.authorization_endpoint))
8581

82+
def get_request_args(self, get_state=stateID):
83+
oauth_state = get_state(self.config["base_url"], rndstr().encode())
84+
request_args = {
85+
"redirect_uri": self.redirect_url,
86+
"state": oauth_state,
87+
}
88+
return request_args
89+
8690
def register_endpoints(self):
8791
"""
8892
Creates a list of all the endpoints this backend module needs to listen to. In this case
@@ -178,6 +182,12 @@ class FacebookBackend(_OAuthBackend):
178182
Backend module for facebook.
179183
"""
180184

185+
"""
186+
The default graph endpoint is for backward compatibility with previous versions of the
187+
Facebook backend in which the graph endpoint was hardcoded in the code.
188+
"""
189+
DEFAULT_GRAPH_ENDPOINT = "https://graph.facebook.com/v2.5/me"
190+
181191
def __init__(self, outgoing, internal_attributes, config, base_url, name):
182192
"""
183193
Constructor.
@@ -201,6 +211,20 @@ def __init__(self, outgoing, internal_attributes, config, base_url, name):
201211
config["verify_accesstoken_state"] = False
202212
super().__init__(outgoing, internal_attributes, config, base_url, name, "facebook", "id")
203213

214+
def get_request_args(self, get_state=stateID):
215+
request_args = super().get_request_args(get_state=get_state)
216+
217+
client_id = self.config["client_config"]["client_id"]
218+
extra_args = {
219+
arg_name: arg_val
220+
for arg_name in ["auth_type", "scope"]
221+
for arg_val in [self.config.get(arg_name, [])]
222+
if arg_val
223+
}
224+
extra_args.update({"client_id": client_id})
225+
request_args.update(extra_args)
226+
return request_args
227+
204228
def auth_info(self, request):
205229
"""
206230
Creates the SATOSA authentication information object.
@@ -224,8 +248,8 @@ def user_information(self, access_token):
224248
:param access_token: The access token to be used to retrieve the data.
225249
:return: Dictionary with attribute name as key and attribute value as value.
226250
"""
227-
payload = {'access_token': access_token}
228-
url = "https://graph.facebook.com/v2.5/me"
251+
payload = {"access_token": access_token}
252+
url = self.config["server_info"].get("graph_endpoint", self.DEFAULT_GRAPH_ENDPOINT)
229253
if self.config["fields"]:
230254
payload["fields"] = ",".join(self.config["fields"])
231255
resp = requests.get(url, params=payload)
@@ -258,14 +282,14 @@ def get_metadata_desc_for_oauth_backend(entity_id, config):
258282
# Add contact person information
259283
for contact_person in entity_info.get("contact_person", []):
260284
person = ContactPersonDesc()
261-
if 'contact_type' in contact_person:
262-
person.contact_type = contact_person['contact_type']
263-
for address in contact_person.get('email_address', []):
285+
if "contact_type" in contact_person:
286+
person.contact_type = contact_person["contact_type"]
287+
for address in contact_person.get("email_address", []):
264288
person.add_email_address(address)
265-
if 'given_name' in contact_person:
266-
person.given_name = contact_person['given_name']
267-
if 'sur_name' in contact_person:
268-
person.sur_name = contact_person['sur_name']
289+
if "given_name" in contact_person:
290+
person.given_name = contact_person["given_name"]
291+
if "sur_name" in contact_person:
292+
person.sur_name = contact_person["sur_name"]
269293

270294
description.add_contact_person(person)
271295

0 commit comments

Comments
 (0)