Skip to content
This repository was archived by the owner on Jun 23, 2023. It is now read-only.

Commit e895053

Browse files
authored
Merge pull request #107 from IdentityPython/userinfo_claims
Userinfo claims
2 parents d9157c1 + 66f155f commit e895053

5 files changed

Lines changed: 163 additions & 77 deletions

File tree

src/oidcop/oidc/userinfo.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from oidcmsg import oidc
1111
from oidcmsg.message import Message
1212
from oidcmsg.oauth2 import ResponseMessage
13+
from oidcop.session.claims import claims_match
1314

1415
from oidcop.endpoint import Endpoint
1516
from oidcop.token.exception import UnknownToken
@@ -140,6 +141,8 @@ def process_request(self, request=None, **kwargs):
140141
user_id=_session_info["user_id"], claims_restriction=_claims
141142
)
142143
info["sub"] = _grant.sub
144+
if _grant.add_acr_value("userinfo"):
145+
info["acr"] = _grant.authentication_event["authn_info"]
143146
else:
144147
info = {
145148
"error": "invalid_request",

src/oidcop/session/grant.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
from oidcop.authn_event import AuthnEvent
1111
from oidcop.session import MintingNotAllowed
12+
from oidcop.session.claims import claims_match
1213
from oidcop.session.token import AccessToken
1314
from oidcop.session.token import AuthorizationCode
1415
from oidcop.session.token import IDToken
@@ -180,6 +181,14 @@ def find_scope(self, based_on):
180181

181182
return self.scope
182183

184+
def add_acr_value(self, claims_release_point):
185+
_release = self.claims.get(claims_release_point)
186+
if _release:
187+
_acr_request = _release.get("acr")
188+
_used_acr = self.authentication_event.get("authn_info")
189+
return claims_match(_used_acr, _acr_request)
190+
return False
191+
183192
def payload_arguments(
184193
self,
185194
session_id: str,
@@ -221,6 +230,10 @@ def payload_arguments(
221230
user_info = endpoint_context.claims_interface.get_user_claims(user_id, _claims_restriction)
222231
payload.update(user_info)
223232

233+
# Should I add the acr value
234+
if self.add_acr_value(claims_release_point):
235+
payload["acr"] = self.authentication_event["authn_info"]
236+
224237
return payload
225238

226239
def mint_token(

src/oidcop/session/manager.py

Lines changed: 49 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def __init__(self, salt: Optional[str] = "", filename: Optional[str] = ""):
3838
if os.path.isfile(filename):
3939
self.salt = open(filename).read()
4040
elif not os.path.isfile(filename) and os.path.exists(
41-
filename
41+
filename
4242
): # Not a file, Something else
4343
raise ConfigurationError("Salt filename points to something that is not a file")
4444
else:
@@ -73,7 +73,8 @@ class SessionManager(Database):
7373
init_args = ["handler"]
7474

7575
def __init__(
76-
self, handler: TokenHandler, conf: Optional[dict] = None, sub_func: Optional[dict] = None,
76+
self, handler: TokenHandler, conf: Optional[dict] = None,
77+
sub_func: Optional[dict] = None,
7778
):
7879
self.conf = conf or {}
7980

@@ -125,9 +126,9 @@ def __setattr__(self, key, value):
125126

126127
def _init_db(self):
127128
Database.__init__(
128-
self,
129-
key=self.load_key(),
130-
salt=self.load_salt()
129+
self,
130+
key=self.load_key(),
131+
salt=self.load_salt()
131132
)
132133

133134
def get_user_info(self, uid: str) -> UserSessionInfo:
@@ -153,14 +154,14 @@ def find_token(self, session_id: str, token_value: str) -> Optional[SessionToken
153154
return None # pragma: no cover
154155

155156
def create_grant(
156-
self,
157-
authn_event: AuthnEvent,
158-
auth_req: AuthorizationRequest,
159-
user_id: str,
160-
client_id: Optional[str] = "",
161-
sub_type: Optional[str] = "public",
162-
token_usage_rules: Optional[dict] = None,
163-
scopes: Optional[list] = None,
157+
self,
158+
authn_event: AuthnEvent,
159+
auth_req: AuthorizationRequest,
160+
user_id: str,
161+
client_id: Optional[str] = "",
162+
sub_type: Optional[str] = "public",
163+
token_usage_rules: Optional[dict] = None,
164+
scopes: Optional[list] = None,
164165
) -> str:
165166
"""
166167
@@ -175,29 +176,31 @@ def create_grant(
175176
"""
176177
sector_identifier = auth_req.get("sector_identifier_uri", "")
177178

179+
_claims = auth_req.get("claims", {})
180+
178181
grant = Grant(
179182
authorization_request=auth_req,
180183
authentication_event=authn_event,
181-
sub=self.sub_func[sub_type](
182-
user_id, salt=self.salt, sector_identifier=sector_identifier
183-
),
184+
sub=self.sub_func[sub_type](user_id, salt=self.salt,
185+
sector_identifier=sector_identifier),
184186
usage_rules=token_usage_rules,
185187
scope=scopes,
188+
claims=_claims
186189
)
187190

188191
self.set([user_id, client_id, grant.id], grant)
189192

190193
return self.encrypted_session_id(user_id, client_id, grant.id)
191194

192195
def create_session(
193-
self,
194-
authn_event: AuthnEvent,
195-
auth_req: AuthorizationRequest,
196-
user_id: str,
197-
client_id: Optional[str] = "",
198-
sub_type: Optional[str] = "public",
199-
token_usage_rules: Optional[dict] = None,
200-
scopes: Optional[list] = None,
196+
self,
197+
authn_event: AuthnEvent,
198+
auth_req: AuthorizationRequest,
199+
user_id: str,
200+
client_id: Optional[str] = "",
201+
sub_type: Optional[str] = "public",
202+
token_usage_rules: Optional[dict] = None,
203+
scopes: Optional[list] = None,
201204
) -> str:
202205
"""
203206
Create part of a user session. The parts added are user- and client
@@ -309,10 +312,10 @@ def revoke_token(self, session_id: str, token_value: str, recursive: bool = Fals
309312
self._revoke_dependent(grant, token)
310313

311314
def get_authentication_events(
312-
self,
313-
session_id: Optional[str] = "",
314-
user_id: Optional[str] = "",
315-
client_id: Optional[str] = "",
315+
self,
316+
session_id: Optional[str] = "",
317+
user_id: Optional[str] = "",
318+
client_id: Optional[str] = "",
316319
) -> List[AuthnEvent]:
317320
"""
318321
Return the authentication events that exists for a user/client combination.
@@ -371,10 +374,10 @@ def revoke_grant(self, session_id: str):
371374
self.set(_path, _info)
372375

373376
def grants(
374-
self,
375-
session_id: Optional[str] = "",
376-
user_id: Optional[str] = "",
377-
client_id: Optional[str] = "",
377+
self,
378+
session_id: Optional[str] = "",
379+
user_id: Optional[str] = "",
380+
client_id: Optional[str] = "",
378381
) -> List[Grant]:
379382
"""
380383
Find all grant connected to a user session
@@ -395,13 +398,13 @@ def grants(
395398
return [self.get([user_id, client_id, gid]) for gid in _csi.subordinate]
396399

397400
def get_session_info(
398-
self,
399-
session_id: str,
400-
user_session_info: bool = False,
401-
client_session_info: bool = False,
402-
grant: bool = False,
403-
authentication_event: bool = False,
404-
authorization_request: bool = False,
401+
self,
402+
session_id: str,
403+
user_session_info: bool = False,
404+
client_session_info: bool = False,
405+
grant: bool = False,
406+
authentication_event: bool = False,
407+
authorization_request: bool = False,
405408
) -> dict:
406409
"""
407410
Returns information connected to a session.
@@ -449,13 +452,13 @@ def get_session_info(
449452
return res
450453

451454
def get_session_info_by_token(
452-
self,
453-
token_value: str,
454-
user_session_info: bool = False,
455-
client_session_info: bool = False,
456-
grant: bool = False,
457-
authentication_event: bool = False,
458-
authorization_request: bool = False,
455+
self,
456+
token_value: str,
457+
user_session_info: bool = False,
458+
client_session_info: bool = False,
459+
grant: bool = False,
460+
authentication_event: bool = False,
461+
authorization_request: bool = False,
459462
) -> dict:
460463
_token_info = self.token_handler.info(token_value)
461464
sid = _token_info.get("sid")

tests/test_05_id_token.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,11 @@ def full_path(local_file):
115115
"acr": INTERNETPROTOCOLPASSWORD,
116116
"class": "oidcop.user_authn.user.NoAuthn",
117117
"kwargs": {"user": "diana"},
118+
},
119+
"mfa": {
120+
"acr": 'https://refeds.org/profile/mfa',
121+
"class": "oidcop.user_authn.user.NoAuthn",
122+
"kwargs": {"user": "diana"},
118123
}
119124
},
120125
"session_manager": {
@@ -170,15 +175,15 @@ def create_session_manager(self):
170175
self.session_manager = self.endpoint_context.session_manager
171176
self.user_id = USER_ID
172177

173-
def _create_session(self, auth_req, sub_type="public", sector_identifier=""):
178+
def _create_session(self, auth_req, sub_type="public", sector_identifier="", authn_info=''):
174179
if sector_identifier:
175180
authz_req = auth_req.copy()
176181
authz_req["sector_identifier_uri"] = sector_identifier
177182
else:
178183
authz_req = auth_req
179184

180185
client_id = authz_req["client_id"]
181-
ae = create_authn_event(self.user_id)
186+
ae = create_authn_event(self.user_id, authn_info=authn_info)
182187
return self.session_manager.create_session(
183188
ae, authz_req, self.user_id, client_id=client_id, sub_type=sub_type
184189
)
@@ -587,3 +592,20 @@ def test_id_token_info(self):
587592
get_sign_and_encrypt_algorithms(
588593
endpoint_context, client_info, payload_type="id_token", sign=True, encrypt=True
589594
)
595+
596+
def test_id_token_acr_claim(self):
597+
_req = AREQS.copy()
598+
_req["claims"] = {"id_token": {"acr": {"value": "https://refeds.org/profile/mfa"}}}
599+
600+
session_id = self._create_session(_req,authn_info="https://refeds.org/profile/mfa")
601+
grant = self.session_manager[session_id]
602+
code = self._mint_code(grant, session_id)
603+
access_token = self._mint_access_token(grant, session_id, code)
604+
605+
id_token = self._mint_id_token(
606+
grant, session_id, token_ref=code, access_token=access_token.value
607+
)
608+
609+
_jwt = factory(id_token.value)
610+
_id_token_content = _jwt.jwt.payload()
611+
assert _id_token_content["acr"] == "https://refeds.org/profile/mfa"

0 commit comments

Comments
 (0)