From 0c3dc20146703a625b7467a82f686e502b4ecbc2 Mon Sep 17 00:00:00 2001 From: LuisPereira23 Date: Thu, 1 Aug 2024 16:36:19 +0100 Subject: [PATCH 1/9] Revert "Merge branch 'branch-preprod' into branch-development" This reverts commit cd8fe20aa7b672b734fb351f46c4e92a8061cc80, reversing changes made to 9aa5d28e97f9fd22adc4ee388e28c5ec619d61dc. --- app/app_config/config_countries.py | 17 + app/app_config/config_service.py | 6 +- app/app_config/oid_config.json | 6 +- app/metadata_config/metadata_config.json | 10 +- app/metadata_config/openid-configuration.json | 20 +- app/route_oidc.py | 322 ++++++------------ 6 files changed, 139 insertions(+), 242 deletions(-) diff --git a/app/app_config/config_countries.py b/app/app_config/config_countries.py index d3badc1..69df71c 100644 --- a/app/app_config/config_countries.py +++ b/app/app_config/config_countries.py @@ -74,6 +74,23 @@ class ConfCountries: ], "dynamic_R2": cfgserv.service_url + "dynamic/form_R2", }, + "sample": { + "name": "Sample", + "pid_url": cfgserv.service_url + "pid/form", + "pid_mdoc_privkey": "/etc/eudiw/pid-issuer/privkey/PID-DS-0001_UT.pem", + # "pid_mdoc_privkey": 'app\certs\PID-DS-0001_UT.pem', + "pid_mdoc_privkey_passwd": None, # None or bytes + "pid_mdoc_cert": "/etc/eudiw/pid-issuer/cert/PID-DS-0001_UT_cert.der", + "qeaa_func": cfgserv.service_url + "V04/qeaa/form", + "un_distinguishing_sign": "FC", + "supported_credentials": [ + "eu.europa.ec.eudi.pid_mdoc", + "eu.europa.ec.eudi.pid_jwt_vc_json", + "eu.europa.ec.eudi.mdl_jwt_vc_json", + "eu.europa.ec.eudi.mdl_mdoc", + ], + "dynamic_R2": cfgserv.service_url + "dynamic/form_R2", + }, "PT": { "name": "Portugal", "pid_mdoc_privkey": "/etc/eudiw/pid-issuer/privkey/PID-DS-0001_PT.pem", diff --git a/app/app_config/config_service.py b/app/app_config/config_service.py index 1ae9a47..14ca4e8 100644 --- a/app/app_config/config_service.py +++ b/app/app_config/config_service.py @@ -34,9 +34,9 @@ class ConfService: # ------------------------------------------------------------------------------------------------ # PID issuer service URL # service_url = "https://preprod.issuer.eudiw.dev:4443/" - service_url = "https://issuer.eudiw.dev/" + # service_url = "https://issuer.eudiw.dev/" # service_url = "https://127.0.0.1:5000/" - # service_url = "https://dev.issuer.eudiw.dev/" + service_url = "https://dev.issuer.eudiw.dev/" wallet_test_url = "https://dev.tester.issuer.eudiw.dev/" @@ -348,7 +348,7 @@ class ConfService: # ------------------------------------------------------------------------------------------------ # LOGS - log_dir = "/tmp/log" + log_dir = "/tmp/log_dev" # log_dir = "../../log" log_file_info = "logs.log" diff --git a/app/app_config/oid_config.json b/app/app_config/oid_config.json index 20d3a9b..9c1be05 100644 --- a/app/app_config/oid_config.json +++ b/app/app_config/oid_config.json @@ -32,8 +32,8 @@ } } }, - "port": 5000, - "domain": "issuer.eudiw.dev", + "port": 6000, + "domain": "dev.issuer.eudiw.dev", "server_name": "{domain}", "base_url": "https://{domain}", "op": { @@ -363,7 +363,7 @@ "server_key": "certs/client.key", "ca_bundle": null, "verify_user": false, - "port": 5000, + "port": 6000, "domain": "issuer.eudiw.dev", "debug": true } diff --git a/app/metadata_config/metadata_config.json b/app/metadata_config/metadata_config.json index 9fbbf8f..685bcd7 100644 --- a/app/metadata_config/metadata_config.json +++ b/app/metadata_config/metadata_config.json @@ -1,9 +1,9 @@ { - "credential_issuer": "https://issuer.eudiw.dev", - "credential_endpoint": "https://issuer.eudiw.dev/credential", - "batch_credential_endpoint": "https://issuer.eudiw.dev/batch_credential", - "notification_endpoint": "https://issuer.eudiw.dev/notification", - "deferred_credential_endpoint": "https://issuer.eudiw.dev/deferred_credential", + "credential_issuer": "https://dev.issuer.eudiw.dev", + "credential_endpoint": "https://dev.issuer.eudiw.dev/credential", + "batch_credential_endpoint": "https://dev.issuer.eudiw.dev/batch_credential", + "notification_endpoint": "https://dev.issuer.eudiw.dev/notification", + "deferred_credential_endpoint": "https://dev.issuer.eudiw.dev/deferred_credential", "credential_configurations_supported": { diff --git a/app/metadata_config/openid-configuration.json b/app/metadata_config/openid-configuration.json index 7ddc19e..22cf933 100644 --- a/app/metadata_config/openid-configuration.json +++ b/app/metadata_config/openid-configuration.json @@ -13,7 +13,7 @@ "urn:ietf:params:oauth:grant-type:jwt-bearer", "refresh_token" ], - "jwks_uri": "https://issuer.eudiw.dev/static/jwks.json", + "jwks_uri": "https://dev.issuer.eudiw.dev/static/jwks.json", "scopes_supported": [ "openid" ], @@ -78,13 +78,13 @@ "code_challenge_methods_supported": [ "S256" ], - "issuer": "https://issuer.eudiw.dev", - "registration_endpoint": "https://issuer.eudiw.dev/registration", - "introspection_endpoint": "https://issuer.eudiw.dev/introspection", - "authorization_endpoint": "https://issuer.eudiw.dev/authorizationV3", - "token_endpoint": "https://issuer.eudiw.dev/token", - "userinfo_endpoint": "https://issuer.eudiw.dev/userinfo", - "end_session_endpoint": "https://issuer.eudiw.dev/session", - "pushed_authorization_request_endpoint": "https://issuer.eudiw.dev/pushed_authorizationv2", - "credential_endpoint": "https://issuer.eudiw.dev/credential" + "issuer": "https://dev.issuer.eudiw.dev", + "registration_endpoint": "https://dev.issuer.eudiw.dev/registration", + "introspection_endpoint": "https://dev.issuer.eudiw.dev/introspection", + "authorization_endpoint": "https://dev.issuer.eudiw.dev/authorizationV3", + "token_endpoint": "https://dev.issuer.eudiw.dev/token", + "userinfo_endpoint": "https://dev.issuer.eudiw.dev/userinfo", + "end_session_endpoint": "https://dev.issuer.eudiw.dev/session", + "pushed_authorization_request_endpoint": "https://dev.issuer.eudiw.dev/pushed_authorizationv2", + "credential_endpoint": "https://dev.issuer.eudiw.dev/credential" } \ No newline at end of file diff --git a/app/route_oidc.py b/app/route_oidc.py index 785e33c..df46727 100644 --- a/app/route_oidc.py +++ b/app/route_oidc.py @@ -34,9 +34,7 @@ import urllib.parse import segno - from flask import Blueprint, jsonify, Response, request, session, current_app, redirect, render_template, url_for - from flask.helpers import make_response, send_from_directory import os @@ -76,7 +74,6 @@ # variable for PAR requests from app.data_management import parRequests, transaction_codes, deferredRequests - def _add_cookie(resp: Response, cookie_spec: Union[dict, list]): kwargs = {k: v for k, v in cookie_spec.items() if k not in ("name",)} kwargs["path"] = "/" @@ -157,19 +154,15 @@ def verify(authn_method): auth_args = authn_method.unpack_token(request.args.get("jws_token")) except: - log.logger_error.error( - "Authorization verification: username or jws_token not found" - ) + log.logger_error.error("Authorization verification: username or jws_token not found") if "jws_token" in request.args: return authentication_error_redirect( - jws_token=request.args.get("jws_token"), - error="invalid_request", - error_description="Authentication verification Error", - ) + jws_token=request.args.get("jws_token"), + error="invalid_request", + error_description="Authentication verification Error", + ) else: - return render_template( - "misc/500.html", error="Authentication verification Error" - ) + return render_template("misc/500.html", error="Authentication verification Error") authz_request = AuthorizationRequest().from_urlencoded(auth_args["query"]) @@ -275,16 +268,8 @@ def authorization(): return service_endpoint(current_app.server.get_endpoint("authorization")) -# @oidc.route("/authorizationV2", methods=["GET"]) -def authorizationv2( - client_id, - redirect_uri, - response_type, - scope=None, - code_challenge_method=None, - code_challenge=None, - authorization_details=None, -): +#@oidc.route("/authorizationV2", methods=["GET"]) +def authorizationv2(client_id,redirect_uri, response_type,scope=None, code_challenge_method=None, code_challenge=None, authorization_details=None): client_secret = str(uuid.uuid4()) @@ -292,10 +277,9 @@ def authorizationv2( client_id=client_id, client_secret=client_secret, redirect_uri=redirect_uri ) - # return service_endpoint(current_app.server.get_endpoint("authorization")) + #return service_endpoint(current_app.server.get_endpoint("authorization")) url = ( - cfgservice.service_url - + "authorization?redirect_uri=" + cfgservice.service_url + "authorization?redirect_uri=" + redirect_uri + "&response_type=" + response_type @@ -311,17 +295,19 @@ def authorizationv2( if code_challenge and code_challenge_method: url = url + "&code_challenge=" - +code_challenge - +"&code_challenge_method=" - +code_challenge_method + + code_challenge + + "&code_challenge_method=" + + code_challenge_method payload = {} headers = {} - response = requests.request("GET", url, headers=headers, data=payload) + response = requests.request( + "GET", url, headers=headers, data=payload + ) if response.status_code != 200: log.logger_error.error("Authorization endpoint invalid request") - return auth_error_redirect(redirect_uri, "invalid_request") + return auth_error_redirect(redirect_uri,"invalid_request") response = response.json() @@ -346,17 +332,12 @@ def authorizationv2( return redirect(response["url"]) - @oidc.route("/authorizationV3", methods=["GET"]) def authorizationV3(): - log.logger_info.info( - "Authorization request Data: " - + str(request.args) - + " | Headers: " - + str(dict(request.headers)) + log.logger_info.info("Authorization request Data: " + str(request.args) + " | Headers: " + str(dict(request.headers)) ) - + if "request_uri" not in request.args: try: client_id = request.args.get("client_id") @@ -368,15 +349,9 @@ def authorizationV3(): authorization_details = request.args.get("authorization_details") except: return make_response("Authorization v2 error", 400) - return authorizationv2( - client_id, - redirect_uri, - response_type, - scope, - code_challenge_method, - code_challenge, - authorization_details, - ) + return authorizationv2(client_id,redirect_uri, response_type,scope, code_challenge_method, code_challenge, authorization_details) + + try: request_uri = request.args.get("request_uri") @@ -394,10 +369,9 @@ def authorizationV3(): if "scope" not in par_args: par_args["scope"] = "openid" - + url = ( - cfgservice.service_url - + "authorization?redirect_uri=" + cfgservice.service_url + "authorization?redirect_uri=" + par_args["redirect_uri"] + "&response_type=" + par_args["response_type"] @@ -411,11 +385,13 @@ def authorizationV3(): payload = {} headers = {} - response = requests.request("GET", url, headers=headers, data=payload) + response = requests.request( + "GET", url, headers=headers, data=payload + ) if response.status_code != 200: log.logger_error.error("Authorization endpoint invalid request") - return auth_error_redirect(par_args["redirect_uri"], "invalid_request") + return auth_error_redirect(par_args["redirect_uri"],"invalid_request") response = response.json() @@ -443,43 +419,34 @@ def authorizationV3(): @oidc.route("/pid_authorization") def pid_authorization_get(): - presentation_id = request.args.get("presentation_id") + presentation_id= request.args.get("presentation_id") - url = ( - "https://dev.verifier-backend.eudiw.dev/ui/presentations/" - + presentation_id - + "?nonce=hiCV7lZi5qAeCy7NFzUWSR4iCfSmRb99HfIvCkPaCLc=" - ) + url = "https://dev.verifier-backend.eudiw.dev/ui/presentations/" + presentation_id + "?nonce=hiCV7lZi5qAeCy7NFzUWSR4iCfSmRb99HfIvCkPaCLc=" headers = { - "Content-Type": "application/json", + 'Content-Type': 'application/json', } response = requests.request("GET", url, headers=headers) if response.status_code != 200: - error_msg = str(response.status_code) - return jsonify({"error": error_msg}), 500 + error_msg= str(response.status_code) + return jsonify({"error": error_msg}),500 else: data = {"message": "Sucess"} - return jsonify({"message": data}), 200 - + return jsonify({"message": data}),200 + @oidc.route("/auth_choice", methods=["GET"]) def auth_choice(): token = request.args.get("token") - supported_credencials = cfgservice.auth_method_supported_credencials - pid_auth = True - country_selection = True + supported_credencials=cfgservice.auth_method_supported_credencials + pid_auth=True + country_selection=True if "authorization_params" not in session: - log.logger_info.info( - "Authorization Params didn't exist in Authentication Choice" - ) - return render_template( - "misc/500.html", - error="Invalid Authentication. No authorization details or scope found.", - ) - + log.logger_info.info("Authorization Params didn't exist in Authentication Choice") + return render_template("misc/500.html", error="Invalid Authentication. No authorization details or scope found.") + authorization_params = session["authorization_params"] authorization_details = [] @@ -489,6 +456,7 @@ def auth_choice(): ) if "scope" in authorization_params: authorization_details.extend(scope2details(authorization_params["scope"])) + credentials_requested = [] for cred in authorization_details: @@ -499,68 +467,50 @@ def auth_choice(): if cred["vct"] not in credentials_requested: credentials_requested.append(cred["vct"]) + for cred in credentials_requested: - if ( - cred in supported_credencials["PID_login"] - and cred not in supported_credencials["country_selection"] - ): - country_selection = False - elif ( - cred not in supported_credencials["PID_login"] - and cred in supported_credencials["country_selection"] - ): - pid_auth = False + if cred in supported_credencials["PID_login"] and cred not in supported_credencials["country_selection"]: + country_selection=False + elif cred not in supported_credencials["PID_login"] and cred in supported_credencials["country_selection"]: + pid_auth=False error = "" if pid_auth == False and country_selection == False: - error = "Combination of requested credentials is not valid!" - - return render_template( - "misc/auth_method.html", - pid_auth=pid_auth, - country_selection=country_selection, - error=error, - redirect_url=log.service_url, - ) + error="Combination of requested credentials is not valid!" + + return render_template("misc/auth_method.html",pid_auth=pid_auth,country_selection=country_selection, error=error, redirect_url= log.service_url) - # return render_template("misc/auth_method.html") + #return render_template("misc/auth_method.html") @oidc.route("/token_service", methods=["POST"]) def token_service(): - # session_id = request.cookies.get("session") + #session_id = request.cookies.get("session") response = service_endpoint(current_app.server.get_endpoint("token")) return response - @oidc.route("/token", methods=["POST"]) def token(): - log.logger_info.info( - "Token request data: " - + str(request.form.to_dict()) - + " | Headers: " - + str(dict(request.headers)) + log.logger_info.info("Token request data: " + str(request.form.to_dict()) + " | Headers: " + str(dict(request.headers)) ) - + req_args = dict([(k, v) for k, v in request.form.items()]) response = None if req_args["grant_type"] == "authorization_code": - + response = service_endpoint(current_app.server.get_endpoint("token")) - elif ( - req_args["grant_type"] == "urn:ietf:params:oauth:grant-type:pre-authorized_code" - ): + elif req_args["grant_type"] == "urn:ietf:params:oauth:grant-type:pre-authorized_code": if "pre-authorized_code" not in req_args or "tx_code" not in req_args: return make_response("invalid_request", 400) - + code = req_args["pre-authorized_code"] if code not in transaction_codes: @@ -583,22 +533,16 @@ def token(): url = cfgservice.service_url + "token_service" redirect_url = urllib.parse.quote(cfgservice.service_url) + "preauth-code" - payload = ( - "grant_type=authorization_code&code=" - + code - + "&redirect_uri=" - + redirect_url - + "&client_id=ID&state=vFs5DfvJqoyHj7_dZs2JbdklePg6pMLsUHHmVIfobRw&code_verifier=FnWCRIhpJtl6IYwVVYB8gZkQsmvBVLfU4HQiABPopYQ6gvIZBwMrXg" - ) - headers = {"Content-Type": "application/x-www-form-urlencoded"} - + payload = 'grant_type=authorization_code&code=' + code + '&redirect_uri=' + redirect_url + '&client_id=ID&state=vFs5DfvJqoyHj7_dZs2JbdklePg6pMLsUHHmVIfobRw&code_verifier=FnWCRIhpJtl6IYwVVYB8gZkQsmvBVLfU4HQiABPopYQ6gvIZBwMrXg' + headers = { + 'Content-Type': 'application/x-www-form-urlencoded' + } + response = requests.request("POST", url, headers=headers, data=payload) if response.status_code != 200: print("\n",str(response.json()),"\n") return make_response("invalid_request", 400) - #response = response.json() - log.logger_info.info("Token response: " + str(response.json())) transaction_codes.pop(code) @@ -635,11 +579,7 @@ def par_endpoint(): @oidc.route("/pushed_authorizationv2", methods=["POST"]) def par_endpointv2(): - log.logger_info.info( - "Recieved Pushed Authorization request. Data: " - + str(request.form.to_dict()) - + " | Headers: " - + str(dict(request.headers)) + log.logger_info.info("Recieved Pushed Authorization request. Data: " + str(request.form.to_dict()) + " | Headers: " + str(dict(request.headers)) ) redirect_uri = None @@ -650,9 +590,7 @@ def par_endpointv2(): except: log.logger_error.error("PAR: client_id or redirect_uri not found") if redirect_uri: - return auth_error_redirect( - redirect_uri, "invalid_request", "invalid parameters" - ) + return auth_error_redirect(redirect_uri, "invalid_request", "invalid parameters") else: return make_response("PARv2 error", 400) @@ -664,13 +602,8 @@ def par_endpointv2(): response = service_endpoint(current_app.server.get_endpoint("pushed_authorization")) - log.logger_info.info( - "PAR response for client_id " - + client_id - + " : " - + str(json.loads(response.get_data())) - ) - + log.logger_info.info("PAR response for client_id " + client_id + " : " + str(json.loads(response.get_data()))) + return response @@ -678,42 +611,24 @@ def par_endpointv2(): def credential(): if request.data: - log.logger_info.info( - "Credential request data: " - + str(json.loads(request.data)) - + " | Headers: " - + str(dict(request.headers)) + log.logger_info.info("Credential request data: " + str(json.loads(request.data)) + " | Headers: " + str(dict(request.headers)) ) _response = service_endpoint(current_app.server.get_endpoint("credential")) - - if isinstance(_response, Response): - log.logger_info.info( - "Credential response " + str(json.loads(_response.get_data())) - ) + + if isinstance(_response,Response): + log.logger_info.info("Credential response " + str(json.loads(_response.get_data()))) return _response - if ( - "transaction_id" in _response - and _response["transaction_id"] not in deferredRequests - ): + if "transaction_id" in _response and _response["transaction_id"] not in deferredRequests: request_data = request.data request_headers = dict(request.headers) - deferredRequests.update( - { - _response["transaction_id"]: { - "data": request_data, - "headers": request_headers, - "expires": datetime.now() - + timedelta(minutes=cfgservice.deffered_expiry), - } - } - ) - + deferredRequests.update({_response["transaction_id"]: {"data":request_data, "headers":request_headers, "expires":datetime.now() + timedelta(minutes=cfgservice.deffered_expiry)}}) + log.logger_info.info("Credential response " + str(_response)) - return make_response(jsonify(_response), 202) + return make_response(jsonify(_response),202) log.logger_info.info("Credential response " + str(_response)) return _response @@ -721,79 +636,53 @@ def credential(): @oidc.route("/batch_credential", methods=["POST"]) def batchCredential(): - log.logger_info.info( - "Batch credential request data: " - + str(json.loads(request.data)) - + " | Headers: " - + str(dict(request.headers)) + log.logger_info.info("Batch credential request data: " + str(json.loads(request.data)) + " | Headers: " + str(dict(request.headers)) ) _response = service_endpoint(current_app.server.get_endpoint("credential")) - if isinstance(_response, Response): - log.logger_info.info( - "Batch Credential response " + str(json.loads(_response.get_data())) - ) + if isinstance(_response,Response): + log.logger_info.info("Batch Credential response " + str(json.loads(_response.get_data()))) return _response - - if ( - "transaction_id" in _response - and _response["transaction_id"] not in deferredRequests - ): + + if "transaction_id" in _response and _response["transaction_id"] not in deferredRequests: request_data = request.data request_headers = dict(request.headers) - deferredRequests.update( - { - _response["transaction_id"]: { - "data": request_data, - "headers": request_headers, - "expires": datetime.now() - + timedelta(minutes=cfgservice.deffered_expiry), - } - } - ) - + deferredRequests.update({_response["transaction_id"]: {"data":request_data, "headers":request_headers, "expires":datetime.now() + timedelta(minutes=cfgservice.deffered_expiry)}}) + log.logger_info.info("Batch credential response " + str(_response)) - return make_response(jsonify(_response), 202) + return make_response(jsonify(_response),202) + log.logger_info.info("Batch credential response " + str(_response)) return _response - @oidc.route("/notification", methods=["POST"]) def notification(): - log.logger_info.info( - "Notification request data: " - + str(json.loads(request.data)) - + " | Headers: " - + str(dict(request.headers)) + log.logger_info.info("Notification request data: " + str(json.loads(request.data)) + " | Headers: " + str(dict(request.headers)) ) _resp = service_endpoint(current_app.server.get_endpoint("notification")) - if isinstance(_resp, Response): + if isinstance(_resp,Response): log.logger_info.info("Notification response " + str(_resp)) return _resp + log.logger_info.info("Notification response " + str(_resp)) - + return _resp - @oidc.route("/deferred_credential", methods=["POST"]) def deferred_credential(): - log.logger_info.info( - "Deferred request data: " - + str(json.loads(request.data)) - + " | Headers: " - + str(dict(request.headers)) + log.logger_info.info("Deferred request data: " + str(json.loads(request.data)) + " | Headers: " + str(dict(request.headers)) ) - + _resp = service_endpoint(current_app.server.get_endpoint("deferred_credential")) - if isinstance(_resp, Response): + if isinstance(_resp,Response): log.logger_info.info("Deferred response " + str(json.loads(_resp.get_data()))) return _resp @@ -801,7 +690,6 @@ def deferred_credential(): return _resp - @oidc.route("credential_offer_choice", methods=["GET"]) def credential_offer(): """Page for selecting credentials @@ -816,9 +704,9 @@ def credential_offer(): credential = credentialsSupported[cred] if credential["format"] == "vc+sd-jwt": - # if credential["scope"] == "eu.europa.ec.eudiw.pid.1": + #if credential["scope"] == "eu.europa.ec.eudiw.pid.1": credentials["sd-jwt vc format"].update( - # {"Personal Identification Data": cred} + #{"Personal Identification Data": cred} {cred: cred} ) @@ -828,22 +716,16 @@ def credential_offer(): ) """ if credential["format"] == "mso_mdoc": - # if credential["scope"] == "eu.europa.ec.eudiw.pid.1": + #if credential["scope"] == "eu.europa.ec.eudiw.pid.1": credentials["mdoc format"].update( {cred: cred} - # {"Personal Identification Data": cred} + #{"Personal Identification Data": cred} ) """ elif credential["scope"] == "org.iso.18013.5.1.mDL": credentials["mdoc format"].update({"Mobile Driver's Licence": cred}) """ - return render_template( - "openid/credential_offer.html", - cred=credentials, - redirect_url=cfgservice.service_url, - credential_offer_URI="openid-credential-offer://", - ) - + return render_template("openid/credential_offer.html", cred=credentials, redirect_url= cfgservice.service_url, credential_offer_URI="openid-credential-offer://") @oidc.route("/test_dump", methods=["GET", "POST"]) def dump_test(): @@ -875,7 +757,7 @@ def credentialOffer(): credentialsSupported = oidc_metadata["credential_configurations_supported"] auth_choice=request.form.get("Authorization Code Grant") form_keys = request.form.keys() - credential_offer_URI = request.form.get("credential_offer_URI") + credential_offer_URI=request.form.get("credential_offer_URI") if "proceed" in form_keys: @@ -906,7 +788,6 @@ def credentialOffer(): # create URI json_string = json.dumps(credential_offer) - uri = f"{credential_offer_URI}credential_offer?credential_offer=" + urllib.parse.quote( json_string, safe=":/" ) @@ -919,7 +800,6 @@ def credentialOffer(): out = io.BytesIO() qrcode.save(out, kind='png', scale=3) - """ qrcode.to_artistic( background=cfgtest.qr_png, target=out, @@ -1006,7 +886,7 @@ def service_endpoint(endpoint): error="invalid_request", error_description=str(err) ) return make_response(err_msg.to_json(), 400) - + if endpoint.name == "notification": try: accessToken = http_info["headers"]["authorization"][7:] @@ -1016,8 +896,8 @@ def service_endpoint(endpoint): _resp = endpoint.process_request(req_args) if isinstance(_resp, ResponseMessage) and "error" in _resp: - _log.info("Error response: {}".format(_resp)) - _resp = make_response(_resp.to_json(), 400) + _log.info("Error response: {}".format(_resp)) + _resp = make_response(_resp.to_json(), 400) except Exception as err: _log.error(err) @@ -1050,7 +930,7 @@ def service_endpoint(endpoint): else: response = do_response(endpoint, args, **args) return response - + except Exception as err: _log.error(err) return make_response( @@ -1117,7 +997,7 @@ def service_endpoint(endpoint): err_msg = ResponseMessage(error="invalid_request", error_description=str(err)) return make_response(err_msg.to_json(), 400) - # _log.info("Response args: {}".format(args)) + #_log.info("Response args: {}".format(args)) # "pushed_authorization" if ( From 949e9cea2bd02ea199cf0d63acf84fe15fa1b97a Mon Sep 17 00:00:00 2001 From: Luis Pereira <36925029+LuisPereira23@users.noreply.github.com> Date: Fri, 2 Aug 2024 14:22:01 +0100 Subject: [PATCH 2/9] Update pre-authorized.md --- api_docs/pre-authorized.md | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/api_docs/pre-authorized.md b/api_docs/pre-authorized.md index 6677090..377234b 100644 --- a/api_docs/pre-authorized.md +++ b/api_docs/pre-authorized.md @@ -1,15 +1,12 @@ # Pre-authorized -For the pre-authorized flow, you can test our example (issuance of loyalty card) by filling out the form in https://issuer.eudiw.dev/dynamic/preauth (or if installed locally), with the following data: - -+ *given_name* -+ *family_name* -+ *company* -+ *client-id* +For the pre-authorized flow, you can test our examples by selecting Pre-Authorization Code Grant in https://issuer.eudiw.dev/credential_offer (or if installed locally). After submitting, a QR code and DeepLink is generated: ![image](https://github.com/devisefutures/eudiw-issuer/assets/61158161/c4efc907-43e8-45dc-9255-e1c246914251) - - + +``` +openid-credential-offer://?credential_offer=%7B%22credential_issuer%22:%20%22https://issuer.eudiw.dev/%22%2C%20%22credential_configuration_ids%22:%20%5B%22eu.europa.ec.eudi.loyalty_mdoc%22%5D%2C%20%22grants%22:%20%7B%22urn:ietf:params:oauth:grant-type:pre-authorized_code%22:%20%7B%22pre-authorized_code%22:%20%22Z0FBQUFBQm1WMWZTQUwySE5WSklFcTJ5WU5pNXRPZ3pjU0V2UXRJbUpRYnRiM3I0eEthVU5OSzZhMXpPSHE1M2ZFVHFnRG9ReXRwRm95MzBtaTVkQzk3U05lRE4xSExqSkQtNGV3QmhNS0pLaVRBTk9HTmdXa2t1c3liMFBJeE9JVUQwc1o0UjI1ZTdFZTFDRndFeFkwd094NEk5eC1sM2ozb0FUekQxTVdjc1plMGFUdXR0czd4TkVxZUh5LVJ0RGtXWmZLbFJ4LU1KMzNtN01qamIzTFp6cER4SVVnSUtzbk84dUJlbmxEQ3ZPUjk1N3VEZndvOFdweWZhQjJzbTJLZ1hvZ0NoLXNBVHNnU0VFSmJEQ0VPWUF6UHotRzIyQVFTWlFTLTBqSmdadm0tYm1wLTYzMDFwYW4wQnVVNUJjSmpubGdwVTZQMEFzUUxMcHdnSHRBZEhZR0FLVXRaelNCNklGbnhqSTRxS05VXzdXa3VqQUNSOXp6UG13N2VqOFlqYXVXQ2dnTUdybHNUSXZwYlpqSWVMLXhBVFlUTHgwVnZydkNsTUxIWGlvRUlibWtoWGNiY1BMdFZKbG1EX2ttU1EwMmZLZ2J5ekNoOEM2ZVJtanQ5VEZWZ3N2SC03SjB6cklBMGZrckpQYXo5aXY4ODVMcjRMbWcyU3JuV21VRkdRdF9CTC12Z3FfQVlYWk5mY2FtdDNuZEVsZmhkSkFZaF9uVEpoakNuY2ZQMzlTZUxnWUlIU3FiZjQ2a0hJNkM3SVRTbTRFaXJ3eUx5UEtjdzBWNTRZMWFPTllsMkFoczNCd1lqQTg1SzJGTEdfQWlLSjRtQUxIVTk5RU9NaXhSUUtoQVRIekFIMUlOUEdkWEpDTmNIRllPTDhTazdVMUFoMjlOemdlZVROYU1xTi1heFl3TVE9%22%7D%7D%7D +``` From 5079e23ae1cc6fb6b6ed9ec9d52305b16d71ac15 Mon Sep 17 00:00:00 2001 From: LuisPereira23 Date: Tue, 6 Aug 2024 14:02:15 +0100 Subject: [PATCH 3/9] initial update to logs --- app/data_management.py | 20 +++++++++++++++++++ app/route_dynamic.py | 7 ++++--- app/route_oidc.py | 45 +++++++++++++++++++++++++++++------------- 3 files changed, 55 insertions(+), 17 deletions(-) diff --git a/app/data_management.py b/app/data_management.py index 44020e7..7d149f0 100644 --- a/app/data_management.py +++ b/app/data_management.py @@ -33,6 +33,26 @@ deferredRequests = {} oid4vp_requests = {} form_dynamic_data = {} +session_ids = {} + + +def getSessionId_requestUri(target_request_uri): + matching_session_id = None + for session_id, session_data in session_ids.items(): + if session_data["request_uri"] == target_request_uri: + matching_session_id = session_id + break + + return matching_session_id + +def getSessionId_authCode(target_authCode): + matching_session_id = None + for session_id, session_data in session_ids.items(): + if session_data["auth_code"] == target_authCode: + matching_session_id = session_id + break + + return matching_session_id ################################################ ## To be moved to a file with scheduled jobs diff --git a/app/route_dynamic.py b/app/route_dynamic.py index 7aee520..e34a6e1 100644 --- a/app/route_dynamic.py +++ b/app/route_dynamic.py @@ -142,6 +142,8 @@ def Supported_Countries(): session["returnURL"] = cfgserv.OpenID_first_endpoint session["country"] = form_country + log.logger_info.info(", Session ID: " + session["session_id"] + ", " + "Authorization selection, Type: " + form_country) + """ log.logger_info.info( " - INFO - " + session["route"] @@ -179,13 +181,13 @@ def dynamic_R1(country): credentials_requested = session["credentials_requested"] credentialsSupported = oidc_metadata["credential_configurations_supported"] - log.logger_info.info( + """ log.logger_info.info( " - INFO - Version:" + cfgserv.current_version + " - URL_R1 for Country: " + country + " has been created" - ) + ) """ if country == "FC": attributesForm = getAttributesForm(session["credentials_requested"]) @@ -1009,7 +1011,6 @@ def redirect_wallet(): form_data = request.form.to_dict() user_id= form_data["user_id"] - print("\n----Session redirect_wallet----\n", session) return redirect( url_get( cfgserv.OpenID_first_endpoint, diff --git a/app/route_oidc.py b/app/route_oidc.py index df46727..6e5d7a4 100644 --- a/app/route_oidc.py +++ b/app/route_oidc.py @@ -72,7 +72,7 @@ CORS(oidc) # enable CORS on the blue print # variable for PAR requests -from app.data_management import parRequests, transaction_codes, deferredRequests +from app.data_management import parRequests, transaction_codes, deferredRequests, session_ids, getSessionId_requestUri, getSessionId_authCode def _add_cookie(resp: Response, cookie_spec: Union[dict, list]): kwargs = {k: v for k, v in cookie_spec.items() if k not in ("name",)} @@ -181,6 +181,10 @@ def verify(authn_method): if isinstance(args, ResponseMessage) and "error" in args: return make_response(args.to_json(), 400) + session_ids[session["session_id"]]["auth_code"] = args["response_args"]["code"] + + log.logger_info.info(", Session ID: " + session["session_id"] + ", " + "Authorization Response, Code: " + args["response_args"]["code"] + ", State: " + args["response_args"]["state"]) + return do_response(endpoint, request, **args) @@ -335,8 +339,6 @@ def authorizationv2(client_id,redirect_uri, response_type,scope=None, code_chall @oidc.route("/authorizationV3", methods=["GET"]) def authorizationV3(): - log.logger_info.info("Authorization request Data: " + str(request.args) + " | Headers: " + str(dict(request.headers)) - ) if "request_uri" not in request.args: try: @@ -364,6 +366,17 @@ def authorizationV3(): # return service_endpoint(current_app.server.get_endpoint("authorization")) log.logger_error.error("Authorization request_uri not found in parRequests") return make_response("Request_uri not found", 400) + + session_id = getSessionId_requestUri(request_uri) + + if session_id == None: + log.logger_error.error("Authorization request_uri not found.") + return make_response("Request_uri not found", 400) + + log.logger_info.info(", Session ID: " + session_id + ", " + "Authorization Request, Payload: " + str(dict(request.args)) + ) + + session["session_id"] = session_id par_args = parRequests[request_uri]["req_args"] @@ -416,6 +429,7 @@ def authorizationV3(): return redirect(response["url"]) + @oidc.route("/pid_authorization") def pid_authorization_get(): @@ -495,14 +509,16 @@ def token_service(): @oidc.route("/token", methods=["POST"]) def token(): - log.logger_info.info("Token request data: " + str(request.form.to_dict()) + " | Headers: " + str(dict(request.headers)) - ) - req_args = dict([(k, v) for k, v in request.form.items()]) + response = None if req_args["grant_type"] == "authorization_code": + + session_id = getSessionId_authCode(req_args["code"]) + + log.logger_info.info(", Session ID: " + session_id + ", " + "Token Request, Payload: " + str(request.form.to_dict())) response = service_endpoint(current_app.server.get_endpoint("token")) @@ -540,7 +556,6 @@ def token(): response = requests.request("POST", url, headers=headers, data=payload) if response.status_code != 200: - print("\n",str(response.json()),"\n") return make_response("invalid_request", 400) #response = response.json() log.logger_info.info("Token response: " + str(response.json())) @@ -579,7 +594,9 @@ def par_endpoint(): @oidc.route("/pushed_authorizationv2", methods=["POST"]) def par_endpointv2(): - log.logger_info.info("Recieved Pushed Authorization request. Data: " + str(request.form.to_dict()) + " | Headers: " + str(dict(request.headers)) + session_id = str(uuid.uuid4()) + + log.logger_info.info(", Session ID: " + session_id + ", " + "Pushed Authorization Request, Payload: " + str(request.form.to_dict()) ) redirect_uri = None @@ -602,8 +619,10 @@ def par_endpointv2(): response = service_endpoint(current_app.server.get_endpoint("pushed_authorization")) - log.logger_info.info("PAR response for client_id " + client_id + " : " + str(json.loads(response.get_data()))) - + log.logger_info.info(", Session ID: " + session_id + ", " + "Pushed Authorization Response, Payload: " + str(json.loads(response.get_data()))) + + session_ids.update({session_id:{"expires":datetime.now() + timedelta(minutes=60), "request_uri":json.loads(response.get_data())["request_uri"]}}) + return response @@ -727,7 +746,7 @@ def credential_offer(): return render_template("openid/credential_offer.html", cred=credentials, redirect_url= cfgservice.service_url, credential_offer_URI="openid-credential-offer://") -@oidc.route("/test_dump", methods=["GET", "POST"]) +""" @oidc.route("/test_dump", methods=["GET", "POST"]) def dump_test(): _store = current_app.server.context.dump() @@ -749,7 +768,7 @@ def load_test(): print("\n-----Data-----\n",data) current_app.server.context.load(data) - return "load" + return "load" """ @oidc.route("/credential_offer", methods=["GET", "POST"]) def credentialOffer(): @@ -1012,8 +1031,6 @@ def service_endpoint(endpoint): + int(datetime.timestamp(datetime.now())), } - print("\n--------parRequests-----\n", parRequests[args["http_response"]["request_uri"]]) - if "redirect_location" in args: return redirect(args["redirect_location"]) if "http_response" in args: From 7b41c6bbc171c9359b58a9a6e7b4d6519bf1f7d5 Mon Sep 17 00:00:00 2001 From: LuisPereira23 Date: Fri, 9 Aug 2024 14:46:51 +0100 Subject: [PATCH 4/9] update logs to more routes --- app/data_management.py | 24 ++++++-- app/preauthorization.py | 20 ++++++- app/route_oid4vp.py | 13 ++++- app/route_oidc.py | 126 +++++++++++++++++++++++++++++++--------- 4 files changed, 145 insertions(+), 38 deletions(-) diff --git a/app/data_management.py b/app/data_management.py index 7d149f0..68eecef 100644 --- a/app/data_management.py +++ b/app/data_management.py @@ -39,7 +39,8 @@ def getSessionId_requestUri(target_request_uri): matching_session_id = None for session_id, session_data in session_ids.items(): - if session_data["request_uri"] == target_request_uri: + + if "request_uri" in session_data and session_data["request_uri"] == target_request_uri: matching_session_id = session_id break @@ -48,7 +49,16 @@ def getSessionId_requestUri(target_request_uri): def getSessionId_authCode(target_authCode): matching_session_id = None for session_id, session_data in session_ids.items(): - if session_data["auth_code"] == target_authCode: + if "auth_code" in session_data and session_data["auth_code"] == target_authCode: + matching_session_id = session_id + break + + return matching_session_id + +def getSessionId_accessToken(target_accessToken): + matching_session_id = None + for session_id, session_data in session_ids.items(): + if "access_token" in session_data and session_data["access_token"] == target_accessToken: matching_session_id = session_id break @@ -106,16 +116,20 @@ def clear_par(): for code in transaction_codes.copy(): if datetime.now() > transaction_codes[code]["expires"]: - cfgservice.logger_info.info("Current transaction_codes:\n" + str(transaction_codes)) + #cfgservice.logger_info.info("Current transaction_codes:\n" + str(transaction_codes)) cfgservice.logger_info.info("Removing tx_code for code: " + str(code)) transaction_codes.pop(code) for id in oid4vp_requests.copy(): if datetime.now() > oid4vp_requests[id]["expires"]: - cfgservice.logger_info.info("Current oid4vp_requests:\n" + str(oid4vp_requests)) + #cfgservice.logger_info.info("Current oid4vp_requests:\n" + str(oid4vp_requests)) cfgservice.logger_info.info("Removing oid4vp_requests with id: " + str(id)) oid4vp_requests.pop(id) - + + for id in session_ids.copy(): + if datetime.now() > session_ids[id]["expires"]: + cfgservice.logger_info.info("Removing session id: " + str(id)) + session_ids.pop(id) """Function to clear app.config['data']""" aux = [] diff --git a/app/preauthorization.py b/app/preauthorization.py index 3f3f1db..a475fd3 100644 --- a/app/preauthorization.py +++ b/app/preauthorization.py @@ -33,10 +33,16 @@ import segno -from app.route_oidc import parRequests, service_endpoint, transaction_codes +from app.route_oidc import service_endpoint from .app_config.config_service import ConfService as cfgservice from app.misc import authentication_error_redirect, getAttributesForm +from app.data_management import parRequests, transaction_codes, getSessionId_requestUri + +from app_config.config_service import ConfService as log + + + preauth = Blueprint("preauth", __name__, url_prefix="/") CORS(preauth) # enable CORS on the blue print @@ -65,12 +71,12 @@ def preauthRed(): 'Content-Type': 'application/x-www-form-urlencoded' } - print("\n------ PAR payload -----\n", payload) + #print("\n------ PAR payload -----\n", payload) response = requests.request("POST", url, headers=headers, data=payload) if response.status_code != 200: - print("\n",str(response.json()),"\n") + #print("\n",str(response.json()),"\n") return make_response("invalid_request", 400) par_response = response.json() @@ -89,6 +95,14 @@ def authorizationpre(): if not request_uri in parRequests: # unknow request_uri => return error # needs to be changed to an appropriate error message, and need to be logged return service_endpoint(current_app.server.get_endpoint("authorization")) + + session_id = getSessionId_requestUri(request_uri) + + if session_id == None: + log.logger_error.error("Authorization request_uri not found.") + return make_response("Request_uri not found", 400) + + session["session_id"] = session_id par_args = parRequests[request_uri]["req_args"] diff --git a/app/route_oid4vp.py b/app/route_oid4vp.py index b3a5068..a50f9d5 100644 --- a/app/route_oid4vp.py +++ b/app/route_oid4vp.py @@ -42,9 +42,15 @@ # secrets from app.data_management import oid4vp_requests, form_dynamic_data +from app_config.config_service import ConfService as log + + @oid4vp.route("/oid4vp", methods=["GET"]) def openid4vp(): + if "session_id" in session: + log.logger_info.info(", Session ID: " + session["session_id"] + ", " + "Authorization selection, Type: " + "oid4vp") + url = "https://dev.verifier-backend.eudiw.dev/ui/presentations" payload_cross_device = json.dumps( { @@ -113,7 +119,6 @@ def openid4vp(): } ) - id = generate_unique_id() payload_same_device = json.dumps( { "type": "vp_token", @@ -178,7 +183,7 @@ def openid4vp(): } ] }, - "wallet_response_redirect_uri_template":cfgservice.service_url + "getpidoid4vp?response_code={RESPONSE_CODE}&session_id=" + id + "wallet_response_redirect_uri_template":cfgservice.service_url + "getpidoid4vp?response_code={RESPONSE_CODE}&session_id=" + session["session_id"] } ) @@ -191,7 +196,7 @@ def openid4vp(): response_same = requests.request("POST", url, headers=headers, data=payload_same_device).json() - oid4vp_requests.update({id:{"response": response_same, "expires":datetime.now() + timedelta(minutes=cfgservice.deffered_expiry)}}) + oid4vp_requests.update({session["session_id"]:{"response": response_same, "expires":datetime.now() + timedelta(minutes=cfgservice.deffered_expiry)}}) deeplink_url = ( "eudi-openid4vp://dev.verifier-backend.eudiw.dev?client_id=" @@ -241,6 +246,7 @@ def openid4vp(): def getpidoid4vp(): if "response_code" in request.args and "session_id" in request.args: + log.logger_info.info(", Session ID: " + session["session_id"] + ", " + "oid4vp flow: same_device") response_code = request.args.get("response_code") presentation_id = oid4vp_requests[request.args.get("session_id")]["response"]["presentation_id"] url = ( @@ -251,6 +257,7 @@ def getpidoid4vp(): ) elif "presentation_id" in request.args: + log.logger_info.info(", Session ID: " + session["session_id"] + ", " + "oid4vp flow: cross_device") presentation_id = request.args.get("presentation_id") url = ( diff --git a/app/route_oidc.py b/app/route_oidc.py index 6e5d7a4..3e8b962 100644 --- a/app/route_oidc.py +++ b/app/route_oidc.py @@ -72,7 +72,7 @@ CORS(oidc) # enable CORS on the blue print # variable for PAR requests -from app.data_management import parRequests, transaction_codes, deferredRequests, session_ids, getSessionId_requestUri, getSessionId_authCode +from app.data_management import getSessionId_accessToken, parRequests, transaction_codes, deferredRequests, session_ids, getSessionId_requestUri, getSessionId_authCode def _add_cookie(resp: Response, cookie_spec: Union[dict, list]): kwargs = {k: v for k, v in cookie_spec.items() if k not in ("name",)} @@ -183,7 +183,14 @@ def verify(authn_method): session_ids[session["session_id"]]["auth_code"] = args["response_args"]["code"] - log.logger_info.info(", Session ID: " + session["session_id"] + ", " + "Authorization Response, Code: " + args["response_args"]["code"] + ", State: " + args["response_args"]["state"]) + logText = ", Session ID: " + session["session_id"] + ", " + "Authorization Response, Code: " + args["response_args"]["code"] + + if "state" in args["response_args"]: + + logText = logText + ", State: " + args["response_args"]["state"] + + + log.logger_info.info(logText) return do_response(endpoint, request, **args) @@ -334,6 +341,11 @@ def authorizationv2(client_id,redirect_uri, response_type,scope=None, code_chall session["authorization_params"] = params + session_id = str(uuid.uuid4()) + session_ids.update({session_id:{"expires":datetime.now() + timedelta(minutes=60)}}) + session["session_id"] = session_id + log.logger_info.info(", Session ID: " + session_id + ", " + "Authorization Request, Payload: " + str({"client_id":client_id,"redirect_uri":redirect_uri, "response_type":response_type, "scope":scope, "code_challenge_method":code_challenge_method, "code_challenge":code_challenge, "authorization_details":authorization_details})) + return redirect(response["url"]) @oidc.route("/authorizationV3", methods=["GET"]) @@ -518,10 +530,20 @@ def token(): session_id = getSessionId_authCode(req_args["code"]) - log.logger_info.info(", Session ID: " + session_id + ", " + "Token Request, Payload: " + str(request.form.to_dict())) + log.logger_info.info(", Session ID: " + session_id + ", " + "Authorization Token Request, Payload: " + str(request.form.to_dict())) response = service_endpoint(current_app.server.get_endpoint("token")) + log.logger_info.info(", Session ID: " + session_id + ", " + "Authorization Token Response, Payload: " + str(json.loads(response.get_data()))) + + response_json = json.loads(response.get_data()) + + if "access_token" in response_json: + session_ids[session_id]["access_token"] = response_json["access_token"] + + if "refresh_token" in response_json: + session_ids[session_id]["refresh_token"] = response_json["refresh_token"] + elif req_args["grant_type"] == "urn:ietf:params:oauth:grant-type:pre-authorized_code": if "pre-authorized_code" not in req_args or "tx_code" not in req_args: @@ -529,6 +551,10 @@ def token(): code = req_args["pre-authorized_code"] + session_id = getSessionId_authCode(code) + + log.logger_info.info(", Session ID: " + session_id + ", " + "Pre-Authorized Token Request, Payload: " + str(request.form.to_dict())) + if code not in transaction_codes: error_message = { "error": "invalid_request", @@ -561,12 +587,23 @@ def token(): log.logger_info.info("Token response: " + str(response.json())) transaction_codes.pop(code) - return response.json() - + + log.logger_info.info(", Session ID: " + session_id + ", " + "Pre-Authorized Token Response, Payload: " + str(response.json())) + + response_json = response.json() + + if "access_token" in response_json: + session_ids[session_id]["access_token"] = response_json["access_token"] + + if "refresh_token" in response_json: + session_ids[session_id]["refresh_token"] = response_json["refresh_token"] + + return response_json + else: - return service_endpoint(current_app.server.get_endpoint("token")) - - log.logger_info.info("Token response: " + str(json.loads(response.get_data()))) + response = service_endpoint(current_app.server.get_endpoint("token")) + log.logger_info.info("Token response: " + str(json.loads(response.get_data()))) + return response @@ -629,14 +666,22 @@ def par_endpointv2(): @oidc.route("/credential", methods=["POST"]) def credential(): - if request.data: - log.logger_info.info("Credential request data: " + str(json.loads(request.data)) + " | Headers: " + str(dict(request.headers)) - ) + headers = dict(request.headers) + payload = json.loads(request.data) + + if "Authorization" not in headers: + return make_response("Authorization error", 400) + + access_token = headers["Authorization"][7:] + print("\naccess_token: ", access_token) + session_id = getSessionId_accessToken(access_token) + + log.logger_info.info(", Session ID: " + session_id + ", " + "Credential Request, Payload: " + str(payload)) _response = service_endpoint(current_app.server.get_endpoint("credential")) if isinstance(_response,Response): - log.logger_info.info("Credential response " + str(json.loads(_response.get_data()))) + log.logger_info.info(", Session ID: " + session_id + ", " + "Credential response, Payload: " + str(json.loads(_response.get_data()))) return _response if "transaction_id" in _response and _response["transaction_id"] not in deferredRequests: @@ -645,23 +690,32 @@ def credential(): request_headers = dict(request.headers) deferredRequests.update({_response["transaction_id"]: {"data":request_data, "headers":request_headers, "expires":datetime.now() + timedelta(minutes=cfgservice.deffered_expiry)}}) - log.logger_info.info("Credential response " + str(_response)) + log.logger_info.info(", Session ID: " + session_id + ", " + "Credential response, Payload: " + str(_response)) return make_response(jsonify(_response),202) - log.logger_info.info("Credential response " + str(_response)) + log.logger_info.info(", Session ID: " + session_id + ", " + "Credential response, Payload: " + str(_response)) return _response @oidc.route("/batch_credential", methods=["POST"]) def batchCredential(): - log.logger_info.info("Batch credential request data: " + str(json.loads(request.data)) + " | Headers: " + str(dict(request.headers)) - ) + + headers = dict(request.headers) + payload = json.loads(request.data) + + if "Authorization" not in headers: + return make_response("Authorization error", 400) + + access_token = headers["Authorization"][6:] + session_id = getSessionId_accessToken(access_token) + + log.logger_info.info(", Session ID: " + session_id + ", " + "Batch Credential Request, Payload: " + str(payload)) _response = service_endpoint(current_app.server.get_endpoint("credential")) if isinstance(_response,Response): - log.logger_info.info("Batch Credential response " + str(json.loads(_response.get_data()))) + log.logger_info.info(", Session ID: " + session_id + ", " + "Batch Credential response, Payload: " + str(json.loads(_response.get_data()))) return _response if "transaction_id" in _response and _response["transaction_id"] not in deferredRequests: @@ -670,42 +724,60 @@ def batchCredential(): request_headers = dict(request.headers) deferredRequests.update({_response["transaction_id"]: {"data":request_data, "headers":request_headers, "expires":datetime.now() + timedelta(minutes=cfgservice.deffered_expiry)}}) - log.logger_info.info("Batch credential response " + str(_response)) + log.logger_info.info(", Session ID: " + session_id + ", " + "Batch credential response, Payload: " + str(_response)) return make_response(jsonify(_response),202) - log.logger_info.info("Batch credential response " + str(_response)) + log.logger_info.info(", Session ID: " + session_id + ", " + "Batch credential response, Payload: " + str(_response)) return _response @oidc.route("/notification", methods=["POST"]) def notification(): - log.logger_info.info("Notification request data: " + str(json.loads(request.data)) + " | Headers: " + str(dict(request.headers)) - ) + + headers = dict(request.headers) + payload = json.loads(request.data) + + if "Authorization" not in headers: + return make_response("Authorization error", 400) + + access_token = headers["Authorization"][6:] + session_id = getSessionId_accessToken(access_token) + + log.logger_info.info(", Session ID: " + session_id + ", " + "Notification Request, Payload: " + str(payload)) + _resp = service_endpoint(current_app.server.get_endpoint("notification")) if isinstance(_resp,Response): - log.logger_info.info("Notification response " + str(_resp)) + log.logger_info.info(", Session ID: " + session_id + ", " + "Notification response, Payload: " + str(_resp)) return _resp - log.logger_info.info("Notification response " + str(_resp)) + log.logger_info.info(", Session ID: " + session_id + ", " + "Notification response, Payload: " + str(_resp)) return _resp @oidc.route("/deferred_credential", methods=["POST"]) def deferred_credential(): - log.logger_info.info("Deferred request data: " + str(json.loads(request.data)) + " | Headers: " + str(dict(request.headers)) - ) + headers = dict(request.headers) + payload = json.loads(request.data) + + if "Authorization" not in headers: + return make_response("Authorization error", 400) + + access_token = headers["Authorization"][6:] + session_id = getSessionId_accessToken(access_token) + + log.logger_info.info(", Session ID: " + session_id + ", " + "Deferred Credential Request, Payload:, Payload: " + str(payload)) _resp = service_endpoint(current_app.server.get_endpoint("deferred_credential")) if isinstance(_resp,Response): - log.logger_info.info("Deferred response " + str(json.loads(_resp.get_data()))) + log.logger_info.info(", Session ID: " + session_id + ", " + "Deferred response, Payload: " + str(json.loads(_resp.get_data()))) return _resp - log.logger_info.info("Deferred response " + str(_resp)) + log.logger_info.info(", Session ID: " + session_id + ", " + "Deferred response, Payload: " + str(_resp)) return _resp From 4b4f3592474214444d2f83315a424913a3441c14 Mon Sep 17 00:00:00 2001 From: LuisPereira23 Date: Fri, 9 Aug 2024 15:34:31 +0100 Subject: [PATCH 5/9] Dockerfile preview --- Dockerfile | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..7b9ba8a --- /dev/null +++ b/Dockerfile @@ -0,0 +1,30 @@ +FROM ubuntu:22.04 + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get install -y \ + python3.10 \ + python3.10-venv \ + python3.10-dev \ + python3-pip \ + git \ + gcc \ + build-essential \ + libssl-dev \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /app + +COPY app/requirements.txt /app/ + +RUN pip3 install --no-cache-dir -r requirements.txt + +COPY . /app + +EXPOSE 5000 + +ENV FLASK_APP=app +ENV FLASK_RUN_PORT=5000 +ENV FLASK_RUN_HOST=0.0.0.0 + +CMD ["flask", "run"] From b6d3c665867a64044e8c55d138e2531ee5a7f53f Mon Sep 17 00:00:00 2001 From: LuisPereira23 Date: Tue, 20 Aug 2024 14:08:19 +0100 Subject: [PATCH 6/9] update dockerfile. Initial work on credentials for potential hackaton --- Dockerfile | 12 +- app/app_config/config_countries.py | 5 + app/app_config/config_service.py | 40 ++++ .../credentials_supported/hiid_mdoc.json | 118 ++++++++++++ .../credentials_supported/iban_mdoc.json | 173 ++++++++++++++++++ .../credentials_supported/msisdn_mdoc.json | 127 +++++++++++++ .../credentials_supported/por_mdoc.json | 110 +++++++++++ .../credentials_supported/tax_mdoc.json | 128 +++++++++++++ 8 files changed, 712 insertions(+), 1 deletion(-) create mode 100644 app/metadata_config/credentials_supported/hiid_mdoc.json create mode 100644 app/metadata_config/credentials_supported/iban_mdoc.json create mode 100644 app/metadata_config/credentials_supported/msisdn_mdoc.json create mode 100644 app/metadata_config/credentials_supported/por_mdoc.json create mode 100644 app/metadata_config/credentials_supported/tax_mdoc.json diff --git a/Dockerfile b/Dockerfile index 7b9ba8a..8ba69c8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,14 +17,24 @@ WORKDIR /app COPY app/requirements.txt /app/ +RUN mkdir -p /etc/eudiw/pid-issuer/cert/ + +RUN mkdir -p /etc/eudiw/pid-issuer/privkey/ + +COPY app/private/certs/ /etc/eudiw/pid-issuer/cert/ + +COPY app/private/privkeys/ /etc/eudiw/pid-issuer/privkey/ + RUN pip3 install --no-cache-dir -r requirements.txt COPY . /app +ENV REQUESTS_CA_BUNDLE=app/cert.pem + EXPOSE 5000 ENV FLASK_APP=app ENV FLASK_RUN_PORT=5000 ENV FLASK_RUN_HOST=0.0.0.0 -CMD ["flask", "run"] +CMD ["flask", "run", "--cert=app/cert.pem", "--key=app/key.pem"] diff --git a/app/app_config/config_countries.py b/app/app_config/config_countries.py index 69df71c..a3e220c 100644 --- a/app/app_config/config_countries.py +++ b/app/app_config/config_countries.py @@ -71,6 +71,11 @@ class ConfCountries: "eu.europa.ec.eudi.pseudonym_over18_mdoc", "eu.europa.ec.eudi.pseudonym_over18_mdoc_deferred_endpoint", "eu.europa.ec.eudi.photoid", + "eu.europa.ec.eudi.por_mdoc", + "eu.europa.ec.eudi.iban_mdoc", + "eu.europa.ec.eudi.hiid_mdoc", + "eu.europa.ec.eudi.tax_mdoc", + "eu.europa.ec.eudi.msisdn_mdoc", ], "dynamic_R2": cfgserv.service_url + "dynamic/form_R2", }, diff --git a/app/app_config/config_service.py b/app/app_config/config_service.py index 14ca4e8..cc3b252 100644 --- a/app/app_config/config_service.py +++ b/app/app_config/config_service.py @@ -271,6 +271,41 @@ class ConfService: "validity": qeaa_validity, "organization_name": "Test QEAA issuer", "namespace": "org.iso.23220.2.photoid.1", + }, + "eu.europa.ec.eudi.por.1": { + "issuing_authority": "Test QEAA issuer", + "organization_id": pid_organization_id, + "validity": qeaa_validity, + "organization_name": "Test QEAA issuer", + "namespace": "eu.europa.ec.eudi.por.1", + }, + "eu.europa.ec.eudi.iban.1": { + "issuing_authority": "Test QEAA issuer", + "organization_id": pid_organization_id, + "validity": qeaa_validity, + "organization_name": "Test QEAA issuer", + "namespace": "eu.europa.ec.eudi.por.1", + }, + "eu.europa.ec.eudi.hiid.1": { + "issuing_authority": "Test QEAA issuer", + "organization_id": pid_organization_id, + "validity": qeaa_validity, + "organization_name": "Test QEAA issuer", + "namespace": "eu.europa.ec.eudi.hiid.1", + }, + "eu.europa.ec.eudi.tax.1": { + "issuing_authority": "Test QEAA issuer", + "organization_id": pid_organization_id, + "validity": qeaa_validity, + "organization_name": "Test QEAA issuer", + "namespace": "eu.europa.ec.eudi.tax.1", + }, + "eu.europa.ec.eudi.msisdn.1": { + "issuing_authority": "Test QEAA issuer", + "organization_id": pid_organization_id, + "validity": qeaa_validity, + "organization_name": "Test QEAA issuer", + "namespace": "eu.europa.ec.eudi.msisdn.1", } } @@ -289,6 +324,11 @@ class ConfService: "eu.europa.ec.eudi.pseudonym_over18_mdoc", "eu.europa.ec.eudi.pseudonym_over18_mdoc_deferred_endpoint", "eu.europa.ec.eudi.photoid", + "eu.europa.ec.eudi.por_mdoc", + "eu.europa.ec.eudi.iban_mdoc", + "eu.europa.ec.eudi.hiid_mdoc", + "eu.europa.ec.eudi.tax_mdoc", + "eu.europa.ec.eudi.msisdn_mdoc", ], } diff --git a/app/metadata_config/credentials_supported/hiid_mdoc.json b/app/metadata_config/credentials_supported/hiid_mdoc.json new file mode 100644 index 0000000..9b14547 --- /dev/null +++ b/app/metadata_config/credentials_supported/hiid_mdoc.json @@ -0,0 +1,118 @@ +{ +"eu.europa.ec.eudi.hiid_mdoc": { + "format": "mso_mdoc", + "doctype": "eu.europa.ec.eudi.hiid.1", + "scope": "eu.europa.ec.eudi.hiid.1", + "policy": { + "batch_size": 50, + "one_time_use": true + }, + "cryptographic_binding_methods_supported": [ + "jwk", "cose_key" + ], + "credential_alg_values_supported": [ + -7 + ], + "credential_crv_values_supported": [ + 1 + ], + "credential_signing_alg_values_supported": [ + "ES256" + ], + "proof_types_supported": { + "jwt": { + "proof_signing_alg_values_supported": [ + "ES256" + ] + }, + "cwt": { + "proof_signing_alg_values_supported": [ + "ES256" + ], + "proof_alg_values_supported": [ + -7 + ], + "proof_crv_values_supported": [ + 1 + ] + } + }, + "display": [ + { + "name": "PoR", + "locale": "en", + "logo": { + "url": "https://examplestate.com/public/por.png", + "alt_text": "A square figure of a PoR" + } + } + ], + "claims": { + "eu.europa.ec.eudi.hiid.1": { + "health_insurance_id": { + "mandatory": true, + "value_type":"string", + "display": [ + { + "name": "Current Legal person identifier", + "locale": "en" } + ] + }, + "patient_id": { + "mandatory": true, + "value_type":"string", + "display": [ + { + "name": "Current Legal person identifier", + "locale": "en" } + ] + }, + "tax_number": { + "mandatory": true, + "value_type":"string", + "display": [ + { + "name": "Current Legal person identifier", + "locale": "en" } + ] + }, + "one_time_token": { + "mandatory": true, + "value_type":"string", + "display": [ + { + "name": "Current Legal person identifier", + "locale": "en" } + ] + }, + "affiliation_country": { + "mandatory": true, + "value_type":"string", + "display": [ + { + "name": "Current Legal person identifier", + "locale": "en" } + ] + }, + "issuance_date": { + "mandatory": true, + "display": [ + { + "name": "Alpha-2 country code, representing the nationality of the PID User.", + "locale": "en" + } + ] + }, + "expiry_date": { + "mandatory": true, + "display": [ + { + "name": "Alpha-2 country code, representing the nationality of the PID User.", + "locale": "en" + } + ] + } + } + } + } +} \ No newline at end of file diff --git a/app/metadata_config/credentials_supported/iban_mdoc.json b/app/metadata_config/credentials_supported/iban_mdoc.json new file mode 100644 index 0000000..d0901bf --- /dev/null +++ b/app/metadata_config/credentials_supported/iban_mdoc.json @@ -0,0 +1,173 @@ +{ +"eu.europa.ec.eudi.iban_mdoc": { + "format": "mso_mdoc", + "doctype": "eu.europa.ec.eudi.iban.1", + "scope": "eu.europa.ec.eudi.iban.1", + "policy": { + "batch_size": 50, + "one_time_use": true + }, + "cryptographic_binding_methods_supported": [ + "jwk", "cose_key" + ], + "credential_alg_values_supported": [ + -7 + ], + "credential_crv_values_supported": [ + 1 + ], + "credential_signing_alg_values_supported": [ + "ES256" + ], + "proof_types_supported": { + "jwt": { + "proof_signing_alg_values_supported": [ + "ES256" + ] + }, + "cwt": { + "proof_signing_alg_values_supported": [ + "ES256" + ], + "proof_alg_values_supported": [ + -7 + ], + "proof_crv_values_supported": [ + 1 + ] + } + }, + "display": [ + { + "name": "PoR", + "locale": "en", + "logo": { + "url": "https://examplestate.com/public/por.png", + "alt_text": "A square figure of a PoR" + } + } + ], + "claims": { + "eu.europa.ec.eudi.iban.1": { + "iban": { + "mandatory": true, + "value_type":"string", + "display": [ + { + "name": "", + "locale": "en" } + ] + }, + "currency": { + "mandatory": true, + "value_type":"string", + "display": [ + { + "name": "", + "locale": "en" } + ] + }, + "bank_account_status": { + "mandatory": true, + "value_type":"string", + "display": [ + { + "name": "", + "locale": "en" } + ] + }, + "payment_possibility": { + "mandatory": true, + "value_type":"bool", + "display": [ + { + "name": "", + "locale": "en" } + ] + }, + "registered_family_name": { + "mandatory": true, + "value_type":"string", + "display": [ + { + "name": "", + "locale": "en" } + ] + }, + "registered_given_name": { + "mandatory": true, + "value_type":"string", + "display": [ + { + "name": "", + "locale": "en" } + ] + }, + "date_of_birth": { + "mandatory": true, + "value_type":"full-date", + "display": [ + { + "name": "Adult or minor", + "locale": "en" + } + ] + }, + "account_holder_owner": { + "mandatory": true, + "value_type":"bool", + "display": [ + { + "name": "", + "locale": "en" } + ] + }, + "coowner": { + "mandatory": true, + "value_type":"bool", + "display": [ + { + "name": "", + "locale": "en" } + ] + }, + "issuing_organization": { + "mandatory": true, + "value_type":"string", + "display": [ + { + "name": "", + "locale": "en" } + ] + }, + "business_identifier_code": { + "mandatory": true, + "value_type":"string", + "display": [ + { + "name": "", + "locale": "en" } + ] + }, + "issuance_date": { + "mandatory": true, + "display": [ + { + "name": "Alpha-2 country code, representing the nationality of the PID User.", + "locale": "en" + } + ] + }, + "expiry_date": { + "mandatory": true, + "display": [ + { + "name": "Alpha-2 country code, representing the nationality of the PID User.", + "locale": "en" + } + ] + } + } + } + } +} \ No newline at end of file diff --git a/app/metadata_config/credentials_supported/msisdn_mdoc.json b/app/metadata_config/credentials_supported/msisdn_mdoc.json new file mode 100644 index 0000000..e9a11d8 --- /dev/null +++ b/app/metadata_config/credentials_supported/msisdn_mdoc.json @@ -0,0 +1,127 @@ +{ +"eu.europa.ec.eudi.msisdn_mdoc": { + "format": "mso_mdoc", + "doctype": "eu.europa.ec.eudi.msisdn.1", + "scope": "eu.europa.ec.eudi.msisdn.1", + "policy": { + "batch_size": 50, + "one_time_use": true + }, + "cryptographic_binding_methods_supported": [ + "jwk", "cose_key" + ], + "credential_alg_values_supported": [ + -7 + ], + "credential_crv_values_supported": [ + 1 + ], + "credential_signing_alg_values_supported": [ + "ES256" + ], + "proof_types_supported": { + "jwt": { + "proof_signing_alg_values_supported": [ + "ES256" + ] + }, + "cwt": { + "proof_signing_alg_values_supported": [ + "ES256" + ], + "proof_alg_values_supported": [ + -7 + ], + "proof_crv_values_supported": [ + 1 + ] + } + }, + "display": [ + { + "name": "PoR", + "locale": "en", + "logo": { + "url": "https://examplestate.com/public/por.png", + "alt_text": "A square figure of a PoR" + } + } + ], + "claims": { + "eu.europa.ec.eudi.msisdn.1": { + "phone_number": { + "mandatory": true, + "value_type":"string", + "display": [ + { + "name": "Current Legal person identifier", + "locale": "en" } + ] + }, + "registered_family_name": { + "mandatory": true, + "value_type":"string", + "display": [ + { + "name": "Current Legal person identifier", + "locale": "en" } + ] + }, + "registered_given_name": { + "mandatory": true, + "value_type":"string", + "display": [ + { + "name": "Current legal Name", + "locale": "en" } + ] + }, + "contract_owner": { + "mandatory": true, + "value_type":"bool", + "display": [ + { + "name": "Date of Birth", + "locale": "en" } + ] + }, + "end_user": { + "mandatory": true, + "value_type":"bool", + "display": [ + { + "name": "Date of Birth", + "locale": "en" } + ] + }, + "mobile_operator": { + "mandatory": true, + "value_type":"string", + "display": [ + { + "name": "Current legal Name", + "locale": "en" } + ] + }, + "issuance_date": { + "mandatory": true, + "display": [ + { + "name": "Alpha-2 country code, representing the nationality of the PID User.", + "locale": "en" + } + ] + }, + "expiry_date": { + "mandatory": true, + "display": [ + { + "name": "Alpha-2 country code, representing the nationality of the PID User.", + "locale": "en" + } + ] + } + } + } + } +} \ No newline at end of file diff --git a/app/metadata_config/credentials_supported/por_mdoc.json b/app/metadata_config/credentials_supported/por_mdoc.json new file mode 100644 index 0000000..e77d850 --- /dev/null +++ b/app/metadata_config/credentials_supported/por_mdoc.json @@ -0,0 +1,110 @@ +{ +"eu.europa.ec.eudi.por_mdoc": { + "format": "mso_mdoc", + "doctype": "eu.europa.ec.eudi.por.1", + "scope": "eu.europa.ec.eudi.por.1", + "policy": { + "batch_size": 50, + "one_time_use": true + }, + "cryptographic_binding_methods_supported": [ + "jwk", "cose_key" + ], + "credential_alg_values_supported": [ + -7 + ], + "credential_crv_values_supported": [ + 1 + ], + "credential_signing_alg_values_supported": [ + "ES256" + ], + "proof_types_supported": { + "jwt": { + "proof_signing_alg_values_supported": [ + "ES256" + ] + }, + "cwt": { + "proof_signing_alg_values_supported": [ + "ES256" + ], + "proof_alg_values_supported": [ + -7 + ], + "proof_crv_values_supported": [ + 1 + ] + } + }, + "display": [ + { + "name": "PoR", + "locale": "en", + "logo": { + "url": "https://examplestate.com/public/por.png", + "alt_text": "A square figure of a PoR" + } + } + ], + "claims": { + "eu.europa.ec.eudi.por.1": { + "legal_person_identifier": { + "mandatory": true, + "value_type":"string", + "display": [ + { + "name": "Current Legal person identifier", + "locale": "en" } + ] + }, + "legal_name": { + "mandatory": true, + "value_type":"string", + "display": [ + { + "name": "Current legal Name", + "locale": "en" } + ] + }, + "full_powers": { + "mandatory": true, + "value_type":"bool", + "display": [ + { + "name": "Date of Birth", + "locale": "en" } + ] + }, + "effective_from_date": { + "mandatory": true, + "value_type":"full-date", + "display": [ + { + "name": "Adult or minor", + "locale": "en" + } + ] + }, + "issuance_date": { + "mandatory": true, + "display": [ + { + "name": "Alpha-2 country code, representing the nationality of the PID User.", + "locale": "en" + } + ] + }, + "expiry_date": { + "mandatory": true, + "display": [ + { + "name": "Alpha-2 country code, representing the nationality of the PID User.", + "locale": "en" + } + ] + } + } + } + } +} \ No newline at end of file diff --git a/app/metadata_config/credentials_supported/tax_mdoc.json b/app/metadata_config/credentials_supported/tax_mdoc.json new file mode 100644 index 0000000..93803f9 --- /dev/null +++ b/app/metadata_config/credentials_supported/tax_mdoc.json @@ -0,0 +1,128 @@ +{ +"eu.europa.ec.eudi.tax_mdoc": { + "format": "mso_mdoc", + "doctype": "eu.europa.ec.eudi.tax.1", + "scope": "eu.europa.ec.eudi.tax.1", + "policy": { + "batch_size": 50, + "one_time_use": true + }, + "cryptographic_binding_methods_supported": [ + "jwk", "cose_key" + ], + "credential_alg_values_supported": [ + -7 + ], + "credential_crv_values_supported": [ + 1 + ], + "credential_signing_alg_values_supported": [ + "ES256" + ], + "proof_types_supported": { + "jwt": { + "proof_signing_alg_values_supported": [ + "ES256" + ] + }, + "cwt": { + "proof_signing_alg_values_supported": [ + "ES256" + ], + "proof_alg_values_supported": [ + -7 + ], + "proof_crv_values_supported": [ + 1 + ] + } + }, + "display": [ + { + "name": "PoR", + "locale": "en", + "logo": { + "url": "https://examplestate.com/public/por.png", + "alt_text": "A square figure of a PoR" + } + } + ], + "claims": { + "eu.europa.ec.eudi.tax.1": { + "tax_number": { + "mandatory": true, + "value_type":"string", + "display": [ + { + "name": "Current Legal person identifier", + "locale": "en" } + ] + }, + "affiliation_country": { + "mandatory": true, + "value_type":"string", + "display": [ + { + "name": "Current legal Name", + "locale": "en" } + ] + }, + "registered_given_name": { + "mandatory": true, + "value_type":"string", + "display": [ + { + "name": "Current Legal person identifier", + "locale": "en" } + ] + }, + "registered_family_name": { + "mandatory": true, + "value_type":"string", + "display": [ + { + "name": "Current legal Name", + "locale": "en" } + ] + }, + "resident_address": { + "mandatory": true, + "value_type":"string", + "display": [ + { + "name": "Current legal Name", + "locale": "en" } + ] + }, + "birth_date": { + "mandatory": true, + "value_type":"full-date", + "display": [ + { + "name": "Adult or minor", + "locale": "en" + } + ] + }, + "issuance_date": { + "mandatory": true, + "display": [ + { + "name": "Alpha-2 country code, representing the nationality of the PID User.", + "locale": "en" + } + ] + }, + "expiry_date": { + "mandatory": true, + "display": [ + { + "name": "Alpha-2 country code, representing the nationality of the PID User.", + "locale": "en" + } + ] + } + } + } + } +} \ No newline at end of file From dc97b134bd7f6bc874c77535955dfaeaa5502411 Mon Sep 17 00:00:00 2001 From: LuisPereira23 Date: Wed, 21 Aug 2024 15:57:04 +0100 Subject: [PATCH 7/9] fix boolean html, added login option to potential creds --- app/app_config/config_service.py | 7 +- app/route_dynamic.py | 10 +-- app/route_oid4vp.py | 98 ++++++++++++++++++------- app/templates/dynamic/dynamic-form.html | 27 ++++++- 4 files changed, 106 insertions(+), 36 deletions(-) diff --git a/app/app_config/config_service.py b/app/app_config/config_service.py index cc3b252..09e2ecf 100644 --- a/app/app_config/config_service.py +++ b/app/app_config/config_service.py @@ -284,7 +284,7 @@ class ConfService: "organization_id": pid_organization_id, "validity": qeaa_validity, "organization_name": "Test QEAA issuer", - "namespace": "eu.europa.ec.eudi.por.1", + "namespace": "eu.europa.ec.eudi.iban.1", }, "eu.europa.ec.eudi.hiid.1": { "issuing_authority": "Test QEAA issuer", @@ -314,6 +314,11 @@ class ConfService: "PID_login": [ "eu.europa.ec.eudi.pseudonym_over18_mdoc", "eu.europa.ec.eudi.pseudonym_over18_mdoc_deferred_endpoint", + "eu.europa.ec.eudi.por_mdoc", + "eu.europa.ec.eudi.iban_mdoc", + "eu.europa.ec.eudi.hiid_mdoc", + "eu.europa.ec.eudi.tax_mdoc", + "eu.europa.ec.eudi.msisdn_mdoc", ], "country_selection": [ "eu.europa.ec.eudi.loyalty_mdoc", diff --git a/app/route_dynamic.py b/app/route_dynamic.py index e34a6e1..24acccc 100644 --- a/app/route_dynamic.py +++ b/app/route_dynamic.py @@ -921,12 +921,12 @@ def Dynamic_form(): DrivingPrivileges.append(drivP) cleaned_data["driving_privileges"] = json.dumps(DrivingPrivileges) + + elif form_data[item] == "true": + cleaned_data[item] = True - elif item == "age_over_18": - if form_data[item] == "on": - cleaned_data["age_over_18"] = True - else: - cleaned_data["age_over_18"] = False + elif form_data[item] == "false": + cleaned_data[item] = False else: cleaned_data[item] = form_data[item] diff --git a/app/route_oid4vp.py b/app/route_oid4vp.py index a50f9d5..6374ce6 100644 --- a/app/route_oid4vp.py +++ b/app/route_oid4vp.py @@ -30,7 +30,7 @@ from flask_cors import CORS import requests import segno -from misc import generate_unique_id, authentication_error_redirect +from misc import generate_unique_id, authentication_error_redirect, getAttributesForm, scope2details from formatter_func import cbor2elems from app.validate_vp_token import validate_vp_token @@ -285,6 +285,7 @@ def getpidoid4vp(): ) mdoc_json = cbor2elems(response.json()["vp_token"] + "==") + is_ageOver18 = False attributesForm = {} if ( @@ -303,6 +304,7 @@ def getpidoid4vp(): or cred_request["credential_configuration_id"] == "eu.europa.ec.eudi.pseudonym_over18_mdoc_deferred_endpoint" ): + is_ageOver18 = True attributesForm.update({"user_pseudonym": str(uuid4())}) elif "vct" in cred_request: if cred_request["vct"] == "eu.europa.ec.eudi.pseudonym_jwt_vc_json": @@ -317,38 +319,78 @@ def getpidoid4vp(): or "eu.europa.ec.eudi.pseudonym.age_over_18.deferred_endpoint" in cred_scopes ): + is_ageOver18 = True attributesForm.update({"user_pseudonym": str(uuid4())}) - for doctype in mdoc_json: - for attribute, value in mdoc_json[doctype]: - if attribute == "age_over_18": - attributesForm.update({attribute: value}) - - doctype_config = cfgservice.config_doctype["eu.europa.ec.eudi.pseudonym.age_over_18.1"] + if is_ageOver18 == True: + for doctype in mdoc_json: + for attribute, value in mdoc_json[doctype]: + if attribute == "age_over_18": + attributesForm.update({attribute: value}) + + doctype_config = cfgservice.config_doctype["eu.europa.ec.eudi.pseudonym.age_over_18.1"] - attributesForm.update({"issuing_country": "FC"}) - attributesForm.update({"issuing_authority": doctype_config["issuing_authority"]}) + attributesForm.update({"issuing_country": "FC"}) + attributesForm.update({"issuing_authority": doctype_config["issuing_authority"]}) - user_id = generate_unique_id() - form_dynamic_data[user_id] = attributesForm - - presentation_data = attributesForm.copy() + user_id = generate_unique_id() + form_dynamic_data[user_id] = attributesForm + + presentation_data = attributesForm.copy() - today = date.today() - expiry = today + timedelta(days=doctype_config["validity"]) - + today = date.today() + expiry = today + timedelta(days=doctype_config["validity"]) + - presentation_data.update({"estimated_issuance_date": today.strftime("%Y-%m-%d")}) - presentation_data.update({"estimated_expiry_date": expiry.strftime("%Y-%m-%d")}) - presentation_data.update({}) - presentation_data.update({}) + presentation_data.update({"estimated_issuance_date": today.strftime("%Y-%m-%d")}) + presentation_data.update({"estimated_expiry_date": expiry.strftime("%Y-%m-%d")}) + presentation_data.update({}) + presentation_data.update({}) - if "jws_token" not in session and "authorization_params" in session: - session["jws_token"] = session["authorization_params"]["token"] + if "jws_token" not in session and "authorization_params" in session: + session["jws_token"] = session["authorization_params"]["token"] - return render_template( - "dynamic/form_authorize_oid4vp.html", - attributes=presentation_data, - user_id="FC." + user_id, - redirect_url=cfgservice.service_url + "dynamic/redirect_wallet", - ) \ No newline at end of file + + return render_template( + "dynamic/form_authorize_oid4vp.html", + attributes=presentation_data, + user_id="FC." + user_id, + redirect_url=cfgservice.service_url + "dynamic/redirect_wallet", + ) + else: + + authorization_params = session["authorization_params"] + authorization_details = [] + if "authorization_details" in authorization_params: + authorization_details.extend( + json.loads(authorization_params["authorization_details"]) + ) + if "scope" in authorization_params: + authorization_details.extend(scope2details(authorization_params["scope"])) + + if not authorization_details: + return authentication_error_redirect( + jws_token=authorization_params["token"], + error="invalid authentication", + error_description="No authorization details or scope found in dynamic route.", + ) + + credentials_requested = [] + for cred in authorization_details: + if "credential_configuration_id" in cred: + if cred["credential_configuration_id"] not in credentials_requested: + credentials_requested.append(cred["credential_configuration_id"]) + elif "vct" in cred: + if cred["vct"] not in credentials_requested: + credentials_requested.append(cred["vct"]) + + session["credentials_requested"] = credentials_requested + + attributesForm = getAttributesForm(credentials_requested) + if "user_pseudonym" in attributesForm: + attributesForm.update({"user_pseudonym": str(uuid4())}) + return render_template( + "dynamic/dynamic-form.html", + attributes=attributesForm, + redirect_url=cfgservice.service_url + "dynamic/form", + ) \ No newline at end of file diff --git a/app/templates/dynamic/dynamic-form.html b/app/templates/dynamic/dynamic-form.html index 5a32654..1055172 100644 --- a/app/templates/dynamic/dynamic-form.html +++ b/app/templates/dynamic/dynamic-form.html @@ -131,9 +131,32 @@

Test Provider Form

- + + + +
-
+ + {% elif name == 'user_pseudonym' %}
From 69a45de6479b6faf8a38042de5af25a4408736f5 Mon Sep 17 00:00:00 2001 From: LuisPereira23 Date: Mon, 26 Aug 2024 13:55:39 +0100 Subject: [PATCH 8/9] update to hackaton credentials --- app/app_config/config_service.py | 2 + app/dynamic_func.py | 3 ++ .../credentials_supported/hiid_mdoc.json | 20 ++++---- .../credentials_supported/iban_mdoc.json | 12 ++--- .../credentials_supported/msisdn_mdoc.json | 50 +++++++++++++++---- .../credentials_supported/por_mdoc.json | 14 +++--- .../credentials_supported/tax_mdoc.json | 31 ++++++++---- app/route_dynamic.py | 13 +++-- app/route_oid4vp.py | 2 + app/route_oidc.py | 6 +-- 10 files changed, 101 insertions(+), 52 deletions(-) diff --git a/app/app_config/config_service.py b/app/app_config/config_service.py index 09e2ecf..1c0ad4d 100644 --- a/app/app_config/config_service.py +++ b/app/app_config/config_service.py @@ -299,6 +299,7 @@ class ConfService: "validity": qeaa_validity, "organization_name": "Test QEAA issuer", "namespace": "eu.europa.ec.eudi.tax.1", + "credential_type": "Tax Number" }, "eu.europa.ec.eudi.msisdn.1": { "issuing_authority": "Test QEAA issuer", @@ -306,6 +307,7 @@ class ConfService: "validity": qeaa_validity, "organization_name": "Test QEAA issuer", "namespace": "eu.europa.ec.eudi.msisdn.1", + "credential_type": "MSISDN", } } diff --git a/app/dynamic_func.py b/app/dynamic_func.py index a18fb74..fedc3ba 100644 --- a/app/dynamic_func.py +++ b/app/dynamic_func.py @@ -134,6 +134,9 @@ def formatter(data, un_distinguishing_sign, doctype, format): data.update({"issue_date": today.strftime("%Y-%m-%d")}) data.update({"expiry_date": expiry.strftime("%Y-%m-%d")}) data.update({"issuing_authority": doctype_config["issuing_authority"]}) + if "credential_type" in doctype_config: + data.update({"credential_type":doctype_config["credential_type"] }) + if "driving_privileges" in attributes_req: json_priv = json.loads(data["driving_privileges"]) diff --git a/app/metadata_config/credentials_supported/hiid_mdoc.json b/app/metadata_config/credentials_supported/hiid_mdoc.json index 9b14547..2afb4f7 100644 --- a/app/metadata_config/credentials_supported/hiid_mdoc.json +++ b/app/metadata_config/credentials_supported/hiid_mdoc.json @@ -39,11 +39,11 @@ }, "display": [ { - "name": "PoR", + "name": "hiid", "locale": "en", "logo": { - "url": "https://examplestate.com/public/por.png", - "alt_text": "A square figure of a PoR" + "url": "https://examplestate.com/public/hiid.png", + "alt_text": "A square figure of a hiid" } } ], @@ -54,7 +54,7 @@ "value_type":"string", "display": [ { - "name": "Current Legal person identifier", + "name": "", "locale": "en" } ] }, @@ -63,7 +63,7 @@ "value_type":"string", "display": [ { - "name": "Current Legal person identifier", + "name": "", "locale": "en" } ] }, @@ -72,7 +72,7 @@ "value_type":"string", "display": [ { - "name": "Current Legal person identifier", + "name": "", "locale": "en" } ] }, @@ -81,7 +81,7 @@ "value_type":"string", "display": [ { - "name": "Current Legal person identifier", + "name": "", "locale": "en" } ] }, @@ -90,7 +90,7 @@ "value_type":"string", "display": [ { - "name": "Current Legal person identifier", + "name": "", "locale": "en" } ] }, @@ -98,7 +98,7 @@ "mandatory": true, "display": [ { - "name": "Alpha-2 country code, representing the nationality of the PID User.", + "name": "", "locale": "en" } ] @@ -107,7 +107,7 @@ "mandatory": true, "display": [ { - "name": "Alpha-2 country code, representing the nationality of the PID User.", + "name": "", "locale": "en" } ] diff --git a/app/metadata_config/credentials_supported/iban_mdoc.json b/app/metadata_config/credentials_supported/iban_mdoc.json index d0901bf..2aa65ae 100644 --- a/app/metadata_config/credentials_supported/iban_mdoc.json +++ b/app/metadata_config/credentials_supported/iban_mdoc.json @@ -39,11 +39,11 @@ }, "display": [ { - "name": "PoR", + "name": "iban", "locale": "en", "logo": { - "url": "https://examplestate.com/public/por.png", - "alt_text": "A square figure of a PoR" + "url": "https://examplestate.com/public/iban.png", + "alt_text": "A square figure of a iban" } } ], @@ -108,7 +108,7 @@ "value_type":"full-date", "display": [ { - "name": "Adult or minor", + "name": "", "locale": "en" } ] @@ -153,7 +153,7 @@ "mandatory": true, "display": [ { - "name": "Alpha-2 country code, representing the nationality of the PID User.", + "name": "", "locale": "en" } ] @@ -162,7 +162,7 @@ "mandatory": true, "display": [ { - "name": "Alpha-2 country code, representing the nationality of the PID User.", + "name": "", "locale": "en" } ] diff --git a/app/metadata_config/credentials_supported/msisdn_mdoc.json b/app/metadata_config/credentials_supported/msisdn_mdoc.json index e9a11d8..c4c0eb3 100644 --- a/app/metadata_config/credentials_supported/msisdn_mdoc.json +++ b/app/metadata_config/credentials_supported/msisdn_mdoc.json @@ -39,11 +39,11 @@ }, "display": [ { - "name": "PoR", + "name": "msisdn", "locale": "en", "logo": { - "url": "https://examplestate.com/public/por.png", - "alt_text": "A square figure of a PoR" + "url": "https://examplestate.com/public/msisdn.png", + "alt_text": "A square figure of a msisdn" } } ], @@ -54,7 +54,7 @@ "value_type":"string", "display": [ { - "name": "Current Legal person identifier", + "name": "", "locale": "en" } ] }, @@ -63,7 +63,7 @@ "value_type":"string", "display": [ { - "name": "Current Legal person identifier", + "name": "", "locale": "en" } ] }, @@ -72,7 +72,7 @@ "value_type":"string", "display": [ { - "name": "Current legal Name", + "name": "", "locale": "en" } ] }, @@ -81,7 +81,7 @@ "value_type":"bool", "display": [ { - "name": "Date of Birth", + "name": "", "locale": "en" } ] }, @@ -90,7 +90,7 @@ "value_type":"bool", "display": [ { - "name": "Date of Birth", + "name": "", "locale": "en" } ] }, @@ -99,7 +99,7 @@ "value_type":"string", "display": [ { - "name": "Current legal Name", + "name": "", "locale": "en" } ] }, @@ -107,7 +107,7 @@ "mandatory": true, "display": [ { - "name": "Alpha-2 country code, representing the nationality of the PID User.", + "name": "", "locale": "en" } ] @@ -116,7 +116,35 @@ "mandatory": true, "display": [ { - "name": "Alpha-2 country code, representing the nationality of the PID User.", + "name": "", + "locale": "en" + } + ] + }, + "credential_type": { + "mandatory": true, + "display": [ + { + "name": "", + "locale": "en" + } + ] + }, + "issuing_organization": { + "mandatory": true, + "value_type":"string", + "display": [ + { + "name": "", + "locale": "en" } + ] + }, + "verification_date": { + "mandatory": true, + "value_type":"full-date", + "display": [ + { + "name": "", "locale": "en" } ] diff --git a/app/metadata_config/credentials_supported/por_mdoc.json b/app/metadata_config/credentials_supported/por_mdoc.json index e77d850..07215ae 100644 --- a/app/metadata_config/credentials_supported/por_mdoc.json +++ b/app/metadata_config/credentials_supported/por_mdoc.json @@ -54,7 +54,7 @@ "value_type":"string", "display": [ { - "name": "Current Legal person identifier", + "name": "", "locale": "en" } ] }, @@ -63,7 +63,7 @@ "value_type":"string", "display": [ { - "name": "Current legal Name", + "name": "", "locale": "en" } ] }, @@ -72,16 +72,16 @@ "value_type":"bool", "display": [ { - "name": "Date of Birth", + "name": "", "locale": "en" } ] }, "effective_from_date": { "mandatory": true, - "value_type":"full-date", + "value_type":"", "display": [ { - "name": "Adult or minor", + "name": "", "locale": "en" } ] @@ -90,7 +90,7 @@ "mandatory": true, "display": [ { - "name": "Alpha-2 country code, representing the nationality of the PID User.", + "name": "", "locale": "en" } ] @@ -99,7 +99,7 @@ "mandatory": true, "display": [ { - "name": "Alpha-2 country code, representing the nationality of the PID User.", + "name": "", "locale": "en" } ] diff --git a/app/metadata_config/credentials_supported/tax_mdoc.json b/app/metadata_config/credentials_supported/tax_mdoc.json index 93803f9..78cefd5 100644 --- a/app/metadata_config/credentials_supported/tax_mdoc.json +++ b/app/metadata_config/credentials_supported/tax_mdoc.json @@ -39,11 +39,11 @@ }, "display": [ { - "name": "PoR", + "name": "tax", "locale": "en", "logo": { - "url": "https://examplestate.com/public/por.png", - "alt_text": "A square figure of a PoR" + "url": "https://examplestate.com/public/tax.png", + "alt_text": "A square figure of a tax" } } ], @@ -54,7 +54,7 @@ "value_type":"string", "display": [ { - "name": "Current Legal person identifier", + "name": "", "locale": "en" } ] }, @@ -63,7 +63,7 @@ "value_type":"string", "display": [ { - "name": "Current legal Name", + "name": "", "locale": "en" } ] }, @@ -72,7 +72,7 @@ "value_type":"string", "display": [ { - "name": "Current Legal person identifier", + "name": "", "locale": "en" } ] }, @@ -81,7 +81,7 @@ "value_type":"string", "display": [ { - "name": "Current legal Name", + "name": "", "locale": "en" } ] }, @@ -90,7 +90,7 @@ "value_type":"string", "display": [ { - "name": "Current legal Name", + "name": "", "locale": "en" } ] }, @@ -99,7 +99,7 @@ "value_type":"full-date", "display": [ { - "name": "Adult or minor", + "name": "", "locale": "en" } ] @@ -108,7 +108,7 @@ "mandatory": true, "display": [ { - "name": "Alpha-2 country code, representing the nationality of the PID User.", + "name": "", "locale": "en" } ] @@ -117,7 +117,16 @@ "mandatory": true, "display": [ { - "name": "Alpha-2 country code, representing the nationality of the PID User.", + "name": "", + "locale": "en" + } + ] + }, + "credential_type": { + "mandatory": true, + "display": [ + { + "name": "", "locale": "en" } ] diff --git a/app/route_dynamic.py b/app/route_dynamic.py index 24acccc..cd8e4f2 100644 --- a/app/route_dynamic.py +++ b/app/route_dynamic.py @@ -396,6 +396,8 @@ def red(): form_data[doctype].update({"estimated_expiry_date":expiry.strftime("%Y-%m-%d")}) form_data[doctype].update({"issuing_country": session["country"]}) form_data[doctype].update({"issuing_authority":doctype_config["issuing_authority"] }) + if "credential_type" in doctype_config: + form_data[doctype].update({"credential_type":doctype_config["credential_type"] }) user_id=session["country"] + "." + token + "&authenticationContextId=" + r1.json()["authenticationContextId"] @@ -495,6 +497,9 @@ def red(): presentation_data[credential].update({"estimated_expiry_date":expiry.strftime("%Y-%m-%d")}) presentation_data[credential].update({"issuing_country": session["country"]}), presentation_data[credential].update({"issuing_authority": doctype_config["issuing_authority"]}) + if "credential_type" in doctype_config: + presentation_data[doctype].update({"credential_type":doctype_config["credential_type"] }) + if "birth_date" in presentation_data[credential]: presentation_data[credential].update({"age_over_18": True if calculate_age(presentation_data[credential]["birth_date"]) >= 18 else False}) @@ -940,9 +945,6 @@ def Dynamic_form(): } ) - if "age_over_18" not in cleaned_data: - cleaned_data["age_over_18"] = False - print("\n-----Cleaned Data----\n", cleaned_data) form_dynamic_data[user_id] = cleaned_data @@ -985,7 +987,10 @@ def Dynamic_form(): presentation_data[credential].update({"estimated_expiry_date":expiry.strftime("%Y-%m-%d")}) presentation_data[credential].update({"issuing_country": session["country"]}), presentation_data[credential].update({"issuing_authority": doctype_config["issuing_authority"]}) - if "birth_date" in presentation_data[credential]: + if "credential_type" in doctype_config: + presentation_data[credential].update({"credential_type":doctype_config["credential_type"] }) + + if "birth_date" in presentation_data[credential] and "age_over_18" in presentation_data[credential]: presentation_data[credential].update({"age_over_18": True if calculate_age(presentation_data[credential]["birth_date"]) >= 18 else False}) diff --git a/app/route_oid4vp.py b/app/route_oid4vp.py index 6374ce6..57a98b7 100644 --- a/app/route_oid4vp.py +++ b/app/route_oid4vp.py @@ -332,6 +332,8 @@ def getpidoid4vp(): attributesForm.update({"issuing_country": "FC"}) attributesForm.update({"issuing_authority": doctype_config["issuing_authority"]}) + if "credential_type" in doctype_config: + attributesForm.update({"credential_type":doctype_config["credential_type"] }) user_id = generate_unique_id() form_dynamic_data[user_id] = attributesForm diff --git a/app/route_oidc.py b/app/route_oidc.py index 3e8b962..46b5cff 100644 --- a/app/route_oidc.py +++ b/app/route_oidc.py @@ -744,16 +744,16 @@ def notification(): access_token = headers["Authorization"][6:] session_id = getSessionId_accessToken(access_token) - log.logger_info.info(", Session ID: " + session_id + ", " + "Notification Request, Payload: " + str(payload)) + #log.logger_info.info(", Session ID: " + session_id + ", " + "Notification Request, Payload: " + str(payload)) _resp = service_endpoint(current_app.server.get_endpoint("notification")) if isinstance(_resp,Response): - log.logger_info.info(", Session ID: " + session_id + ", " + "Notification response, Payload: " + str(_resp)) + #log.logger_info.info(", Session ID: " + session_id + ", " + "Notification response, Payload: " + str(_resp)) return _resp - log.logger_info.info(", Session ID: " + session_id + ", " + "Notification response, Payload: " + str(_resp)) + #log.logger_info.info(", Session ID: " + session_id + ", " + "Notification response, Payload: " + str(_resp)) return _resp From 511be97e77dbb5863680dc4f2dbfdffff6a0baf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Miranda?= Date: Mon, 26 Aug 2024 17:58:01 +0100 Subject: [PATCH 9/9] adding changes to hackathon --- README.md | 15 +++++++++++++++ .../test_tokens/DS-token/PID-DS-hackathon.zip | Bin 0 -> 1489 bytes .../PIDIssuerCAUThackathon.cacert.pem | 18 ++++++++++++++++++ 3 files changed, 33 insertions(+) create mode 100644 api_docs/test_tokens/DS-token/PID-DS-hackathon.zip create mode 100644 api_docs/test_tokens/IACA-token/PIDIssuerCAUThackathon.cacert.pem diff --git a/README.md b/README.md index ce5b027..2cebfbc 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,18 @@ + +---- + +# NOTICE + +:heavy_exclamation_mark: This is a development version provided for the POTENTIAL LSP hackathon in Dresden. + +Changes in the configuration process: + ++ You must copy your IACA trusted certificate(s) (in PEM format) to the trusted_CAs_path folder - you can use the [example test IACA certificate for country Utopia (UT)](api_docs/test_tokens/IACA-token/PIDIssuerCAUThackathon.cacert.pem) -. + ++ You can use the [example test private DS keys and certificates, for country Utopia (UT)](api_docs/test_tokens/DS-token/PID-DS-hackathon.zip) - the password of the example test private DS keys is b"123456". + +---- + # EUDIW Issuer [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) diff --git a/api_docs/test_tokens/DS-token/PID-DS-hackathon.zip b/api_docs/test_tokens/DS-token/PID-DS-hackathon.zip new file mode 100644 index 0000000000000000000000000000000000000000..9a26a6fc90a55461d18590121d1d9154ef9560ba GIT binary patch literal 1489 zcmWIWW@Zs#-~d9gh2oJ6P%w>=fkBl)fg!-tMb{-*H#9^yBQZHUu_Plu&%nS?&(O%& z#MDeLJGD};AT>8MgqMNc_TGs!XCMaAr4`%^j4WS)W&n+5U`X{pnAhYWvgfnt`+E|O zE(c6}r`=6;TA;}X~()M_LQZ8{2QC-MeU75u3KJVWm`<8ReY^mG0@16PQ#Mo*WYa4rT z3b!=pI#Z^ zQ`(y1X5VKiD>b*LZt2n)TX+nmB|d6s2u_|BaEUMJRP@I78~g{SM%gSYi%oyFb={j? z`qGySpX|>vQ3(|-@H-^XRyk?=qQ~3M^rX&^SheM+ssFR8X>7|c&AEG#;aWhIOp^|i zZolN5rBZz>&TXjK(0Im1=iM2LWsB1`bllndZ1*)e(fkvAj(_tnJ<-3UaH-DmcK;5E z6&ruBKl(42^}jPiRwQudXIm4NKk|%854hiWJ8X%PI8**X(q~rxqL5!lHtc5%*1GfL ziJj!V%+EJkn16SKJvS=ju3Oq>xp~{)M=fW2qiQnmrW@USzwbjL(`%_YH^=OuI+T<3U~`!C#Ed*)AV#r0Wh@5r@!JdZ!uc<@bX+;5hB+Vc$y zwg~RB{m;hGpy1ZGzSy00hTx)Y+#4?S{YcB76t!N)BIn!bIE&Y1cWV2dtuo->Zu7KV zNp$<2XUnpy`i0hpOk}Z+-(EI{-FoeYwaW~2_ihL`-!9E75prHZw%$$W z^Vsc`$K@CAzxe6G3zziR&s$!(mj8Ud;lTxVtqEJ$)=55HYH4U}@r8R=Pq!w1b^KcW^jkrmZ|vQrzc=kR zo3;Ff{H^Rwo$dc^-_|pM@{IL-uFB&~3=E*05#Y_pB*Kg++>rAVsBnXUhDH#JDn%W# om7t7@>>N-=g@J~~<%~G3BsSXyc(byB3}gbr2SEBZ(9aAE04%a^RR910 literal 0 HcmV?d00001 diff --git a/api_docs/test_tokens/IACA-token/PIDIssuerCAUThackathon.cacert.pem b/api_docs/test_tokens/IACA-token/PIDIssuerCAUThackathon.cacert.pem new file mode 100644 index 0000000..0adb548 --- /dev/null +++ b/api_docs/test_tokens/IACA-token/PIDIssuerCAUThackathon.cacert.pem @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC5zCCAo2gAwIBAgIUPQnDe9TMdWxD5EU/hKgW8OfMiP8wCgYIKoZIzj0EAwIw +XDElMCMGA1UEAwwcUElEIElzc3VlciBDQSAtIFVUIGhhY2thdGhvbjEmMCQGA1UE +CgwdRVVESSBSZWZlcmVuY2UgSW1wbGVtZW50YXRpb24xCzAJBgNVBAYTAlVUMB4X +DTI0MDgyMzE4MDYyNFoXDTMzMTExOTE4MDYyM1owXDElMCMGA1UEAwwcUElEIElz +c3VlciBDQSAtIFVUIGhhY2thdGhvbjEmMCQGA1UECgwdRVVESSBSZWZlcmVuY2Ug +SW1wbGVtZW50YXRpb24xCzAJBgNVBAYTAlVUMFkwEwYHKoZIzj0CAQYIKoZIzj0D +AQcDQgAEsNAL6wdUuFOxlI5A0seZjknuRvUNbPOoMtwWiXtdOOqaxRob5IYUF6g4 +n3riCwj6cjEgKF7IZzk82GnlyeZWBKOCASswggEnMBIGA1UdEwEB/wQIMAYBAf8C +AQAwHwYDVR0jBBgwFoAUQeybFcEY/LhCJLGM+A+hxxfmseQwFgYDVR0lAQH/BAww +CgYIK4ECAgAAAQcwSgYDVR0fBEMwQTA/oD2gO4Y5aHR0cHM6Ly9wcmVwcm9kLnBr +aS5ldWRpdy5kZXYvY3JsL3BpZF9DQV9VVF9oYWNrYXRob24uY3JsMB0GA1UdDgQW +BBRB7JsVwRj8uEIksYz4D6HHF+ax5DAOBgNVHQ8BAf8EBAMCAQYwXQYDVR0SBFYw +VIZSaHR0cHM6Ly9naXRodWIuY29tL2V1LWRpZ2l0YWwtaWRlbnRpdHktd2FsbGV0 +L2FyY2hpdGVjdHVyZS1hbmQtcmVmZXJlbmNlLWZyYW1ld29yazAKBggqhkjOPQQD +AgNIADBFAiEA9mTTG2Id/2cxLO+mr1Q8sKB48ldrL2QfpFPbyFgVsZkCIHlCHaoR +CdJ9i7MTuracg4VRH69ius0DSvxFYJCfTpy+ +-----END CERTIFICATE-----