Skip to content

Commit f0f38af

Browse files
rohec00kiemon5ter
authored andcommitted
Changes as a result of Ali's testing.
1 parent 7ca9a80 commit f0f38af

File tree

1 file changed

+73
-68
lines changed

1 file changed

+73
-68
lines changed

src/satosa/backends/idpy_oidc.py

Lines changed: 73 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,50 @@
11
"""
2-
OIDC backend module.
2+
OIDC/OAuth2 backend module.
33
"""
44
import logging
55
from datetime import datetime
66

7+
from idpyoidc.client.oauth2.stand_alone_client import StandAloneClient
78
from idpyoidc.server.user_authn.authn_context import UNSPECIFIED
89

910
import satosa.logging_util as lu
1011
from satosa.backends.base import BackendModule
11-
from satosa.exception import SATOSAAuthenticationError
1212
from satosa.internal import AuthenticationInformation
1313
from satosa.internal import InternalData
14+
from ..exception import SATOSAAuthenticationError
15+
from ..response import Redirect
1416

1517
logger = logging.getLogger(__name__)
1618

17-
"""
18-
OIDC/OAuth2 backend module.
19-
"""
20-
from idpyoidc.client.oauth2.stand_alone_client import StandAloneClient
21-
2219

2320
class IdpyOIDCBackend(BackendModule):
2421
"""
2522
Backend module for OIDC and OAuth 2.0, can be directly used.
2623
"""
2724

28-
def __init__(self, outgoing, internal_attributes, config, base_url, name):
25+
def __init__(self, auth_callback_func, internal_attributes, config, base_url, name):
2926
"""
30-
:type outgoing:
27+
OIDC backend module.
28+
:param auth_callback_func: Callback should be called by the module after the authorization
29+
in the backend is done.
30+
:param internal_attributes: Mapping dictionary between SATOSA internal attribute names and
31+
the names returned by underlying IdP's/OP's as well as what attributes the calling SP's and
32+
RP's expects namevice.
33+
:param config: Configuration parameters for the module.
34+
:param base_url: base url of the service
35+
:param name: name of the plugin
36+
37+
:type auth_callback_func:
3138
(satosa.context.Context, satosa.internal.InternalData) -> satosa.response.Response
32-
:type internal_attributes: dict[str, dict[str, list[str] | str]]
33-
:type config: dict[str, Any]
39+
:type internal_attributes: dict[string, dict[str, str | list[str]]]
40+
:type config: dict[str, dict[str, str] | list[str]]
3441
:type base_url: str
3542
:type name: str
36-
37-
:param outgoing: Callback should be called by the module after
38-
the authorization in the backend is done.
39-
:param internal_attributes: Internal attribute map
40-
:param config: The module config
41-
:param base_url: base url of the service
42-
:param name: name of the plugin
4343
"""
44-
super().__init__(outgoing, internal_attributes, base_url, name)
45-
46-
self.client = StandAloneClient(config=config["client_config"],
47-
client_type=config["client_config"]['client_type'])
48-
# Deal with provider discovery and client registration
44+
super().__init__(auth_callback_func, internal_attributes, base_url, name)
45+
# self.auth_callback_func = auth_callback_func
46+
# self.config = config
47+
self.client = StandAloneClient(config=config["client"], client_type="oidc")
4948
self.client.do_provider_info()
5049
self.client.do_client_registration()
5150

@@ -57,7 +56,8 @@ def start_auth(self, context, internal_request):
5756
:type internal_request: satosa.internal.InternalData
5857
:rtype satosa.response.Redirect
5958
"""
60-
return self.client.init_authorization()
59+
login_url = self.client.init_authorization()
60+
return Redirect(login_url)
6161

6262
def register_endpoints(self):
6363
"""
@@ -67,8 +67,56 @@ def register_endpoints(self):
6767
:rtype: Sequence[(str, Callable[[satosa.context.Context], satosa.response.Response]]
6868
:return: A list that can be used to map the request to SATOSA to this endpoint.
6969
"""
70+
return self.client.context.claims.get_usage('redirect_uris')
71+
72+
def response_endpoint(self, context, *args):
73+
"""
74+
Handles the authentication response from the OP.
75+
:type context: satosa.context.Context
76+
:type args: Any
77+
:rtype: satosa.response.Response
7078
71-
return self.client.context.claims.get_usage('authorization_endpoint')
79+
:param context: SATOSA context
80+
:param args: None
81+
:return:
82+
"""
83+
84+
_info = self.client.finalize(context.request)
85+
self._check_error_response(_info, context)
86+
userinfo = _info.get('userinfo')
87+
id_token = _info.get('id_token')
88+
89+
if not id_token and not userinfo:
90+
msg = "No id_token or userinfo, nothing to do.."
91+
logline = lu.LOG_FMT.format(id=lu.get_session_id(context.state), message=msg)
92+
logger.error(logline)
93+
raise SATOSAAuthenticationError(context.state, "No user info available.")
94+
95+
all_user_claims = dict(list(userinfo.items()) + list(id_token.items()))
96+
msg = "UserInfo: {}".format(all_user_claims)
97+
logline = lu.LOG_FMT.format(id=lu.get_session_id(context.state), message=msg)
98+
logger.debug(logline)
99+
internal_resp = self._translate_response(all_user_claims, _info["issuer"])
100+
return self.auth_callback_func(context, internal_resp)
101+
102+
def _translate_response(self, response, issuer):
103+
"""
104+
Translates oidc response to SATOSA internal response.
105+
:type response: dict[str, str]
106+
:type issuer: str
107+
:type subject_type: str
108+
:rtype: InternalData
109+
110+
:param response: Dictioary with attribute name as key.
111+
:param issuer: The oidc op that gave the repsonse.
112+
:param subject_type: public or pairwise according to oidc standard.
113+
:return: A SATOSA internal response.
114+
"""
115+
auth_info = AuthenticationInformation(UNSPECIFIED, str(datetime.now()), issuer)
116+
internal_resp = InternalData(auth_info=auth_info)
117+
internal_resp.attributes = self.converter.to_internal("openid", response)
118+
internal_resp.subject_id = response["sub"]
119+
return internal_resp
72120

73121
def _check_error_response(self, response, context):
74122
"""
@@ -86,46 +134,3 @@ def _check_error_response(self, response, context):
86134
logline = lu.LOG_FMT.format(id=lu.get_session_id(context.state), message=msg)
87135
logger.debug(logline)
88136
raise SATOSAAuthenticationError(context.state, "Access denied")
89-
90-
def _authn_response(self, context):
91-
"""
92-
Handles the authentication response from the AS.
93-
94-
:type context: satosa.context.Context
95-
:rtype: satosa.response.Response
96-
:param context: The context in SATOSA
97-
:return: A SATOSA response. This method is only responsible to call the callback function
98-
which generates the Response object.
99-
"""
100-
101-
_info = self.client.finalize(context.request)
102-
self._check_error_response(_info, context)
103-
104-
try:
105-
auth_info = self.auth_info(context.request)
106-
except NotImplementedError:
107-
auth_info = AuthenticationInformation(auth_class_ref=UNSPECIFIED,
108-
timestamp=str(datetime.now()),
109-
issuer=_info["issuer"])
110-
111-
attributes = self.converter.to_internal(
112-
self.client.client_type, _info['userinfo'],
113-
)
114-
115-
internal_response = InternalData(
116-
auth_info=auth_info,
117-
attributes=attributes,
118-
subject_id=_info['userinfo']['sub']
119-
)
120-
return internal_response
121-
122-
def auth_info(self, request):
123-
"""
124-
Creates the SATOSA authentication information object.
125-
:type request: dict[str, str]
126-
:rtype: AuthenticationInformation
127-
128-
:param request: The request parameters in the authentication response sent by the AS.
129-
:return: How, who and when the authentication took place.
130-
"""
131-
raise NotImplementedError("Method 'auth_info' must be implemented in the subclass!")

0 commit comments

Comments
 (0)