-
Notifications
You must be signed in to change notification settings - Fork 131
/
Copy pathorcid.py
108 lines (94 loc) · 4.26 KB
/
orcid.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
"""
OAuth backend for Orcid
"""
import json
import requests
import logging
from urllib.parse import urljoin
from oic.utils.authn.authn_context import UNSPECIFIED
from oic.oauth2.consumer import stateID
from oic.oauth2.message import AuthorizationResponse
from .oauth import _get_metadata_to_decorate
from satosa.backends.oauth import _OAuthBackend
from satosa.context import Context
from satosa.internal import InternalData
from satosa.internal import AuthenticationInformation
from satosa.util import rndstr
logger = logging.getLogger(__name__)
class OrcidBackend(_OAuthBackend):
"""Orcid OAuth 2.0 backend"""
def __init__(self, outgoing, internal_attributes, config, base_url, name):
"""Orcid backend constructor
:param outgoing: Callback should be called by the module after the
authorization in the backend is done.
:param internal_attributes: Mapping dictionary between SATOSA internal
attribute names and the names returned by underlying IdP's/OP's as
well as what attributes the calling SP's and RP's expects namevice.
:param config: configuration parameters for the module.
:param base_url: base url of the service
:param name: name of the plugin
:type outgoing:
(satosa.context.Context, satosa.internal.InternalData) ->
satosa.response.Response
:type internal_attributes: dict[string, dict[str, str | list[str]]]
:type config: dict[str, dict[str, str] | list[str] | str]
:type base_url: str
:type name: str
"""
config.setdefault('response_type', 'code')
config['verify_accesstoken_state'] = False
super().__init__(
outgoing, internal_attributes, config, base_url, name, 'orcid',
'orcid')
def get_request_args(self, get_state=stateID):
oauth_state = get_state(self.config["base_url"], rndstr().encode())
request_args = {
"client_id": self.config['client_config']['client_id'],
"redirect_uri": self.redirect_url,
"scope": ' '.join(self.config['scope']),
"state": oauth_state,
}
return request_args
def auth_info(self, requrest):
return AuthenticationInformation(
UNSPECIFIED, None,
self.config['server_info']['authorization_endpoint'])
def _authn_response(self, context):
state_data = context.state[self.name]
aresp = self.consumer.parse_response(
AuthorizationResponse, info=json.dumps(context.request))
self._verify_state(aresp, state_data, context.state)
rargs = {"code": aresp["code"], "redirect_uri": self.redirect_url,
"state": state_data["state"]}
atresp = self.consumer.do_access_token_request(
request_args=rargs, state=aresp['state'])
user_info = self.user_information(
atresp['access_token'], atresp['orcid'], atresp.get('name'))
internal_response = InternalData(
auth_info=self.auth_info(context.request))
internal_response.attributes = self.converter.to_internal(
self.external_type, user_info)
internal_response.subject_id = user_info[self.user_id_attr]
context.decorate(Context.KEY_METADATA_STORE, _get_metadata_to_decorate(self.config))
return self.auth_callback_func(context, internal_response)
def user_information(self, access_token, orcid, name=None):
base_url = self.config['server_info']['user_info']
url = urljoin(base_url, '{}/person'.format(orcid))
headers = {
'Accept': 'application/orcid+json',
'Authorization': "Bearer {}".format(access_token)
}
r = requests.get(url, headers=headers)
r = r.json()
emails, addresses = r['emails']['email'], r['addresses']['address']
rname = r.get('name') or {}
ret = dict(
address=', '.join([e['country']['value'] for e in addresses]),
displayname=name,
edupersontargetedid=orcid, orcid=orcid,
mail=' '.join([e['email'] for e in emails]),
name=name,
givenname=(rname.get('given-names') or {}).get('value'),
surname=(rname.get('family-name') or {}).get('value'),
)
return ret