Skip to content

Commit 3e4690a

Browse files
Merge pull request #5 from SUNET/add-pid-constructor
Add PID Constructor
2 parents 7d87527 + ed19215 commit 3e4690a

File tree

1 file changed

+117
-6
lines changed
  • src/openid4v/openid_credential_issuer/credential_constructor

1 file changed

+117
-6
lines changed

src/openid4v/openid_credential_issuer/credential_constructor/pid.py

+117-6
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,123 @@
11
import json
22
import logging
3-
from typing import Optional
4-
from typing import Union
3+
from typing import Optional, Union
4+
5+
from idpyoidc.message import Message
6+
from idpysdjwt.issuer import Issuer
7+
from satosa_idpyop.persistence import Persistence
8+
from satosa_idpyop.utils import combine_client_subject_id
9+
10+
from openid4v.openid_credential_issuer.credential import CredentialConstructor
11+
12+
logger = logging.getLogger(__name__)
13+
14+
15+
class PIDConstructor(CredentialConstructor):
16+
17+
def __init__(self, upstream_get, **kwargs):
18+
19+
CredentialConstructor.__init__(self, upstream_get=upstream_get)
20+
self.url = kwargs.get("url") # MUST have a value
21+
self.body = kwargs.get("body", {})
22+
self.claims = kwargs.get(
23+
"attributes", ["family_name", "given_name", "birth_date"]
24+
)
25+
26+
def _get_userinfo(self, cntx, user_id, claims_restriction, client_id):
27+
_persistence = self.upstream_get("attribute", "persistence")
28+
logger.debug(f"Using {_persistence.name} persistence layer")
29+
client_subject_id = combine_client_subject_id(client_id, user_id)
30+
authn_claims = _persistence.load_claims(client_subject_id)
31+
# filter on accepted claims
32+
_ava = {}
33+
if {"family_name", "given_name", "birth_date"}.issubset(
34+
set(list(authn_claims.keys()))
35+
):
36+
for attr, value in authn_claims.items():
37+
if attr in ["family_name", "given_name", "birth_date"]:
38+
_ava[attr] = value
39+
40+
logger.debug(f"Authentication claims: {_ava}")
41+
return _ava
42+
43+
def __call__(
44+
self,
45+
user_id: str,
46+
client_id: str,
47+
request: Union[dict, Message],
48+
grant: Optional[dict] = None,
49+
id_token: Optional[str] = None,
50+
authz_detail: Optional[dict] = None,
51+
persistence: Optional[Persistence] = None,
52+
) -> str:
53+
logger.debug(":" * 20 + "PID constructor" + ":" * 20)
54+
55+
# Get extra arguments from the authorization request if available
56+
if "issuer_state" in grant.authorization_request:
57+
msg = Message().from_urlencoded(grant.authorization_request["issuer_state"])
58+
_body = msg.to_dict()
59+
_body["credential_type"] = "sdjwt"
60+
_vct = authz_detail["vct"]
61+
_body["document_type"] = _vct
62+
else:
63+
_body = grant.authorization_request
64+
65+
logger.debug(f"Authorization request claims: {_body}")
66+
67+
# and more arguments from what the authentication returned
68+
# _persistence = self.upstream_get("attribute", "persistence")
69+
logger.debug(f"Using {persistence.name} persistence layer")
70+
client_subject_id = combine_client_subject_id(client_id, user_id)
71+
authn_claims = persistence.load_claims(client_subject_id)
72+
# filter on accepted claims
73+
_ava = {}
74+
logger.debug(f"AVA claims: {authn_claims}")
75+
for attr, value in authn_claims.items():
76+
if attr in ["family_name", "given_name", "birth_date"]:
77+
_ava[attr] = value
78+
logger.debug(f"Authentication claims: {_ava}")
79+
80+
if "birth_date" in _ava:
81+
if isinstance(_ava["birth_date"], list):
82+
_ava["birth_date"] = _ava["birth_date"][0]
83+
84+
if "identity" not in _body:
85+
_body["identity"] = {"schema": {"name": "FR"}}
86+
87+
_body["identity"].update(_ava)
88+
89+
_body["jwk"] = request["__verified_proof"].jws_header["jwk"]
90+
91+
ci = Issuer(
92+
key_jar=self.upstream_get("attribute", "keyjar"),
93+
iss=self.upstream_get("attribute", "entity_id"),
94+
sign_alg="ES256",
95+
lifetime=31536000,
96+
holder_key={},
97+
)
98+
99+
logger.debug(f"Combined body: {_body}")
100+
101+
_sdjwt = ci.create_holder_message(
102+
payload=_body, jws_headers={"typ": "example+sd-jwt"}
103+
)
104+
return json.dumps({"credentials": [{"credential": _sdjwt}]})
105+
106+
107+
"""
108+
import json
109+
import logging
110+
from typing import Optional, Union
5111
6112
from cryptojwt.jwk.jwk import key_from_jwk_dict
7113
from idpyoidc.client.exception import OidcServiceError
8114
from idpyoidc.exception import RequestError
9115
from idpyoidc.message import Message
10-
from openid4v.openid_credential_issuer.credential import CredentialConstructor
11116
from satosa_idpyop.persistence import Persistence
12117
from satosa_idpyop.utils import combine_client_subject_id
13118
119+
from openid4v.openid_credential_issuer.credential import CredentialConstructor
120+
14121
logger = logging.getLogger(__name__)
15122
16123
@@ -20,7 +127,9 @@ def __init__(self, upstream_get, **kwargs):
20127
CredentialConstructor.__init__(self, upstream_get=upstream_get)
21128
self.url = kwargs.get("url") # MUST have a value
22129
self.body = kwargs.get("body", {})
23-
self.claims = kwargs.get("attributes", ["family_name", "given_name", "birth_date"])
130+
self.claims = kwargs.get(
131+
"attributes", ["family_name", "given_name", "birth_date"]
132+
)
24133
25134
def _get_userinfo(self, cntx, user_id, claims_restriction, client_id):
26135
_persistence = self.upstream_get("attribute", "persistence")
@@ -29,15 +138,16 @@ def _get_userinfo(self, cntx, user_id, claims_restriction, client_id):
29138
authn_claims = _persistence.load_claims(client_subject_id)
30139
# filter on accepted claims
31140
_ava = {}
32-
if {"family_name", "given_name", "birth_date"}.issubset(set(list(authn_claims.keys()))):
141+
if {"family_name", "given_name", "birth_date"}.issubset(
142+
set(list(authn_claims.keys()))
143+
):
33144
for attr, value in authn_claims.items():
34145
if attr in ["family_name", "given_name", "birth_date"]:
35146
_ava[attr] = value
36147
37148
logger.debug(f"Authentication claims: {_ava}")
38149
return _ava
39150
40-
41151
# def __call__(self,
42152
# user_id: str,
43153
# client_id: str,
@@ -95,3 +205,4 @@ def _get_userinfo(self, cntx, user_id, claims_restriction, client_id):
95205
# msg = self.get_response(url=self.url, body=_body, headers={"Content-Type": "application/json"})
96206
# logger.debug(f"return message: {msg}")
97207
# return msg
208+
"""

0 commit comments

Comments
 (0)