From 04e133c935a695ed9633cc59a22cd66c328d97bf Mon Sep 17 00:00:00 2001 From: Gavin Jaeger-Freeborn Date: Wed, 22 May 2024 11:55:55 -0700 Subject: [PATCH 01/31] Setup TTL for automatic deletion of auth_sessions Signed-off-by: Gavin Jaeger-Freeborn --- docker/docker-compose.yaml | 1 + docker/manage | 1 + oidc-controller/api/authSessions/models.py | 2 +- oidc-controller/api/core/config.py | 4 ++++ oidc-controller/api/db/session.py | 19 +++++++++++++++++++ 5 files changed, 26 insertions(+), 1 deletion(-) diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 7aa2d0a6..aa388391 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -25,6 +25,7 @@ services: - CONTROLLER_URL=${CONTROLLER_URL} - CONTROLLER_CAMERA_REDIRECT_URL=${CONTROLLER_CAMERA_REDIRECT_URL} - CONTROLLER_PRESENTATION_EXPIRE_TIME=${CONTROLLER_PRESENTATION_EXPIRE_TIME} + - CONTROLLER_PRESENTATION_BUFFER_TIME=${CONTROLLER_PRESENTATION_BUFFER_TIME} - ACAPY_TENANCY=${AGENT_TENANT_MODE} - ACAPY_AGENT_URL=${AGENT_ENDPOINT} - ACAPY_ADMIN_URL=${AGENT_ADMIN_URL} diff --git a/docker/manage b/docker/manage index 832aa427..e01a0e3c 100755 --- a/docker/manage +++ b/docker/manage @@ -171,6 +171,7 @@ configureEnvironment() { # The number of time in seconds a proof request will be valid for export CONTROLLER_PRESENTATION_EXPIRE_TIME=10 + export CONTROLLER_PRESENTATION_BUFFER_TIME=10 #controller app settings export SET_NON_REVOKED="True" diff --git a/oidc-controller/api/authSessions/models.py b/oidc-controller/api/authSessions/models.py index 65fe13d1..dd4a711a 100644 --- a/oidc-controller/api/authSessions/models.py +++ b/oidc-controller/api/authSessions/models.py @@ -29,8 +29,8 @@ class AuthSessionBase(BaseModel): pyop_auth_code: str response_url: str presentation_request_msg: Optional[dict] = None - model_config = ConfigDict(populate_by_name=True) + created_at: datetime = Field(default=datetime.now()) class AuthSession(AuthSessionBase, UUIDModel): diff --git a/oidc-controller/api/core/config.py b/oidc-controller/api/core/config.py index 04e49300..d07e8cd0 100644 --- a/oidc-controller/api/core/config.py +++ b/oidc-controller/api/core/config.py @@ -167,6 +167,10 @@ class GlobalConfig(BaseSettings): "CONTROLLER_PRESENTATION_EXPIRE_TIME", 10 ) + CONTROLLER_PRESENTATION_BUFFER_TIME: int = os.environ.get( + "CONTROLLER_PRESENTATION_BUFFER_TIME", 10 + ) + ACAPY_AGENT_URL: Optional[str] = os.environ.get("ACAPY_AGENT_URL") if not ACAPY_AGENT_URL: logger.warning("ACAPY_AGENT_URL was not provided, agent will not be accessible") diff --git a/oidc-controller/api/db/session.py b/oidc-controller/api/db/session.py index b7c88d0f..80037f36 100644 --- a/oidc-controller/api/db/session.py +++ b/oidc-controller/api/db/session.py @@ -1,6 +1,8 @@ from pymongo import MongoClient, ASCENDING from api.core.config import settings from .collections import COLLECTION_NAMES +from ..core.config import settings +from ..authSessions.models import AuthSessionState async def get_async_session(): @@ -20,8 +22,25 @@ async def init_db(): client_configs.create_index([("client_id", ASCENDING)], unique=True) auth_session = db.get_collection(COLLECTION_NAMES.AUTH_SESSION) + expire_time = settings.CONTROLLER_PRESENTATION_EXPIRE_TIME + settings.CONTROLLER_PRESENTATION_BUFFER_TIME auth_session.create_index([("pres_exch_id", ASCENDING)], unique=True) auth_session.create_index([("pyop_auth_code", ASCENDING)], unique=True) + auth_session.create_index([("created_at", ASCENDING)], + name="expired_ttl", + expireAfterSeconds=expire_time, + partialFilterExpression={"proof_status": + { "$eq": AuthSessionState.EXPIRED.value }}) + auth_session.create_index([("created_at", ASCENDING)], + name="failed_ttl", + expireAfterSeconds=expire_time, + partialFilterExpression={"proof_status": + { "$eq": AuthSessionState.FAILED.value }}) + auth_session.create_index([("created_at", ASCENDING)], + name="abandoned_ttl", + expireAfterSeconds=expire_time, + partialFilterExpression= {"proof_status": + { "$eq": AuthSessionState.ABANDONED.value } + }) async def get_db(): From f4cc0de0dff6663ade70d6ec234d346f31a68d36 Mon Sep 17 00:00:00 2001 From: Gavin Jaeger-Freeborn Date: Wed, 22 May 2024 12:13:40 -0700 Subject: [PATCH 02/31] Minimize repeated code in index creation Signed-off-by: Gavin Jaeger-Freeborn --- oidc-controller/api/db/session.py | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/oidc-controller/api/db/session.py b/oidc-controller/api/db/session.py index 80037f36..3b54b951 100644 --- a/oidc-controller/api/db/session.py +++ b/oidc-controller/api/db/session.py @@ -22,25 +22,19 @@ async def init_db(): client_configs.create_index([("client_id", ASCENDING)], unique=True) auth_session = db.get_collection(COLLECTION_NAMES.AUTH_SESSION) - expire_time = settings.CONTROLLER_PRESENTATION_EXPIRE_TIME + settings.CONTROLLER_PRESENTATION_BUFFER_TIME auth_session.create_index([("pres_exch_id", ASCENDING)], unique=True) auth_session.create_index([("pyop_auth_code", ASCENDING)], unique=True) - auth_session.create_index([("created_at", ASCENDING)], - name="expired_ttl", - expireAfterSeconds=expire_time, - partialFilterExpression={"proof_status": - { "$eq": AuthSessionState.EXPIRED.value }}) - auth_session.create_index([("created_at", ASCENDING)], - name="failed_ttl", - expireAfterSeconds=expire_time, - partialFilterExpression={"proof_status": - { "$eq": AuthSessionState.FAILED.value }}) - auth_session.create_index([("created_at", ASCENDING)], - name="abandoned_ttl", - expireAfterSeconds=expire_time, - partialFilterExpression= {"proof_status": - { "$eq": AuthSessionState.ABANDONED.value } - }) + + expire_time: int = settings.CONTROLLER_PRESENTATION_EXPIRE_TIME + settings.CONTROLLER_PRESENTATION_BUFFER_TIME + + for k, v in [("expired_ttl", AuthSessionState.EXPIRED), + ("failed_ttl", AuthSessionState.FAILED), + ("abandoned_ttl", AuthSessionState.ABANDONED)]: + auth_session.create_index([("created_at", ASCENDING)], + expireAfterSeconds=expire_time, + name=k, + partialFilterExpression={"proof_status": + { "$eq": v.value }}) async def get_db(): From be3b2095c3138bf04ee7a861fe95c289a419e2c4 Mon Sep 17 00:00:00 2001 From: Gavin Jaeger-Freeborn Date: Tue, 28 May 2024 11:40:45 -0700 Subject: [PATCH 03/31] Migrated the configuration of authsession deletion to a json config file Signed-off-by: Gavin Jaeger-Freeborn --- oidc-controller/api/db/session.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/oidc-controller/api/db/session.py b/oidc-controller/api/db/session.py index 3b54b951..6ae96f1a 100644 --- a/oidc-controller/api/db/session.py +++ b/oidc-controller/api/db/session.py @@ -1,4 +1,6 @@ +import json from pymongo import MongoClient, ASCENDING +from pathlib import Path from api.core.config import settings from .collections import COLLECTION_NAMES from ..core.config import settings @@ -25,16 +27,18 @@ async def init_db(): auth_session.create_index([("pres_exch_id", ASCENDING)], unique=True) auth_session.create_index([("pyop_auth_code", ASCENDING)], unique=True) - expire_time: int = settings.CONTROLLER_PRESENTATION_EXPIRE_TIME + settings.CONTROLLER_PRESENTATION_BUFFER_TIME - - for k, v in [("expired_ttl", AuthSessionState.EXPIRED), - ("failed_ttl", AuthSessionState.FAILED), - ("abandoned_ttl", AuthSessionState.ABANDONED)]: - auth_session.create_index([("created_at", ASCENDING)], - expireAfterSeconds=expire_time, - name=k, - partialFilterExpression={"proof_status": - { "$eq": v.value }}) + with open((Path(__file__).parent.parent / "authSessions" / "sessiontimeout.json").resolve()) as user_file: + experation_times: dict[str, int] = json.loads(user_file.read()) + auth_session_states: list[str] = [str(i) for i in list(AuthSessionState)] + for k, v in experation_times.items(): + assert isinstance(k, str) + assert k in auth_session_states + auth_session.create_index( + [("created_at", ASCENDING)], + expireAfterSeconds=v + settings.CONTROLLER_PRESENTATION_BUFFER_TIME, + name=k + "_ttl", + partialFilterExpression={"proof_status": {"$eq": k}}, + ) async def get_db(): From 3f8cd8e2ccf4faf9b1092531cff157d0b91db50d Mon Sep 17 00:00:00 2001 From: Gavin Jaeger-Freeborn Date: Tue, 28 May 2024 11:41:10 -0700 Subject: [PATCH 04/31] Increase expiration buffer to avoid conflicts with keycloak Signed-off-by: Gavin Jaeger-Freeborn --- docker/manage | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/manage b/docker/manage index e01a0e3c..f1e1ac7d 100755 --- a/docker/manage +++ b/docker/manage @@ -171,7 +171,7 @@ configureEnvironment() { # The number of time in seconds a proof request will be valid for export CONTROLLER_PRESENTATION_EXPIRE_TIME=10 - export CONTROLLER_PRESENTATION_BUFFER_TIME=10 + export CONTROLLER_PRESENTATION_BUFFER_TIME=20 #controller app settings export SET_NON_REVOKED="True" From 46f673a0f6a6cbfad40b2f2a41a2a63deee28ea6 Mon Sep 17 00:00:00 2001 From: Gavin Jaeger-Freeborn Date: Tue, 28 May 2024 11:47:35 -0700 Subject: [PATCH 05/31] Added default auth session config Signed-off-by: Gavin Jaeger-Freeborn --- oidc-controller/api/authSessions/sessiontimeout.json | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 oidc-controller/api/authSessions/sessiontimeout.json diff --git a/oidc-controller/api/authSessions/sessiontimeout.json b/oidc-controller/api/authSessions/sessiontimeout.json new file mode 100644 index 00000000..c51b0f6f --- /dev/null +++ b/oidc-controller/api/authSessions/sessiontimeout.json @@ -0,0 +1,5 @@ +{ + "expired": 10, + "failed": 10, + "abandoned": 10 +} From 85e7c3036a0f30a2a66db0678f94249380bf7785 Mon Sep 17 00:00:00 2001 From: Gavin Jaeger-Freeborn Date: Tue, 28 May 2024 14:32:19 -0700 Subject: [PATCH 06/31] Corrected long line Signed-off-by: Gavin Jaeger-Freeborn --- oidc-controller/api/db/session.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/oidc-controller/api/db/session.py b/oidc-controller/api/db/session.py index 6ae96f1a..76d0c9e7 100644 --- a/oidc-controller/api/db/session.py +++ b/oidc-controller/api/db/session.py @@ -27,7 +27,11 @@ async def init_db(): auth_session.create_index([("pres_exch_id", ASCENDING)], unique=True) auth_session.create_index([("pyop_auth_code", ASCENDING)], unique=True) - with open((Path(__file__).parent.parent / "authSessions" / "sessiontimeout.json").resolve()) as user_file: + with open( + ( + Path(__file__).parent.parent / "authSessions" / "sessiontimeout.json" + ).resolve() + ) as user_file: experation_times: dict[str, int] = json.loads(user_file.read()) auth_session_states: list[str] = [str(i) for i in list(AuthSessionState)] for k, v in experation_times.items(): From 6a7bcafa7948a92f7bfa3e0ed9088d5c6b608cfb Mon Sep 17 00:00:00 2001 From: Gavin Jaeger-Freeborn Date: Tue, 28 May 2024 14:34:11 -0700 Subject: [PATCH 07/31] remove duplicate import Signed-off-by: Gavin Jaeger-Freeborn --- oidc-controller/api/db/session.py | 1 - 1 file changed, 1 deletion(-) diff --git a/oidc-controller/api/db/session.py b/oidc-controller/api/db/session.py index 76d0c9e7..70c488b9 100644 --- a/oidc-controller/api/db/session.py +++ b/oidc-controller/api/db/session.py @@ -3,7 +3,6 @@ from pathlib import Path from api.core.config import settings from .collections import COLLECTION_NAMES -from ..core.config import settings from ..authSessions.models import AuthSessionState From 0fd21cfe96be6bd205a00d37814242148b6149dd Mon Sep 17 00:00:00 2001 From: Gavin Jaeger-Freeborn Date: Tue, 28 May 2024 15:29:01 -0700 Subject: [PATCH 08/31] Log errors in sessiontimeout.json Signed-off-by: Gavin Jaeger-Freeborn --- oidc-controller/api/db/session.py | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/oidc-controller/api/db/session.py b/oidc-controller/api/db/session.py index 70c488b9..0160537a 100644 --- a/oidc-controller/api/db/session.py +++ b/oidc-controller/api/db/session.py @@ -1,4 +1,5 @@ import json +import structlog from pymongo import MongoClient, ASCENDING from pathlib import Path from api.core.config import settings @@ -11,6 +12,7 @@ async def get_async_session(): client = MongoClient(settings.MONGODB_URL, uuidRepresentation="standard") +logger: structlog.typing.FilteringBoundLogger = structlog.getLogger(__name__) async def init_db(): @@ -33,15 +35,24 @@ async def init_db(): ) as user_file: experation_times: dict[str, int] = json.loads(user_file.read()) auth_session_states: list[str] = [str(i) for i in list(AuthSessionState)] - for k, v in experation_times.items(): - assert isinstance(k, str) - assert k in auth_session_states - auth_session.create_index( - [("created_at", ASCENDING)], - expireAfterSeconds=v + settings.CONTROLLER_PRESENTATION_BUFFER_TIME, - name=k + "_ttl", - partialFilterExpression={"proof_status": {"$eq": k}}, - ) + try: + for k, v in experation_times.items(): + assert isinstance(k, str) + assert k in auth_session_states + auth_session.create_index( + [("created_at", ASCENDING)], + expireAfterSeconds=v + settings.CONTROLLER_PRESENTATION_BUFFER_TIME, + name=k + "_ttl", + partialFilterExpression={"proof_status": {"$eq": k}}, + ) + except Exception as e: + match e: + case AssertionError(): + logger.error( + "Invalid entry in sessiontimeout.json ", + e, + " no following expiration times will be applied", + ) async def get_db(): From 13736ff682793be406e835e36fce40a3fe93fbf1 Mon Sep 17 00:00:00 2001 From: Gavin Jaeger-Freeborn Date: Tue, 4 Jun 2024 16:47:22 -0700 Subject: [PATCH 09/31] Support restarting the controller and setting the config path as an envvar Signed-off-by: Gavin Jaeger-Freeborn --- docker/docker-compose.yaml | 2 + docker/manage | 3 + oidc-controller/api/core/config.py | 3 + oidc-controller/api/db/session.py | 99 ++++++++++++++++++++++-------- 4 files changed, 81 insertions(+), 26 deletions(-) diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 00eb5721..a1579cd5 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -26,6 +26,7 @@ services: - CONTROLLER_CAMERA_REDIRECT_URL=${CONTROLLER_CAMERA_REDIRECT_URL} - CONTROLLER_PRESENTATION_EXPIRE_TIME=${CONTROLLER_PRESENTATION_EXPIRE_TIME} - CONTROLLER_PRESENTATION_BUFFER_TIME=${CONTROLLER_PRESENTATION_BUFFER_TIME} + - CONTROLLER_SESSION_TIMEOUT_CONFIG_FILE=${CONTROLLER_SESSION_TIMEOUT_CONFIG_FILE} - ACAPY_TENANCY=${AGENT_TENANT_MODE} - ACAPY_AGENT_URL=${AGENT_ENDPOINT} - ACAPY_ADMIN_URL=${AGENT_ADMIN_URL} @@ -40,6 +41,7 @@ services: - 5678:5678 volumes: - ../oidc-controller:/app:rw + - ./oidc-controller/sessiontimeout.json:/tmp/sessiontimeout.json networks: - vc_auth diff --git a/docker/manage b/docker/manage index f1e1ac7d..06c49840 100755 --- a/docker/manage +++ b/docker/manage @@ -173,6 +173,9 @@ configureEnvironment() { export CONTROLLER_PRESENTATION_EXPIRE_TIME=10 export CONTROLLER_PRESENTATION_BUFFER_TIME=20 + # The path to the auth_session timeouts config file + export CONTROLLER_SESSION_TIMEOUT_CONFIG_FILE="/tmp/sessiontimeout.json" + #controller app settings export SET_NON_REVOKED="True" export USE_OOB_PRESENT_PROOF=${USE_OOB_PRESENT_PROOF:-"false"} diff --git a/oidc-controller/api/core/config.py b/oidc-controller/api/core/config.py index d07e8cd0..16578396 100644 --- a/oidc-controller/api/core/config.py +++ b/oidc-controller/api/core/config.py @@ -171,6 +171,9 @@ class GlobalConfig(BaseSettings): "CONTROLLER_PRESENTATION_BUFFER_TIME", 10 ) + CONTROLLER_SESSION_TIMEOUT_CONFIG_FILE: Optional[str] = os.environ.get( + "CONTROLLER_SESSION_TIMEOUT_CONFIG_FILE" + ) ACAPY_AGENT_URL: Optional[str] = os.environ.get("ACAPY_AGENT_URL") if not ACAPY_AGENT_URL: logger.warning("ACAPY_AGENT_URL was not provided, agent will not be accessible") diff --git a/oidc-controller/api/db/session.py b/oidc-controller/api/db/session.py index 0160537a..5d33aee5 100644 --- a/oidc-controller/api/db/session.py +++ b/oidc-controller/api/db/session.py @@ -1,6 +1,8 @@ import json import structlog from pymongo import MongoClient, ASCENDING +from pymongo.collection import Collection +from pymongo.errors import OperationFailure from pathlib import Path from api.core.config import settings from .collections import COLLECTION_NAMES @@ -15,6 +17,68 @@ async def get_async_session(): logger: structlog.typing.FilteringBoundLogger = structlog.getLogger(__name__) +def index_name(k: str) -> str: + return k + "_ttl" + + +def create_ttl_indexes(auth_session: Collection, file: str): + auth_session_states: list[str] = [str(i) for i in list(AuthSessionState)] + # Drop all old indexes if they exist + for state in auth_session_states: + + try: + auth_session.drop_index(index_name(state)) + except OperationFailure as _: + # If this index does not exist just continue + pass + logger.warning("Here") + try: + with open(Path(file).resolve()) as user_file: + expiration_times: dict[str, int] = json.loads(user_file.read()) + # Ensure the given config is valid + if not all( + isinstance(k, str) and (k in auth_session_states) and isinstance(v, int) + for k, v in expiration_times.items() + ): + raise Exception("Invalid json formatting") + + except Exception as e: + match e: + case FileNotFoundError(): + logger.warning( + "The file " + + file + + " does not exist or could not be opened " + + "because of this no auth session timeouts will be applied.", + ) + case json.JSONDecodeError(): + logger.warning( + "Failed to decode the auth session timeouts timout config file " + + file + + " with the following error " + + str(e), + ) + case Exception(): + logger.error( + "There is at least one invalid entry in the file " + + file + + ". Ensure all entries in your session timout file map an " + + "AuthSessionState to an integer " + + "valid auth session strings are " + + str([str(i) for i in list(AuthSessionState)]) + + " No expiration times will be applied", + ) + else: + # Create all indexes based on the config file + for k, v in expiration_times.items(): + auth_session.create_index( + [("created_at", ASCENDING)], + expireAfterSeconds=v + settings.CONTROLLER_PRESENTATION_BUFFER_TIME, + name=index_name(k), + partialFilterExpression={"proof_status": {"$eq": k}}, + ) + + async def init_db(): # must be idempotent db = client[settings.DB_NAME] @@ -27,32 +91,15 @@ async def init_db(): auth_session = db.get_collection(COLLECTION_NAMES.AUTH_SESSION) auth_session.create_index([("pres_exch_id", ASCENDING)], unique=True) auth_session.create_index([("pyop_auth_code", ASCENDING)], unique=True) - - with open( - ( - Path(__file__).parent.parent / "authSessions" / "sessiontimeout.json" - ).resolve() - ) as user_file: - experation_times: dict[str, int] = json.loads(user_file.read()) - auth_session_states: list[str] = [str(i) for i in list(AuthSessionState)] - try: - for k, v in experation_times.items(): - assert isinstance(k, str) - assert k in auth_session_states - auth_session.create_index( - [("created_at", ASCENDING)], - expireAfterSeconds=v + settings.CONTROLLER_PRESENTATION_BUFFER_TIME, - name=k + "_ttl", - partialFilterExpression={"proof_status": {"$eq": k}}, - ) - except Exception as e: - match e: - case AssertionError(): - logger.error( - "Invalid entry in sessiontimeout.json ", - e, - " no following expiration times will be applied", - ) + if settings.CONTROLLER_SESSION_TIMEOUT_CONFIG_FILE: + create_ttl_indexes( + auth_session, settings.CONTROLLER_SESSION_TIMEOUT_CONFIG_FILE + ) + else: + logger.warn( + "No configuration file was set for CONTROLLER_SESSION_TIMEOUT_CONFIG_FILE" + + " No expiration times will be applied.", + ) async def get_db(): From 2e4c67f19db30c771dce40af5a428e1cc3d5bede Mon Sep 17 00:00:00 2001 From: Gavin Jaeger-Freeborn Date: Tue, 4 Jun 2024 16:52:40 -0700 Subject: [PATCH 10/31] Add default config file Signed-off-by: Gavin Jaeger-Freeborn --- docker/oidc-controller/sessiontimeout.json | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 docker/oidc-controller/sessiontimeout.json diff --git a/docker/oidc-controller/sessiontimeout.json b/docker/oidc-controller/sessiontimeout.json new file mode 100644 index 00000000..8056cc1b --- /dev/null +++ b/docker/oidc-controller/sessiontimeout.json @@ -0,0 +1,5 @@ +{ + "expired": 20, + "failed": 10, + "abandoned": 10 +} From e9915b108741eef3eea16d4a63ba3b305e6fd24a Mon Sep 17 00:00:00 2001 From: Gavin Jaeger-Freeborn Date: Tue, 4 Jun 2024 16:53:30 -0700 Subject: [PATCH 11/31] Remove old print statement Signed-off-by: Gavin Jaeger-Freeborn --- oidc-controller/api/db/session.py | 1 - 1 file changed, 1 deletion(-) diff --git a/oidc-controller/api/db/session.py b/oidc-controller/api/db/session.py index 5d33aee5..0f449dbb 100644 --- a/oidc-controller/api/db/session.py +++ b/oidc-controller/api/db/session.py @@ -31,7 +31,6 @@ def create_ttl_indexes(auth_session: Collection, file: str): except OperationFailure as _: # If this index does not exist just continue pass - logger.warning("Here") try: with open(Path(file).resolve()) as user_file: expiration_times: dict[str, int] = json.loads(user_file.read()) From 0bb01369e3dc3bb94d2d5c9c70fabf681152c598 Mon Sep 17 00:00:00 2001 From: Gavin Jaeger-Freeborn Date: Tue, 4 Jun 2024 16:54:39 -0700 Subject: [PATCH 12/31] Simplify printing of auth_session_states Signed-off-by: Gavin Jaeger-Freeborn --- oidc-controller/api/db/session.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/oidc-controller/api/db/session.py b/oidc-controller/api/db/session.py index 0f449dbb..8469f430 100644 --- a/oidc-controller/api/db/session.py +++ b/oidc-controller/api/db/session.py @@ -25,7 +25,6 @@ def create_ttl_indexes(auth_session: Collection, file: str): auth_session_states: list[str] = [str(i) for i in list(AuthSessionState)] # Drop all old indexes if they exist for state in auth_session_states: - try: auth_session.drop_index(index_name(state)) except OperationFailure as _: @@ -64,7 +63,7 @@ def create_ttl_indexes(auth_session: Collection, file: str): + ". Ensure all entries in your session timout file map an " + "AuthSessionState to an integer " + "valid auth session strings are " - + str([str(i) for i in list(AuthSessionState)]) + + str(auth_session_states) + " No expiration times will be applied", ) else: From 7417bea9ed981d61925b0569d307077e862e9583 Mon Sep 17 00:00:00 2001 From: Gavin Jaeger-Freeborn Date: Wed, 5 Jun 2024 10:04:32 -0700 Subject: [PATCH 13/31] Migrated to a single ttl index Signed-off-by: Gavin Jaeger-Freeborn --- docker/docker-compose.yaml | 2 +- docker/manage | 5 ++- docker/oidc-controller/sessiontimeout.json | 6 +-- .../api/authSessions/sessiontimeout.json | 5 --- oidc-controller/api/core/config.py | 4 +- oidc-controller/api/db/session.py | 44 ++++++++----------- 6 files changed, 27 insertions(+), 39 deletions(-) delete mode 100644 oidc-controller/api/authSessions/sessiontimeout.json diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index a1579cd5..ad4a1dbc 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -25,7 +25,7 @@ services: - CONTROLLER_URL=${CONTROLLER_URL} - CONTROLLER_CAMERA_REDIRECT_URL=${CONTROLLER_CAMERA_REDIRECT_URL} - CONTROLLER_PRESENTATION_EXPIRE_TIME=${CONTROLLER_PRESENTATION_EXPIRE_TIME} - - CONTROLLER_PRESENTATION_BUFFER_TIME=${CONTROLLER_PRESENTATION_BUFFER_TIME} + - CONTROLLER_PRESENTATION_CLEANUP_TIME=${CONTROLLER_PRESENTATION_CLEANUP_TIME} - CONTROLLER_SESSION_TIMEOUT_CONFIG_FILE=${CONTROLLER_SESSION_TIMEOUT_CONFIG_FILE} - ACAPY_TENANCY=${AGENT_TENANT_MODE} - ACAPY_AGENT_URL=${AGENT_ENDPOINT} diff --git a/docker/manage b/docker/manage index 06c49840..198cb5c5 100755 --- a/docker/manage +++ b/docker/manage @@ -171,7 +171,10 @@ configureEnvironment() { # The number of time in seconds a proof request will be valid for export CONTROLLER_PRESENTATION_EXPIRE_TIME=10 - export CONTROLLER_PRESENTATION_BUFFER_TIME=20 + + # How long auth_sessions with matching the states in + # CONTROLLER_SESSION_TIMEOUT_CONFIG_FILE are stored for + export CONTROLLER_PRESENTATION_CLEANUP_TIME=86400 # The path to the auth_session timeouts config file export CONTROLLER_SESSION_TIMEOUT_CONFIG_FILE="/tmp/sessiontimeout.json" diff --git a/docker/oidc-controller/sessiontimeout.json b/docker/oidc-controller/sessiontimeout.json index 8056cc1b..71945059 100644 --- a/docker/oidc-controller/sessiontimeout.json +++ b/docker/oidc-controller/sessiontimeout.json @@ -1,5 +1 @@ -{ - "expired": 20, - "failed": 10, - "abandoned": 10 -} +["expired", "failed", "abandoned"] diff --git a/oidc-controller/api/authSessions/sessiontimeout.json b/oidc-controller/api/authSessions/sessiontimeout.json deleted file mode 100644 index c51b0f6f..00000000 --- a/oidc-controller/api/authSessions/sessiontimeout.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "expired": 10, - "failed": 10, - "abandoned": 10 -} diff --git a/oidc-controller/api/core/config.py b/oidc-controller/api/core/config.py index 16578396..b22a7c89 100644 --- a/oidc-controller/api/core/config.py +++ b/oidc-controller/api/core/config.py @@ -167,8 +167,8 @@ class GlobalConfig(BaseSettings): "CONTROLLER_PRESENTATION_EXPIRE_TIME", 10 ) - CONTROLLER_PRESENTATION_BUFFER_TIME: int = os.environ.get( - "CONTROLLER_PRESENTATION_BUFFER_TIME", 10 + CONTROLLER_PRESENTATION_CLEANUP_TIME: int = os.environ.get( + "CONTROLLER_PRESENTATION_CLEANUP_TIME", 86400 ) CONTROLLER_SESSION_TIMEOUT_CONFIG_FILE: Optional[str] = os.environ.get( diff --git a/oidc-controller/api/db/session.py b/oidc-controller/api/db/session.py index 8469f430..fbae8798 100644 --- a/oidc-controller/api/db/session.py +++ b/oidc-controller/api/db/session.py @@ -17,26 +17,27 @@ async def get_async_session(): logger: structlog.typing.FilteringBoundLogger = structlog.getLogger(__name__) -def index_name(k: str) -> str: - return k + "_ttl" - +def apply_expiration_times(auth_session: Collection, expiration_times: list[str]): + # Create all indexes based on the config file + auth_session.create_index( + [("created_at", ASCENDING)], + # TODO add timout for controller + expireAfterSeconds=settings.CONTROLLER_PRESENTATION_CLEANUP_TIME + name="expiration_ttl", + partialFilterExpression={"$or": + [{ "proof_status": {"$eq": state}} for state in expiration_times] + }, + ) def create_ttl_indexes(auth_session: Collection, file: str): auth_session_states: list[str] = [str(i) for i in list(AuthSessionState)] - # Drop all old indexes if they exist - for state in auth_session_states: - try: - auth_session.drop_index(index_name(state)) - except OperationFailure as _: - # If this index does not exist just continue - pass try: with open(Path(file).resolve()) as user_file: - expiration_times: dict[str, int] = json.loads(user_file.read()) + expiration_times: list[str] = json.load(user_file) # Ensure the given config is valid if not all( - isinstance(k, str) and (k in auth_session_states) and isinstance(v, int) - for k, v in expiration_times.items() + isinstance(status, str) and (status in auth_session_states) + for status in expiration_times ): raise Exception("Invalid json formatting") @@ -51,7 +52,7 @@ def create_ttl_indexes(auth_session: Collection, file: str): ) case json.JSONDecodeError(): logger.warning( - "Failed to decode the auth session timeouts timout config file " + "Failed to decode the auth session timeouts timeout config file " + file + " with the following error " + str(e), @@ -60,21 +61,14 @@ def create_ttl_indexes(auth_session: Collection, file: str): logger.error( "There is at least one invalid entry in the file " + file - + ". Ensure all entries in your session timout file map an " - + "AuthSessionState to an integer " + + ". Ensure all entries in your session timeout file " + + "should be a valid AuthSessionState " + "valid auth session strings are " + str(auth_session_states) - + " No expiration times will be applied", + + ". No expiration times will be applied", ) else: - # Create all indexes based on the config file - for k, v in expiration_times.items(): - auth_session.create_index( - [("created_at", ASCENDING)], - expireAfterSeconds=v + settings.CONTROLLER_PRESENTATION_BUFFER_TIME, - name=index_name(k), - partialFilterExpression={"proof_status": {"$eq": k}}, - ) + apply_expiration_times(auth_session, expiration_times) async def init_db(): From 3034b138f0afbd1b86261ea35ee2e91b074d2d20 Mon Sep 17 00:00:00 2001 From: Gavin Jaeger-Freeborn Date: Wed, 5 Jun 2024 10:53:12 -0700 Subject: [PATCH 14/31] Handle preexisting ttl index Signed-off-by: Gavin Jaeger-Freeborn --- docker/manage | 2 +- oidc-controller/api/core/config.py | 2 ++ oidc-controller/api/db/session.py | 37 +++++++++++++++++++----------- 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/docker/manage b/docker/manage index 198cb5c5..f1c1f123 100755 --- a/docker/manage +++ b/docker/manage @@ -173,7 +173,7 @@ configureEnvironment() { export CONTROLLER_PRESENTATION_EXPIRE_TIME=10 # How long auth_sessions with matching the states in - # CONTROLLER_SESSION_TIMEOUT_CONFIG_FILE are stored for + # CONTROLLER_SESSION_TIMEOUT_CONFIG_FILE are stored for in seconds export CONTROLLER_PRESENTATION_CLEANUP_TIME=86400 # The path to the auth_session timeouts config file diff --git a/oidc-controller/api/core/config.py b/oidc-controller/api/core/config.py index b22a7c89..bda20744 100644 --- a/oidc-controller/api/core/config.py +++ b/oidc-controller/api/core/config.py @@ -167,6 +167,8 @@ class GlobalConfig(BaseSettings): "CONTROLLER_PRESENTATION_EXPIRE_TIME", 10 ) + # How long auth_sessions with matching the states in + # CONTROLLER_SESSION_TIMEOUT_CONFIG_FILE are stored for in seconds CONTROLLER_PRESENTATION_CLEANUP_TIME: int = os.environ.get( "CONTROLLER_PRESENTATION_CLEANUP_TIME", 86400 ) diff --git a/oidc-controller/api/db/session.py b/oidc-controller/api/db/session.py index fbae8798..7c98d038 100644 --- a/oidc-controller/api/db/session.py +++ b/oidc-controller/api/db/session.py @@ -19,15 +19,26 @@ async def get_async_session(): def apply_expiration_times(auth_session: Collection, expiration_times: list[str]): # Create all indexes based on the config file - auth_session.create_index( - [("created_at", ASCENDING)], - # TODO add timout for controller - expireAfterSeconds=settings.CONTROLLER_PRESENTATION_CLEANUP_TIME - name="expiration_ttl", - partialFilterExpression={"$or": - [{ "proof_status": {"$eq": state}} for state in expiration_times] - }, - ) + index_name = "auth_session_ttl" + + try: + auth_session.create_index( + [("created_at", ASCENDING)], + expireAfterSeconds=settings.CONTROLLER_PRESENTATION_CLEANUP_TIME, + name=index_name, + partialFilterExpression={ + "$or": [{"proof_status": {"$eq": state}} for state in expiration_times] + }, + ) + except OperationFailure as _: + # Warn the user if the index already exists + logger.warning( + "The index " + + index_name + + " already exists. It must manually be deleted to " + + "update the timeout or matched AuthSessionState's" + ) + def create_ttl_indexes(auth_session: Collection, file: str): auth_session_states: list[str] = [str(i) for i in list(AuthSessionState)] @@ -36,8 +47,8 @@ def create_ttl_indexes(auth_session: Collection, file: str): expiration_times: list[str] = json.load(user_file) # Ensure the given config is valid if not all( - isinstance(status, str) and (status in auth_session_states) - for status in expiration_times + isinstance(status, str) and (status in auth_session_states) + for status in expiration_times ): raise Exception("Invalid json formatting") @@ -61,8 +72,8 @@ def create_ttl_indexes(auth_session: Collection, file: str): logger.error( "There is at least one invalid entry in the file " + file - + ". Ensure all entries in your session timeout file " - + "should be a valid AuthSessionState " + + ". The timeout config file should contain " + + "a json list of AuthSessionStates." + "valid auth session strings are " + str(auth_session_states) + ". No expiration times will be applied", From 7adafebd1fcfd214a117c5a9e20634f32967bd57 Mon Sep 17 00:00:00 2001 From: Gavin Jaeger-Freeborn Date: Thu, 6 Jun 2024 15:20:15 -0700 Subject: [PATCH 15/31] Add documentation to explain the Auth Session cleanup configuration Signed-off-by: Gavin Jaeger-Freeborn --- docs/README.md | 13 +++++++++++++ oidc-controller/api/db/session.py | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index aef7b0e6..a5530902 100644 --- a/docs/README.md +++ b/docs/README.md @@ -343,6 +343,19 @@ The following additional metadata must be present at the OP's `/.well-known/open - The `scopes_supported` JSON array, should be extended to include the `vc_authn` scope +## Auth Session Cleanup + +For each authentication attempt, an auth session is created. Over Time, these can accumulate, increasing the database size. To address this issue, a configuration file specified by the environment variable CONTROLLER_SESSION_TIMEOUT_CONFIG_FILE is used to automatically clean up auth sessions based on their current state. This file contains a JSON array of different auth session states as strings. + +An example configuration file would contain the following text +```json +["expired", "failed", "abandoned"] +``` + +If no file is found no auth session cleanup is applied. If this config file is changed and the service is restarted no new clean up will be scheduled. The user must manually delete the existing Time To Live (ttl) index from the MongoDB database under the title `auth_session_ttl`. + +The environment variable `CONTROLLER_PRESENTATION_CLEANUP_TIME` determined the frequency at which these sessions are deleted. It's value should contain an integer indicating the number of seconds each session will remain. By default it is set to `86400` (one day). + ## Un-Answered questions - SIOP instead of DIDComm for the requests between the RP and IW? diff --git a/oidc-controller/api/db/session.py b/oidc-controller/api/db/session.py index 7c98d038..37415faf 100644 --- a/oidc-controller/api/db/session.py +++ b/oidc-controller/api/db/session.py @@ -73,7 +73,7 @@ def create_ttl_indexes(auth_session: Collection, file: str): "There is at least one invalid entry in the file " + file + ". The timeout config file should contain " - + "a json list of AuthSessionStates." + + "a JSON array of AuthSessionStates." + "valid auth session strings are " + str(auth_session_states) + ". No expiration times will be applied", From b5ed152991f36e6f60481b1c12eb91b260ea4c1d Mon Sep 17 00:00:00 2001 From: Gavin Jaeger-Freeborn Date: Thu, 6 Jun 2024 15:55:54 -0700 Subject: [PATCH 16/31] Removed linting problem with `as _` Signed-off-by: Gavin Jaeger-Freeborn --- oidc-controller/api/db/session.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oidc-controller/api/db/session.py b/oidc-controller/api/db/session.py index 37415faf..187a455c 100644 --- a/oidc-controller/api/db/session.py +++ b/oidc-controller/api/db/session.py @@ -30,7 +30,7 @@ def apply_expiration_times(auth_session: Collection, expiration_times: list[str] "$or": [{"proof_status": {"$eq": state}} for state in expiration_times] }, ) - except OperationFailure as _: + except OperationFailure: # Warn the user if the index already exists logger.warning( "The index " From 1338ca6d649466c7da57ffb01fceb28f0a67c048 Mon Sep 17 00:00:00 2001 From: Gavin Jaeger-Freeborn Date: Wed, 12 Jun 2024 13:34:01 -0700 Subject: [PATCH 17/31] initial conversion to present proof 2.0 Signed-off-by: Gavin Jaeger-Freeborn --- oidc-controller/api/core/acapy/client.py | 8 +++++--- .../api/verificationConfigs/models.py | 18 ++++++++++++------ 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/oidc-controller/api/core/acapy/client.py b/oidc-controller/api/core/acapy/client.py index b0fadf69..3b4d1f80 100644 --- a/oidc-controller/api/core/acapy/client.py +++ b/oidc-controller/api/core/acapy/client.py @@ -14,8 +14,8 @@ WALLET_DID_URI = "/wallet/did" PUBLIC_WALLET_DID_URI = "/wallet/did/public" -CREATE_PRESENTATION_REQUEST_URL = "/present-proof/create-request" -PRESENT_PROOF_RECORDS = "/present-proof/records" +CREATE_PRESENTATION_REQUEST_URL = "/present-proof-2.0/create-request" +PRESENT_PROOF_RECORDS = "/present-proof-2.0/records" class AcapyClient: @@ -42,7 +42,9 @@ def create_presentation_request( self, presentation_request_configuration: dict ) -> CreatePresentationResponse: logger.debug(">>> create_presentation_request") - present_proof_payload = {"proof_request": presentation_request_configuration} + present_proof_payload = { + "presentation_request": presentation_request_configuration + } resp_raw = requests.post( self.acapy_host + CREATE_PRESENTATION_REQUEST_URL, diff --git a/oidc-controller/api/verificationConfigs/models.py b/oidc-controller/api/verificationConfigs/models.py index 5f572a49..913a8836 100644 --- a/oidc-controller/api/verificationConfigs/models.py +++ b/oidc-controller/api/verificationConfigs/models.py @@ -48,23 +48,29 @@ def generate_proof_request(self): result = { "name": "proof_requested", "version": "0.0.1", - "requested_attributes": {}, - "requested_predicates": {}, + "indy": { + "requested_attributes": {}, + "requested_predicates": {}, + }, } for i, req_attr in enumerate(self.proof_request.requested_attributes): label = req_attr.label or "req_attr_" + str(i) - result["requested_attributes"][label] = req_attr.dict(exclude_none=True) + result["indy"]["requested_attributes"][label] = req_attr.dict( + exclude_none=True + ) if settings.SET_NON_REVOKED: - result["requested_attributes"][label]["non_revoked"] = { + result["indy"]["requested_attributes"][label]["non_revoked"] = { "from": int(time.time()), "to": int(time.time()), } # TODO add I indexing for req_pred in self.proof_request.requested_predicates: label = req_pred.label or "req_pred_" + str(i) - result["requested_predicates"][label] = req_pred.dict(exclude_none=True) + result["indy"]["requested_predicates"][label] = req_pred.dict( + exclude_none=True + ) if settings.SET_NON_REVOKED: - result["requested_attributes"][label]["non_revoked"] = { + result["indy"]["requested_attributes"][label]["non_revoked"] = { "from": int(time.time()), "to": int(time.time()), } From 4cb03e66d4f61e3d2e7dfec063fb3119f9830c7c Mon Sep 17 00:00:00 2001 From: Gavin Jaeger-Freeborn Date: Tue, 18 Jun 2024 12:08:39 -0700 Subject: [PATCH 18/31] Properly generate a 2.0 proof request without error from acapy Signed-off-by: Gavin Jaeger-Freeborn --- oidc-controller/api/core/acapy/client.py | 2 +- oidc-controller/api/core/acapy/models.py | 4 ++-- oidc-controller/api/routers/oidc.py | 6 ++---- .../api/verificationConfigs/models.py | 18 ++++++------------ 4 files changed, 11 insertions(+), 19 deletions(-) diff --git a/oidc-controller/api/core/acapy/client.py b/oidc-controller/api/core/acapy/client.py index 3b4d1f80..1522c3ad 100644 --- a/oidc-controller/api/core/acapy/client.py +++ b/oidc-controller/api/core/acapy/client.py @@ -43,7 +43,7 @@ def create_presentation_request( ) -> CreatePresentationResponse: logger.debug(">>> create_presentation_request") present_proof_payload = { - "presentation_request": presentation_request_configuration + "presentation_request": {"indy": presentation_request_configuration} } resp_raw = requests.post( diff --git a/oidc-controller/api/core/acapy/models.py b/oidc-controller/api/core/acapy/models.py index 7b4df94c..bf001649 100644 --- a/oidc-controller/api/core/acapy/models.py +++ b/oidc-controller/api/core/acapy/models.py @@ -15,5 +15,5 @@ class WalletDidPublicResponse(BaseModel): class CreatePresentationResponse(BaseModel): thread_id: str - presentation_exchange_id: str - presentation_request: Dict + pres_ex_id: str + pres_request: Dict diff --git a/oidc-controller/api/routers/oidc.py b/oidc-controller/api/routers/oidc.py index ee8d5856..b9e6e24e 100644 --- a/oidc-controller/api/routers/oidc.py +++ b/oidc-controller/api/routers/oidc.py @@ -120,9 +120,7 @@ async def get_authorize(request: Request, db: Database = Depends(get_db)): use_public_did = not settings.USE_OOB_LOCAL_DID_SERVICE wallet_did = client.get_wallet_did(public=use_public_did) - byo_attachment = PresentProofv10Attachment.build( - pres_exch_dict["presentation_request"] - ) + byo_attachment = PresentProofv10Attachment.build(pres_exch_dict["pres_request"]) msg = None if settings.USE_OOB_PRESENT_PROOF: @@ -166,7 +164,7 @@ async def get_authorize(request: Request, db: Database = Depends(get_db)): pyop_auth_code=authn_response["code"], request_parameters=model.to_dict(), ver_config_id=ver_config_id, - pres_exch_id=response.presentation_exchange_id, + pres_exch_id=response.pres_ex_id, presentation_exchange=pres_exch_dict, presentation_request_msg=msg_contents.dict(by_alias=True), ) diff --git a/oidc-controller/api/verificationConfigs/models.py b/oidc-controller/api/verificationConfigs/models.py index 913a8836..5f572a49 100644 --- a/oidc-controller/api/verificationConfigs/models.py +++ b/oidc-controller/api/verificationConfigs/models.py @@ -48,29 +48,23 @@ def generate_proof_request(self): result = { "name": "proof_requested", "version": "0.0.1", - "indy": { - "requested_attributes": {}, - "requested_predicates": {}, - }, + "requested_attributes": {}, + "requested_predicates": {}, } for i, req_attr in enumerate(self.proof_request.requested_attributes): label = req_attr.label or "req_attr_" + str(i) - result["indy"]["requested_attributes"][label] = req_attr.dict( - exclude_none=True - ) + result["requested_attributes"][label] = req_attr.dict(exclude_none=True) if settings.SET_NON_REVOKED: - result["indy"]["requested_attributes"][label]["non_revoked"] = { + result["requested_attributes"][label]["non_revoked"] = { "from": int(time.time()), "to": int(time.time()), } # TODO add I indexing for req_pred in self.proof_request.requested_predicates: label = req_pred.label or "req_pred_" + str(i) - result["indy"]["requested_predicates"][label] = req_pred.dict( - exclude_none=True - ) + result["requested_predicates"][label] = req_pred.dict(exclude_none=True) if settings.SET_NON_REVOKED: - result["indy"]["requested_attributes"][label]["non_revoked"] = { + result["requested_attributes"][label]["non_revoked"] = { "from": int(time.time()), "to": int(time.time()), } From 17f99d652c18f656ed812fc142396cd32c8d29f4 Mon Sep 17 00:00:00 2001 From: Gavin Jaeger-Freeborn Date: Tue, 18 Jun 2024 16:16:08 -0700 Subject: [PATCH 19/31] Support handling new present proof v2 topic Signed-off-by: Gavin Jaeger-Freeborn --- oidc-controller/api/routers/acapy_handler.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/oidc-controller/api/routers/acapy_handler.py b/oidc-controller/api/routers/acapy_handler.py index ea3379b0..504682a3 100644 --- a/oidc-controller/api/routers/acapy_handler.py +++ b/oidc-controller/api/routers/acapy_handler.py @@ -29,14 +29,12 @@ async def post_topic(request: Request, topic: str, db: Database = Depends(get_db client = AcapyClient() match topic: - case "present_proof": + case "present_proof_v2_0": webhook_body = await _parse_webhook_body(request) - logger.info( - f">>>> pres_exch_id: {webhook_body['presentation_exchange_id']}" - ) + logger.info(f">>>> pres_exch_id: {webhook_body['pres_ex_id']}") auth_session: AuthSession = await AuthSessionCRUD(db).get_by_pres_exch_id( - webhook_body["presentation_exchange_id"] + webhook_body["pres_ex_id"] ) # Get the saved websocket session From 665a2cfc012cf49fed0ab2d1c16a276521356a9f Mon Sep 17 00:00:00 2001 From: Gavin Jaeger-Freeborn Date: Thu, 20 Jun 2024 10:34:51 -0700 Subject: [PATCH 20/31] Update present_proof_presentation to 2.0 type Signed-off-by: Gavin Jaeger-Freeborn --- oidc-controller/api/core/aries/present_proof_presentation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oidc-controller/api/core/aries/present_proof_presentation.py b/oidc-controller/api/core/aries/present_proof_presentation.py index 95b01b5e..1eb37afa 100644 --- a/oidc-controller/api/core/aries/present_proof_presentation.py +++ b/oidc-controller/api/core/aries/present_proof_presentation.py @@ -10,7 +10,7 @@ class PresentationRequestMessage(BaseModel): # https://github.com/hyperledger/aries-rfcs/blob/main/features/0037-present-proof/README.md#presentation id: str = Field(alias="@id") type: str = Field( - "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/present-proof/1.0/request-presentation", + "https://didcomm.org/present-proof/2.0/request-presentation", alias="@type", ) request: List[PresentProofv10Attachment] = Field( From 88d16bc669186d80c4986e2583a495575b989994 Mon Sep 17 00:00:00 2001 From: Gavin Jaeger-Freeborn Date: Thu, 27 Jun 2024 12:19:01 -0700 Subject: [PATCH 21/31] Updated to utilize --debug-webhooks Signed-off-by: Gavin Jaeger-Freeborn --- docker/docker-compose.yaml | 2 +- oidc-controller/api/authSessions/models.py | 7 ++----- oidc-controller/api/core/oidc/issue_token_service.py | 6 +++--- oidc-controller/api/routers/acapy_handler.py | 6 ++---- 4 files changed, 8 insertions(+), 13 deletions(-) diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index ad4a1dbc..c36f16c9 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -131,7 +131,7 @@ services: command: [ "-c", - 'sleep 15; aca-py start --inbound-transport http ''0.0.0.0'' ${AGENT_HTTP_PORT} --outbound-transport http --wallet-storage-config ''{"url":"${POSTGRESQL_WALLET_HOST}:${POSTGRESQL_WALLET_PORT}","max_connections":5}'' --wallet-storage-creds ''{"account":"${POSTGRESQL_WALLET_USER}","password":"${POSTGRESQL_WALLET_PASSWORD}","admin_account":"${POSTGRESQL_WALLET_USER}","admin_password":"${POSTGRESQL_WALLET_PASSWORD}"}'' --admin ''0.0.0.0'' ${AGENT_ADMIN_PORT} --${AGENT_ADMIN_MODE} ', + 'sleep 15; aca-py start --debug-webhooks --inbound-transport http ''0.0.0.0'' ${AGENT_HTTP_PORT} --outbound-transport http --wallet-storage-config ''{"url":"${POSTGRESQL_WALLET_HOST}:${POSTGRESQL_WALLET_PORT}","max_connections":5}'' --wallet-storage-creds ''{"account":"${POSTGRESQL_WALLET_USER}","password":"${POSTGRESQL_WALLET_PASSWORD}","admin_account":"${POSTGRESQL_WALLET_USER}","admin_password":"${POSTGRESQL_WALLET_PASSWORD}"}'' --admin ''0.0.0.0'' ${AGENT_ADMIN_PORT} --${AGENT_ADMIN_MODE} ', ] wallet-db: diff --git a/oidc-controller/api/authSessions/models.py b/oidc-controller/api/authSessions/models.py index dd4a711a..9bfa4e74 100644 --- a/oidc-controller/api/authSessions/models.py +++ b/oidc-controller/api/authSessions/models.py @@ -35,11 +35,7 @@ class AuthSessionBase(BaseModel): class AuthSession(AuthSessionBase, UUIDModel): proof_status: AuthSessionState = Field(default=AuthSessionState.NOT_STARTED) - - @property - def presentation_exchange(self) -> Dict: - client = AcapyClient() - return client.get_presentation_request(self.pres_exch_id) + presentation_exchange: dict = Field(default_factory=dict) class AuthSessionCreate(AuthSessionBase): @@ -48,4 +44,5 @@ class AuthSessionCreate(AuthSessionBase): class AuthSessionPatch(AuthSessionBase): proof_status: AuthSessionState = Field(default=AuthSessionState.PENDING) + presentation_exchange: dict = Field(default_factory=dict) pass diff --git a/oidc-controller/api/core/oidc/issue_token_service.py b/oidc-controller/api/core/oidc/issue_token_service.py index 75caace8..b6125951 100644 --- a/oidc-controller/api/core/oidc/issue_token_service.py +++ b/oidc-controller/api/core/oidc/issue_token_service.py @@ -52,7 +52,7 @@ def get_claims( logger.info( "pres_request_token" + str( - auth_session.presentation_exchange["pres_request"][ + auth_session.presentation_exchange["pres_request"]["indy"][ "requested_attributes" ] ) @@ -63,13 +63,13 @@ def get_claims( try: for referent, requested_attrdict in auth_session.presentation_exchange[ "pres_request" - ]["requested_attributes"].items(): + ]["indy"]["requested_attributes"].items(): requested_attr = ReqAttr(**requested_attrdict) logger.debug( f"Processing referent: {referent}, requested_attr: {requested_attr}" ) revealed_attrs: Dict[str, RevealedAttribute] = ( - auth_session.presentation_exchange["presentation"][ + auth_session.presentation_exchange["pres"]["indy"][ "requested_proof" ]["revealed_attr_groups"] ) diff --git a/oidc-controller/api/routers/acapy_handler.py b/oidc-controller/api/routers/acapy_handler.py index b5ab1030..4dc45e97 100644 --- a/oidc-controller/api/routers/acapy_handler.py +++ b/oidc-controller/api/routers/acapy_handler.py @@ -42,15 +42,13 @@ async def post_topic(request: Request, topic: str, db: Database = Depends(get_db sid = connections.get(pid) if webhook_body["state"] == "presentation-received": - logger.info("GOT A PRESENTATION, TIME TO VERIFY") - # Not found Record not found: pres_ex_v20/0a85e304-15e9-4ead-8e82-4151a577b26f. - client.verify_presentation(auth_session.pres_exch_id) - # This state is the default on the front end.. So don't send a status + logger.info("presentation-received") if webhook_body["state"] == "done": logger.info("VERIFIED") if webhook_body["verified"] == "true": auth_session.proof_status = AuthSessionState.VERIFIED + auth_session.presentation_exchange = webhook_body["by_format"] await sio.emit("status", {"status": "verified"}, to=sid) else: auth_session.proof_status = AuthSessionState.FAILED From 24b0836d5904688226570a311eeabbbab94240a6 Mon Sep 17 00:00:00 2001 From: Gavin Jaeger-Freeborn Date: Thu, 27 Jun 2024 12:22:19 -0700 Subject: [PATCH 22/31] Remove unused verify_presentation code Signed-off-by: Gavin Jaeger-Freeborn --- oidc-controller/api/core/acapy/client.py | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/oidc-controller/api/core/acapy/client.py b/oidc-controller/api/core/acapy/client.py index 07a48d91..b31a1044 100644 --- a/oidc-controller/api/core/acapy/client.py +++ b/oidc-controller/api/core/acapy/client.py @@ -81,24 +81,6 @@ def get_presentation_request(self, presentation_exchange_id: Union[UUID, str]): logger.debug(f"<<< get_presentation_request -> {resp}") return resp - def verify_presentation(self, presentation_exchange_id: Union[UUID, str]): - logger.debug(">>> verify_presentation") - - resp_raw = requests.post( - self.acapy_host - + PRESENT_PROOF_RECORDS - + "/" - + str(presentation_exchange_id) - + "/verify-presentation", - headers=self.agent_config.get_headers(), - ) - assert resp_raw.status_code == 200, resp_raw.content - - resp = json.loads(resp_raw.content) - - logger.debug(f"<<< verify_presentation -> {resp}") - return resp - def get_wallet_did(self, public=False) -> WalletDid: logger.debug(">>> get_wallet_did") url = None From 40da36b37d31dbfa19bd648196fc65f60ccb6c0c Mon Sep 17 00:00:00 2001 From: Gavin Jaeger-Freeborn Date: Wed, 3 Jul 2024 17:45:03 -0700 Subject: [PATCH 23/31] Updated tests to reflect the new present_proof 2.0 changes Signed-off-by: Gavin Jaeger-Freeborn --- oidc-controller/api/authSessions/models.py | 2 +- .../api/core/acapy/tests/test_client.py | 42 - .../api/core/oidc/tests/__mocks__.py | 1371 ++++++++++++++++- .../oidc/tests/test_issue_token_service.py | 176 +-- oidc-controller/api/routers/acapy_handler.py | 2 + 5 files changed, 1399 insertions(+), 194 deletions(-) diff --git a/oidc-controller/api/authSessions/models.py b/oidc-controller/api/authSessions/models.py index 9bfa4e74..b6d5b36a 100644 --- a/oidc-controller/api/authSessions/models.py +++ b/oidc-controller/api/authSessions/models.py @@ -35,7 +35,7 @@ class AuthSessionBase(BaseModel): class AuthSession(AuthSessionBase, UUIDModel): proof_status: AuthSessionState = Field(default=AuthSessionState.NOT_STARTED) - presentation_exchange: dict = Field(default_factory=dict) + presentation_exchange: dict | None = Field(default_factory=dict) class AuthSessionCreate(AuthSessionBase): diff --git a/oidc-controller/api/core/acapy/tests/test_client.py b/oidc-controller/api/core/acapy/tests/test_client.py index 8fc8078e..19794eb2 100644 --- a/oidc-controller/api/core/acapy/tests/test_client.py +++ b/oidc-controller/api/core/acapy/tests/test_client.py @@ -144,48 +144,6 @@ async def test_get_presentation_throws_assertion_error_for_non_200_response_from assert e is not None -@pytest.mark.asyncio -async def test_verify_presentation_returns_sucessfully_with_valid_data(requests_mock): - requests_mock.post( - settings.ACAPY_ADMIN_URL - + PRESENT_PROOF_RECORDS - + "/" - + "1234-567890" - + "/verify-presentation", - headers={}, - json={"result": "success"}, - status_code=200, - ) - - client = AcapyClient() - client.agent_config.get_headers = mock.MagicMock(return_value={"x-api-key": ""}) - verification = client.verify_presentation("1234-567890") - assert verification is not None - - -@pytest.mark.asyncio -async def test_verify_presentation_throws_assertion_error_for_non_200_resp_from_acapy( - requests_mock, -): - requests_mock.post( - settings.ACAPY_ADMIN_URL - + PRESENT_PROOF_RECORDS - + "/" - + "1234-567890" - + "/verify-presentation", - headers={}, - json={"result": "success"}, - status_code=400, - ) - - client = AcapyClient() - client.agent_config.get_headers = mock.MagicMock(return_value={"x-api-key": ""}) - try: - client.verify_presentation("1234-567890") - except AssertionError as e: - assert e is not None - - @pytest.mark.asyncio async def test_get_wallet_did_public_returns_sucessfully_on_public_url_and_simple_resp( requests_mock, diff --git a/oidc-controller/api/core/oidc/tests/__mocks__.py b/oidc-controller/api/core/oidc/tests/__mocks__.py index 9a859c3b..2aec37ac 100644 --- a/oidc-controller/api/core/oidc/tests/__mocks__.py +++ b/oidc-controller/api/core/oidc/tests/__mocks__.py @@ -3,86 +3,1326 @@ from api.verificationConfigs.models import VerificationConfig, VerificationProofRequest from api.authSessions.models import AuthSession +# Presentation returned from the debug webhook presentation = { - "thread_id": "428ab5dc-185f-40ba-8714-498c79b822f3", - "created_at": "2023-09-15T17:49:16.397954Z", + "state": "done", + "created_at": "2024-07-03T23:19:34.611906Z", + "updated_at": "2024-07-03T23:19:46.309431Z", + "trace": False, + "pres_ex_id": "ea6b9907-34d7-45df-9b2c-ebf0b17606e3", + "thread_id": "5cc7ac29-1b6c-404a-9820-cb52348f5cd8", + "initiator": "self", "role": "verifier", - "auto_present": False, - "presentation_exchange_id": "ccaac3c5-1606-44fb-ade3-33937dfb6dca", - "presentation_request": { - "nonce": "633368193772519315256591", - "name": "proof_requested", - "version": "0.0.1", - "requested_attributes": "invalid", # Add test requested attributes - "requested_predicates": {}, - }, - "presentation": { - "proof": { - "proofs": [ - { - "primary_proof": { - "eq_proof": { - "revealed_attrs": { - "email": "73814602767252868561268261832462872577293109184327908660400248444458427915643" + # Since we only rely on the by_format entry from the webhook we will exclude the pres_request and pres entries + "pres": {}, + "pres_request": {}, + "by_format": { + "pres_request": { + "indy": { + "name": "proof_requested", + "version": "0.0.1", + "requested_attributes": { + "req_attr_0": { + "names": ["given_names", "family_name", "country"], + "restrictions": [ + { + "schema_name": "Person", + "issuer_did": "L6ASjmDDbDH7yPL1t2yFj9", }, - "a_prime": "40809417934849907123261471333411809633354074436405510819719547561843004255793023387498104889708571270607131703938696756407487467873368373775514806534499374680059274765067020721416649641170535056242622419292879011504372419414431627060123535951854477609020119038446707071877530649018798857842493513975477809431501443034563297114458359206476293934461316378865994820249592804467758433904174100097026785285885805688715928578812499534293751388422584754995155726212908115142236221995953756086868367889189436621196564054394071524712971126196703085030819194540892286515361206473918715176334283198231106756804249201321110676817", - "e": "132166309846004002298968329630750626534906193461199076364226183288855464715230907038994149984215600411276690314987056469395087923423376034", - "v": "389852336354596234620050863642411241608940316153319185423050526707483503655636070718528707360249027781424214465818771183857876571898645233712049550065615437328753383559647676180932006072233802815800996667626559757294734656704881893512658110558847026240354780006408044914149694965180223127754340423475416000940696265773581100989216553579233198807834530514088943560794612627439000580021272527304012796141460444099028132444095750454921031441117671745673457339631909515144154149520136598843334638440138229207476684776682052027672414220327439299122271829090948693982983191662066627130404544959976637513561609461174550463471636750546362339385232864872096796748538286120598703640917737216094473317639418313144992206875604615452306826430748790105881684965109026881701380306409317275248915954859998470708379642658766566479932465684746033865495941061980422316943701392367332573162799073428328950386460662125690364635698421614848860", - "m": { - "time": "11896542455624181868867605459032062290636202825612483735571300567506766112289589216989585608752835036405824270689163034684130257627062069669940002269643613585904726141499206139998", - "master_secret": "11978669714570126125906674715703732098298259897696576462078203241042378591463388624826815153862705227192729264588296043944542578244619153416802465506231069348686312732305137579470", + { + "schema_name": "Person", + "issuer_did": "QEquAHkM35w4XVT3Ku5yat", }, - "m2": "5273201069177175286302918970464632772021241766353736934881903190951819578898991714048991692651562372414552361008106867369251847950990157935703897032904209115054769991615534757984", - }, - "ge_proofs": [], + { + "schema_name": "Person", + "issuer_did": "M6dhuFj5UwbhWkSLmvYSPc", + }, + ], + "non_revoked": {"from": 1720048774, "to": 1720048774}, } - } - ], - "aggregated_proof": { - "c_hash": "8135055767072243139738404741550925116429855319200452769474586673630216912237", - "c_list": [[1]], # Not complete data - }, - }, - "requested_proof": { - "revealed_attrs": {}, - "revealed_attr_groups": "invalid", # Add test revealed_attr_groups - "self_attested_attrs": {}, - "unrevealed_attrs": {}, - "predicates": {}, - }, - "identifiers": [ - { - "schema_id": "MTYqmTBoLT7KLP5RNfgK3b:2:verified-email:1.2.3", - "cred_def_id": "MTYqmTBoLT7KLP5RNfgK3b:3:CL:160342:default", + }, + "requested_predicates": {}, + "nonce": "482986012434214017125155", } - ], - }, - "verified": "true", - "state": "verified", - "presentation_request_dict": { - "@type": "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/present-proof/1.0/request-presentation", - "@id": "428ab5dc-185f-40ba-8714-498c79b822f3", - "request_presentations~attach": [ - { - "@id": "libindy-request-presentation-0", - "mime-type": "application/json", - "data": { - "base64": "eyJuYW1lIjogInByb29mX3JlcXVlc3RlZCIsICJ2ZXJzaW9uIjogIjAuMC4xIiwgInJlcXVlc3RlZF9hdHRyaWJ1dGVzIjogeyJyZXFfYXR0cl8wIjogeyJuYW1lcyI6IFsiZW1haWwiXSwgInJlc3RyaWN0aW9ucyI6IFt7InNjaGVtYV9uYW1lIjogInZlcmlmaWVkLWVtYWlsIiwgImlzc3Vlcl9kaWQiOiAiTVRZcW1UQm9MVDdLTFA1Uk5mZ0szYiJ9XSwgIm5vbl9yZXZva2VkIjogeyJmcm9tIjogMTY5NDgwMDE1NiwgInRvIjogMTY5NDgwMDE1Nn19fSwgInJlcXVlc3RlZF9wcmVkaWNhdGVzIjoge30sICJub25jZSI6ICI2MzMzNjgxOTM3NzI1MTkzMTUyNTY1OTEifQ==" + }, + "pres": { + "indy": { + "proof": { + "proofs": [ + { + "primary_proof": { + "eq_proof": { + "revealed_attrs": { + "country": "86091274455216294775467369229742497264338784747007530312638607524003986429176", + "family_name": "113629974197202028031743901156327948162780316423694730740234167618518062162596", + "given_names": "76669239303478628101977841967793018579704250003747303526675562174585617552380", + }, + "a_prime": "2752222004877393821946929938073672611147357281059879521899806617641901512078292728233552312669329595632699992281211310560630119613851389226292535964139817900617656492285503361597775161415020675353717626041656018753130092707159054275841026878421955025121014248985727425739760620186724345593157325448760454610171620029895802219351364562560524767768610096400702319143737465105065636413811415077694673724042500823762662459382470421367877341306655666708482381420027692007528656203079310607577830421182592394593025201292174148668834643310063767037229675811858030672760158692729475901407331902660576426733253619929226060268", + "e": "102841862579899394748101147926225980988284317813112065230903614605314014035947304230163816238422875108941564659002477799025081687187392664", + "v": "340554867558408556086093357826919358981768688107702729186444760247624631187520933039857336704146147023355618957606880959189296705224102334205611452495756420013307641114311191009854508367863340596260590519868011111621720944151253717359212043925748482163255710293540177923671752798999157616712082760660227861699025758925900988898324491597484653513304455651295039941405670388088884398259987637322460464433200791270462104062429525936637370537045866501073856217218230952704740292007889662804702281742640484955419486408213030492343503178198133861960152857738146894637982725893571193484870299796819648297800613770619480724452314284510318543536018028785185704134724262814039371175071125393355389941351202952956698975527531149089360343307140830624326790808831939799983724667618495300675831855759210725037345661004234516784307671404917875347355735815873031905709514634573948684201415633998012744531513426767583902308555806436032654", + "m": { + "birthdate_dateint": "2317803952542234063661759293832421485652656388240237711527971094776116471559195771533088720727964442958750426288263407108699113440607014715096343620937655263988188245154992805212", + "postal_code": "13727738920755492307396073621712351905517022535067330097134309796662703430906670320426685354562424395662500231978398320009224896369971528807242516076740504982663276320285129302631", + "region": "3898195067062190478457383545815179090617918998297207199547465504224386508721201959429059369633677859144984158022759874128284753392377775929027017463835652454541255512561810292078", + "expiry_date_dateint": "1204872815564296386187685604872485470626704285162933433087178339388517259255510287682979746219846957707949686777474066074611239256716654389445948098483804962372057850528148316029", + "locality": "11597054987853203184948655530942371268298406246991558492105828772699898911736544655696849808225830742870112219397307784712358362395552936350188310170208972108005848961591911477503", + "picture": "14641000664237394077660961546055467154927331706979893334588037194402882205412243501778560547260767521200288939747477540499011291230586841366281495152240927393478170444685893793783", + "master_secret": "4252460198761928963159948081931316045352726783662126724328223445443841137604034050982338605126196822109533123917772099980836750148245507650791305835087690105765818233633128944431", + "street_address": "3560551625564482857726867909758955501827276751876742793553852459460447211837243047171871137409586429468289265675104969343100465771655497110647867735756342782358553559892513719211", + }, + "m2": "107647437624274074225577067637575889195829933774437016608705283255664821276524579359299897835225486152482167547142833677763136652802989144079919893235727063123901540099764495836537932088943437692548244061678402727930152673030538299444980914271819137106655956398015046583452950363426592988835609493966130571990449513830235169850774795253857705229121742358139926694896354254176218769873859762034144165363639892519830179053238329882658569288333230957319971991916929963871999471702250722891217519065523463036309640116959367152993038424082277570805646677169251122696449294514072097743347954889564270796690063964912492338908067695318697927901767558415960389078154393040759703813007950982125536151182482207736527997139078582782983599359567", + }, + "ge_proofs": [], + }, + "non_revoc_proof": { + "x_list": { + "rho": "0BDEBEADA4E8CFD37E3075CF4A8E8692AD979BA1E67280E022F8AC2BE7B414BC", + "r": "02E59C4AB19B82D0ECB102EED639C52520DEF0EC91C53D519847F557042A8BB5", + "r_prime": "172216BDB6A481590690AA9645545A6EB1C996A5A1F87B072E2E61D526BA2ADE", + "r_prime_prime": "05AD762C4982BB2C99DCA23CC34E49AB7C9B193B2AB980A9BF1A1F4539310563", + "r_prime_prime_prime": "21CA2C2F459339319A38D0FD0407549BD75E1983EF0527EBEF140D3B42717C57", + "o": "07F8C8DD6F6B48A181929BE32AADDCBDC455AB7326179ECBCEE62E9E7EF2EACE", + "o_prime": "00C31E4206448C7DF461E160723F4CE3E75251124C04C4B509BAA172208788CA", + "m": "1B783BDDDB0965E3290FBA267128559477A1FF4AA38B8D12DFA8A9AD59521E7C", + "m_prime": "1FED18DDA918C196FF152EDF9C8D011462716C3DD6D11F5F0CD90CFACEF608EC", + "t": "06C04F2283725F67CFACD01B02524CCB09A770A453E56661913B521D400C4DAA", + "t_prime": "1292D8603B59229E97C72BDEE0E4F7339A2B0FE7A12722B42425F7E13BF0FE40", + "s": "0D9A9993EE09B1A3FE3CA860BBEAA728527B28B270C821D9054EB1A3F93617B7", + "c": "0EC53966010589EEBFDF7CAC1C170497851FB787A0BECB7D5F72A7B495878840", + }, + "c_list": { + "e": "6 4651BA4F1C0AB1C5053D42EAB0C2E456B3B30BCD2635EC613E2C6F77D4314E09 4 2C75BD1BD839572F1A7AFDE66DF6901EEA237DAB1C894C6B2D9063DCAF8AD083 4 1D0AE756C99A31B629090EB010DF8019E8726F62A6A79ED3B0067E3B601E5129", + "d": "6 5E8ACF57AEAE922B8F9CF4338D685AFE95D3CA9E73C1D9FAC35283451431BCB2 4 38601A54F24AD2D67F2B0383D38181130F0464D6ADBF59A34C41D8F5EB65548F 4 2370840CD4539CCD8DFB57C2773506904E9F3DFFA05A1C7C50BFEABC9511DED4", + "a": "6 39BEBB2486CA80D78023106BFA5152D5FED0D4D1AF47E56C361493D92C168C7B 4 10B2508D266D226215609821D3751A09A17DC6A39BCFF99222E292820620CE0B 4 25958FF85B4500ACC02EFBE543D83CBA89B2F61E346011358E89BFCFE445C359", + "g": "6 4E19AB7AF315C9D1077824C3595C805B5D32AA208851C9DC468A226BF0923B6D 4 23AD292298AC27F8588434346C74840099E5B64CA8011306905865062A1D4CD9 4 136FD412DBCE4E88BAC53A89D950A34982452DAC9F040F5FF3626121938E202D", + "w": "21 120B428648EFEE29FC69E0480AA397E4A5656E40DCE8F895DE1F8223E3E266792 21 12D419B813F7121B34F4D8B4623AD9A4BF90AF1098364A6401FB90782B0BC14DD 6 69373B75859E60550ED0A2C2518DD702DF753BD505337F8F1BF3F1D4DECB216D 4 31BC340A04647154743FC8435DD21611FE4D5B138A115117518DBEDDC1614583 6 72D33EE28AAC3006255F0AE6A8D009E392C5B4B0E1DBE123989BE110B05316F7 4 17BF62E556E9342915F0B629454EEF7B0F7CFE4203EE340998899CF2414A1BD0", + "s": "21 13F870B79A5C3DE382C620604C9393A46B368A466A55B756DFA02C7335D474469 21 13845BAFC4CD9164134507780598652A3258CC3C573FED0F23E8ED882A95F71E0 6 5440CD8DDD96B0BE4FECE0986AD5D5E1ED837FB743D24DA47D46C35165EE2CFB 4 2B6D777402034F257672648C86A239FBF80CB13BE9D5366503799DAFE1389E6E 6 8352806A9F2A72280568AF25CF84853FB97797DD9462344F00F46804D7DA6BF0 4 257FADE0B8C4BAFE1E701A92B0A86485F87C3F39BCA32AFC79E03151CBB6899F", + "u": "21 14469DC22C807EF596E1ADDA5CE8C214BA2D8BBABFBCDE7DC004282BFC7BC5E6D 21 10DF670CD8CB13AF3B881758B58D500605D2FA12A8DD31FA9D03EC671DBE9976E 6 64C6246591AB0A440E8BF053BE303122CC033A2FCDF65F4BC99E0B70D43A97EF 4 2EA64ED893C1D01098CD57A17276FC1BB8D737D58F0A53613041218E2D6D10B5 6 7C76C90A62311399E702C95871BB3A6578F09D4DEF6FF11B31EE36A67F7AD79A 4 1A573663F8BA8693F7DB05485B415E3D6511F3F7A43674345221EE59D4829688", + }, + }, + } + ], + "aggregated_proof": { + "c_hash": "57363661658638127431038526438947214485171900767855222428637184224177462688850", + "c_list": [ + [ + 4, + 12, + 196, + 249, + 69, + 109, + 233, + 15, + 19, + 185, + 56, + 188, + 56, + 74, + 111, + 91, + 193, + 115, + 182, + 138, + 207, + 16, + 248, + 255, + 97, + 249, + 162, + 54, + 175, + 239, + 138, + 21, + 173, + 21, + 47, + 182, + 0, + 224, + 223, + 147, + 133, + 177, + 7, + 66, + 214, + 213, + 204, + 238, + 75, + 219, + 33, + 115, + 30, + 243, + 132, + 200, + 185, + 81, + 250, + 85, + 141, + 175, + 223, + 133, + 118, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + [ + 4, + 15, + 189, + 215, + 22, + 168, + 110, + 143, + 45, + 26, + 18, + 252, + 166, + 6, + 219, + 13, + 49, + 67, + 222, + 196, + 199, + 206, + 197, + 134, + 107, + 228, + 42, + 111, + 177, + 121, + 202, + 61, + 137, + 31, + 238, + 25, + 157, + 241, + 30, + 133, + 172, + 25, + 235, + 184, + 145, + 16, + 176, + 85, + 215, + 140, + 146, + 114, + 43, + 163, + 1, + 109, + 32, + 183, + 46, + 206, + 220, + 220, + 190, + 136, + 133, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + [ + 4, + 12, + 97, + 61, + 34, + 131, + 238, + 234, + 77, + 175, + 24, + 21, + 20, + 184, + 135, + 150, + 174, + 166, + 170, + 22, + 64, + 69, + 147, + 87, + 102, + 1, + 70, + 71, + 9, + 148, + 130, + 219, + 150, + 18, + 65, + 77, + 240, + 27, + 90, + 17, + 20, + 26, + 55, + 205, + 224, + 48, + 247, + 178, + 133, + 45, + 244, + 28, + 158, + 164, + 197, + 141, + 7, + 210, + 228, + 2, + 27, + 231, + 21, + 102, + 143, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + [ + 4, + 25, + 228, + 152, + 198, + 93, + 188, + 146, + 223, + 229, + 127, + 158, + 85, + 244, + 87, + 50, + 176, + 254, + 181, + 172, + 243, + 101, + 114, + 198, + 54, + 34, + 52, + 144, + 176, + 24, + 62, + 124, + 165, + 27, + 110, + 207, + 236, + 193, + 200, + 51, + 143, + 159, + 171, + 47, + 230, + 103, + 253, + 20, + 226, + 243, + 17, + 251, + 245, + 73, + 252, + 224, + 192, + 149, + 129, + 68, + 128, + 129, + 186, + 225, + 87, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + [ + 15, + 32, + 107, + 89, + 100, + 140, + 118, + 230, + 170, + 196, + 33, + 129, + 149, + 120, + 146, + 9, + 159, + 107, + 131, + 203, + 79, + 159, + 244, + 130, + 77, + 176, + 198, + 205, + 174, + 143, + 53, + 39, + 11, + 127, + 150, + 44, + 234, + 105, + 31, + 73, + 127, + 118, + 176, + 199, + 113, + 244, + 245, + 163, + 63, + 119, + 203, + 251, + 223, + 7, + 129, + 21, + 107, + 247, + 240, + 165, + 234, + 36, + 93, + 14, + 11, + 169, + 22, + 156, + 115, + 5, + 16, + 255, + 25, + 49, + 13, + 253, + 104, + 97, + 95, + 123, + 190, + 112, + 247, + 176, + 254, + 243, + 11, + 120, + 4, + 60, + 152, + 174, + 115, + 157, + 100, + 85, + 13, + 231, + 198, + 56, + 247, + 239, + 46, + 174, + 68, + 238, + 73, + 91, + 243, + 205, + 108, + 190, + 193, + 112, + 178, + 251, + 191, + 220, + 76, + 14, + 144, + 30, + 49, + 144, + 91, + 42, + 160, + 222, + ], + [ + 36, + 228, + 220, + 20, + 145, + 124, + 198, + 157, + 90, + 165, + 236, + 89, + 171, + 105, + 129, + 168, + 56, + 95, + 159, + 131, + 68, + 204, + 36, + 255, + 217, + 83, + 15, + 231, + 242, + 177, + 36, + 230, + 16, + 122, + 71, + 44, + 0, + 75, + 122, + 27, + 178, + 233, + 45, + 225, + 139, + 59, + 167, + 80, + 152, + 127, + 155, + 214, + 166, + 65, + 33, + 48, + 224, + 20, + 175, + 31, + 131, + 130, + 27, + 82, + 4, + 229, + 90, + 106, + 108, + 89, + 227, + 3, + 62, + 51, + 49, + 184, + 84, + 234, + 152, + 237, + 221, + 168, + 242, + 83, + 239, + 69, + 78, + 211, + 23, + 188, + 168, + 215, + 179, + 212, + 30, + 214, + 36, + 222, + 100, + 38, + 19, + 162, + 7, + 185, + 119, + 66, + 36, + 245, + 13, + 68, + 98, + 115, + 77, + 229, + 52, + 3, + 229, + 50, + 190, + 130, + 208, + 199, + 28, + 113, + 239, + 6, + 18, + 185, + ], + [ + 6, + 17, + 170, + 116, + 247, + 33, + 250, + 89, + 85, + 106, + 233, + 131, + 132, + 99, + 20, + 63, + 37, + 223, + 131, + 116, + 153, + 137, + 180, + 169, + 206, + 26, + 34, + 253, + 147, + 254, + 137, + 24, + 9, + 73, + 164, + 58, + 58, + 173, + 191, + 96, + 81, + 85, + 179, + 13, + 234, + 255, + 231, + 141, + 51, + 14, + 116, + 117, + 77, + 172, + 108, + 218, + 42, + 183, + 175, + 213, + 135, + 103, + 153, + 195, + 31, + 147, + 43, + 101, + 123, + 238, + 241, + 74, + 228, + 224, + 145, + 65, + 247, + 173, + 234, + 27, + 248, + 147, + 198, + 195, + 244, + 84, + 213, + 228, + 100, + 181, + 19, + 248, + 206, + 91, + 162, + 24, + 8, + 108, + 69, + 103, + 158, + 55, + 128, + 220, + 123, + 223, + 208, + 38, + 225, + 218, + 59, + 32, + 84, + 5, + 164, + 0, + 205, + 42, + 95, + 114, + 174, + 52, + 211, + 107, + 114, + 225, + 217, + 179, + ], + [ + 21, + 205, + 66, + 159, + 152, + 122, + 39, + 49, + 164, + 139, + 62, + 39, + 133, + 69, + 234, + 243, + 35, + 153, + 83, + 25, + 113, + 187, + 118, + 192, + 15, + 255, + 227, + 99, + 0, + 49, + 163, + 51, + 167, + 190, + 26, + 83, + 129, + 114, + 79, + 216, + 74, + 241, + 36, + 173, + 207, + 95, + 115, + 123, + 178, + 46, + 122, + 21, + 216, + 73, + 126, + 41, + 189, + 162, + 120, + 226, + 98, + 191, + 135, + 111, + 27, + 170, + 33, + 18, + 120, + 164, + 3, + 187, + 164, + 123, + 163, + 14, + 157, + 171, + 6, + 62, + 132, + 52, + 188, + 248, + 202, + 164, + 191, + 201, + 53, + 204, + 166, + 130, + 63, + 0, + 15, + 169, + 124, + 140, + 9, + 119, + 209, + 151, + 72, + 163, + 77, + 77, + 27, + 20, + 89, + 130, + 183, + 41, + 104, + 101, + 182, + 250, + 245, + 97, + 144, + 96, + 62, + 18, + 77, + 183, + 93, + 101, + 233, + 97, + 13, + 81, + 167, + 110, + 101, + 19, + 213, + 211, + 249, + 8, + 239, + 157, + 84, + 24, + 136, + 31, + 252, + 225, + 159, + 59, + 151, + 94, + 224, + 184, + 115, + 162, + 155, + 234, + 37, + 168, + 110, + 213, + 231, + 150, + 143, + 124, + 228, + 109, + 43, + 189, + 183, + 231, + 11, + 30, + 217, + 5, + 21, + 221, + 178, + 137, + 4, + 40, + 90, + 220, + 242, + 163, + 154, + 54, + 143, + 42, + 70, + 117, + 205, + 192, + 219, + 102, + 212, + 100, + 196, + 154, + 229, + 43, + 234, + 174, + 71, + 44, + 228, + 166, + 2, + 155, + 204, + 173, + 244, + 33, + 81, + 188, + 60, + 236, + 204, + 135, + 52, + 136, + 200, + 165, + 158, + 112, + 253, + 241, + 81, + 60, + 174, + 130, + 97, + 80, + 62, + 248, + 147, + 144, + 218, + 253, + 172, + 78, + 82, + 83, + 65, + 12, + 22, + 39, + 114, + 202, + 209, + 13, + 5, + 145, + 253, + 14, + 161, + 236, + ], + ], + }, + }, + "requested_proof": { + "revealed_attrs": {}, + "revealed_attr_groups": { + "req_attr_0": { + "sub_proof_index": 0, + "values": { + "country": { + "raw": "Canada", + "encoded": "86091274455216294775467369229742497264338784747007530312638607524003986429176", + }, + "given_names": { + "raw": "Joyce", + "encoded": "76669239303478628101977841967793018579704250003747303526675562174585617552380", + }, + "family_name": { + "raw": "Lee-Martinez", + "encoded": "113629974197202028031743901156327948162780316423694730740234167618518062162596", + }, + }, + } + }, + "self_attested_attrs": {}, + "unrevealed_attrs": {}, + "predicates": {}, }, + "identifiers": [ + { + "schema_id": "QEquAHkM35w4XVT3Ku5yat:2:Person:1.2", + "cred_def_id": "QEquAHkM35w4XVT3Ku5yat:3:CL:7174:Person", + "rev_reg_id": "QEquAHkM35w4XVT3Ku5yat:4:QEquAHkM35w4XVT3Ku5yat:3:CL:7174:Person:CL_ACCUM:8b290969-7045-4c8e-965c-ddba20c57c4d", + "timestamp": 1710972108, + } + ], } - ], + }, }, - "initiator": "self", - "updated_at": "2023-09-15T17:49:33.477755Z", - "trace": False, + "verified": "true", + "verified_msgs": [], + "auto_present": False, "auto_verify": True, - "verified_msgs": ["RMV_GLB_NRI", "RMV_RFNT_NRI::req_attr_0"], + "auto_remove": True, } auth_session = AuthSession( pres_exch_id="e444bc3e-346d-47d1-882d-39c014b8978c", expired_timestamp=datetime.now() + timedelta(seconds=3000), - ver_config_id="verified-email", + ver_config_id="showcase-person", request_parameters={ "scope": "openid vc_authn", "state": "oFLNfUyzDtWHmc61dNiQZkVZRsRUUXZ5KZIiQBeQuJQ.xfaKQBh1xfQ.T02DEr3QRTmMUfjegc9fQQ", @@ -95,6 +1335,7 @@ pyop_auth_code="str", response_url="str", proof_status="pending", + # presentation_exchange=presentation["by_format"], ) ver_config = VerificationConfig( diff --git a/oidc-controller/api/core/oidc/tests/test_issue_token_service.py b/oidc-controller/api/core/oidc/tests/test_issue_token_service.py index 601257fc..81358b71 100644 --- a/oidc-controller/api/core/oidc/tests/test_issue_token_service.py +++ b/oidc-controller/api/core/oidc/tests/test_issue_token_service.py @@ -68,20 +68,20 @@ @pytest.mark.asyncio async def test_valid_proof_presentation_with_one_attribute_returns_claims(): - presentation["presentation_request"][ + presentation["by_format"]["pres_request"]["indy"][ "requested_attributes" ] = basic_valid_requested_attributes - presentation["presentation"]["requested_proof"][ + presentation["by_format"]["pres"]["indy"]["requested_proof"][ "revealed_attr_groups" ] = basic_valid_revealed_attr_groups - with mock.patch.object(AuthSession, "presentation_exchange", presentation): - claims = Token.get_claims(auth_session, ver_config) - assert claims is not None + auth_session.presentation_exchange = presentation["by_format"] + claims = Token.get_claims(auth_session, ver_config) + assert claims is not None @pytest.mark.asyncio async def test_valid_proof_presentation_with_multiple_attributes_returns_claims(): - presentation["presentation_request"]["requested_attributes"] = { + presentation["by_format"]["pres_request"]["indy"]["requested_attributes"] = { "req_attr_0": { "names": ["email"], "restrictions": [ @@ -101,7 +101,9 @@ async def test_valid_proof_presentation_with_multiple_attributes_returns_claims( ], }, } - presentation["presentation"]["requested_proof"]["revealed_attr_groups"] = { + presentation["by_format"]["pres"]["indy"]["requested_proof"][ + "revealed_attr_groups" + ] = { "req_attr_0": { "sub_proof_index": 0, "values": { @@ -127,72 +129,72 @@ async def test_valid_proof_presentation_with_multiple_attributes_returns_claims( }, }, } - with mock.patch.object(AuthSession, "presentation_exchange", presentation): - claims = Token.get_claims(auth_session, ver_config) - assert claims is not None + auth_session.presentation_exchange = presentation["by_format"] + claims = Token.get_claims(auth_session, ver_config) + assert claims is not None @pytest.mark.asyncio async def test_include_v1_attributes_false_does_not_add_the_named_attributes(): - presentation["presentation_request"][ + presentation["by_format"]["pres_request"]["indy"][ "requested_attributes" ] = multiple_valid_requested_attributes - presentation["presentation"]["requested_proof"][ + presentation["by_format"]["pres"]["indy"]["requested_proof"][ "revealed_attr_groups" ] = multiple_valid_revealed_attr_groups - with mock.patch.object(AuthSession, "presentation_exchange", presentation): - ver_config.include_v1_attributes = False - claims = Token.get_claims(auth_session, ver_config) - vc_presented_attributes_obj = eval(claims["vc_presented_attributes"]) - assert claims is not None - assert vc_presented_attributes_obj["email_1"] == "test@email.com" - assert vc_presented_attributes_obj["age_1"] == "30" - assert "email_1" not in claims - assert "age_1" not in claims + auth_session.presentation_exchange = presentation["by_format"] + ver_config.include_v1_attributes = False + claims = Token.get_claims(auth_session, ver_config) + vc_presented_attributes_obj = eval(claims["vc_presented_attributes"]) + assert claims is not None + assert vc_presented_attributes_obj["email_1"] == "test@email.com" + assert vc_presented_attributes_obj["age_1"] == "30" + assert "email_1" not in claims + assert "age_1" not in claims @pytest.mark.asyncio async def test_include_v1_attributes_true_adds_the_named_attributes(): - presentation["presentation_request"][ + presentation["by_format"]["pres_request"]["indy"][ "requested_attributes" ] = multiple_valid_requested_attributes - presentation["presentation"]["requested_proof"][ + presentation["by_format"]["pres"]["indy"]["requested_proof"][ "revealed_attr_groups" ] = multiple_valid_revealed_attr_groups - with mock.patch.object(AuthSession, "presentation_exchange", presentation): - ver_config.include_v1_attributes = True - claims = Token.get_claims(auth_session, ver_config) - vc_presented_attributes_obj = eval(claims["vc_presented_attributes"]) - assert claims is not None - assert vc_presented_attributes_obj["email_1"] == "test@email.com" - assert vc_presented_attributes_obj["age_1"] == "30" - assert claims["email_1"] == "test@email.com" - assert claims["age_1"] == "30" + auth_session.presentation_exchange = presentation["by_format"] + ver_config.include_v1_attributes = True + claims = Token.get_claims(auth_session, ver_config) + vc_presented_attributes_obj = eval(claims["vc_presented_attributes"]) + assert claims is not None + assert vc_presented_attributes_obj["email_1"] == "test@email.com" + assert vc_presented_attributes_obj["age_1"] == "30" + assert claims["email_1"] == "test@email.com" + assert claims["age_1"] == "30" @pytest.mark.asyncio async def test_include_v1_attributes_none_does_not_add_the_named_attributes(): - presentation["presentation_request"][ + presentation["by_format"]["pres_request"]["indy"][ "requested_attributes" ] = multiple_valid_requested_attributes - presentation["presentation"]["requested_proof"][ + presentation["by_format"]["pres"]["indy"]["requested_proof"][ "revealed_attr_groups" ] = multiple_valid_revealed_attr_groups - with mock.patch.object(AuthSession, "presentation_exchange", presentation): - ver_config.include_v1_attributes = None - print(ver_config.include_v1_attributes) - claims = Token.get_claims(auth_session, ver_config) - vc_presented_attributes_obj = eval(claims["vc_presented_attributes"]) - assert claims is not None - assert vc_presented_attributes_obj["email_1"] == "test@email.com" - assert vc_presented_attributes_obj["age_1"] == "30" - assert "email_1" not in claims - assert "age_1" not in claims + auth_session.presentation_exchange = presentation["by_format"] + ver_config.include_v1_attributes = None + print(ver_config.include_v1_attributes) + claims = Token.get_claims(auth_session, ver_config) + vc_presented_attributes_obj = eval(claims["vc_presented_attributes"]) + assert claims is not None + assert vc_presented_attributes_obj["email_1"] == "test@email.com" + assert vc_presented_attributes_obj["age_1"] == "30" + assert "email_1" not in claims + assert "age_1" not in claims @pytest.mark.asyncio async def test_revealed_attrs_dont_match_requested_attributes_throws_exception(): - presentation["presentation_request"]["requested_attributes"] = { + presentation["by_format"]["pres_request"]["indy"]["requested_attributes"] = { "req_attr_0": { "names": ["email"], "restrictions": [ @@ -203,7 +205,9 @@ async def test_revealed_attrs_dont_match_requested_attributes_throws_exception() ], } } - presentation["presentation"]["requested_proof"]["revealed_attr_groups"] = { + presentation["by_format"]["pres"]["indy"]["requested_proof"][ + "revealed_attr_groups" + ] = { "req_attr_0": { "sub_proof_index": 0, "values": { @@ -217,77 +221,77 @@ async def test_revealed_attrs_dont_match_requested_attributes_throws_exception() }, } } - with mock.patch.object(AuthSession, "presentation_exchange", presentation): - with pytest.raises(Exception): - Token.get_claims(auth_session, ver_config) + auth_session.presentation_exchange = presentation["by_format"] + with pytest.raises(Exception): + Token.get_claims(auth_session, ver_config) @pytest.mark.asyncio async def test_valid_presentation_with_matching_subject_identifier_in_claims_sub(): - presentation["presentation_request"][ + presentation["by_format"]["pres_request"]["indy"][ "requested_attributes" ] = basic_valid_requested_attributes - presentation["presentation"]["requested_proof"][ + presentation["by_format"]["pres"]["indy"]["requested_proof"][ "revealed_attr_groups" ] = basic_valid_revealed_attr_groups - with mock.patch.object(AuthSession, "presentation_exchange", presentation): - claims = Token.get_claims(auth_session, ver_config) - print(claims) - assert claims["sub"] == "test@email.com" + auth_session.presentation_exchange = presentation["by_format"] + claims = Token.get_claims(auth_session, ver_config) + print(claims) + assert claims["sub"] == "test@email.com" @pytest.mark.asyncio async def test_valid_pres_with_non_matching_subj_id_gen_consistent_id_missing_no_sub(): - presentation["presentation_request"][ + presentation["by_format"]["pres_request"]["indy"][ "requested_attributes" ] = basic_valid_requested_attributes - presentation["presentation"]["requested_proof"][ + presentation["by_format"]["pres"]["indy"]["requested_proof"][ "revealed_attr_groups" ] = basic_valid_revealed_attr_groups - with mock.patch.object(AuthSession, "presentation_exchange", presentation): - ver_config.subject_identifier = "not-email" - claims = Token.get_claims(auth_session, ver_config) - assert not ver_config.generate_consistent_identifier - assert "sub" not in claims + auth_session.presentation_exchange = presentation["by_format"] + ver_config.subject_identifier = "not-email" + claims = Token.get_claims(auth_session, ver_config) + assert not ver_config.generate_consistent_identifier + assert "sub" not in claims @pytest.mark.asyncio async def test_valid_pres_non_matching_subj_id_gen_consistent_id_false_has_no_sub(): - presentation["presentation_request"][ + presentation["by_format"]["pres_request"]["indy"][ "requested_attributes" ] = basic_valid_requested_attributes - presentation["presentation"]["requested_proof"][ + presentation["by_format"]["pres"]["indy"]["requested_proof"][ "revealed_attr_groups" ] = basic_valid_revealed_attr_groups - with mock.patch.object(AuthSession, "presentation_exchange", presentation): - ver_config.subject_identifier = "not-email" - ver_config.generate_consistent_identifier = False - claims = Token.get_claims(auth_session, ver_config) - assert "sub" not in claims + auth_session.presentation_exchange = presentation["by_format"] + ver_config.subject_identifier = "not-email" + ver_config.generate_consistent_identifier = False + claims = Token.get_claims(auth_session, ver_config) + assert "sub" not in claims @pytest.mark.asyncio async def test_valid_pres_non_matching_subj_id_gen_consistent_id_true_has_sub(): - presentation["presentation_request"][ + presentation["by_format"]["pres_request"]["indy"][ "requested_attributes" ] = basic_valid_requested_attributes - presentation["presentation"]["requested_proof"][ + presentation["by_format"]["pres"]["indy"]["requested_proof"][ "revealed_attr_groups" ] = basic_valid_revealed_attr_groups - with mock.patch.object(AuthSession, "presentation_exchange", presentation): - ver_config.subject_identifier = "not-email" - ver_config.generate_consistent_identifier = True - claims = Token.get_claims(auth_session, ver_config) - assert "sub" in claims + auth_session.presentation_exchange = presentation["by_format"] + ver_config.subject_identifier = "not-email" + ver_config.generate_consistent_identifier = True + claims = Token.get_claims(auth_session, ver_config) + assert "sub" in claims - # Ensure that this sub is not using the ver_config.subject_identifier - ver_config.subject_identifier = "email" - ver_config.generate_consistent_identifier = False - claims_subject_identifier = Token.get_claims(auth_session, ver_config) - assert claims["sub"] != claims_subject_identifier["sub"] + # Ensure that this sub is not using the ver_config.subject_identifier + ver_config.subject_identifier = "email" + ver_config.generate_consistent_identifier = False + claims_subject_identifier = Token.get_claims(auth_session, ver_config) + assert claims["sub"] != claims_subject_identifier["sub"] - # Ensure that sub is consistent - ver_config.subject_identifier = "not-email" - ver_config.generate_consistent_identifier = True - claims_duplicate = Token.get_claims(auth_session, ver_config) - assert claims["sub"] == claims_duplicate["sub"] + # Ensure that sub is consistent + ver_config.subject_identifier = "not-email" + ver_config.generate_consistent_identifier = True + claims_duplicate = Token.get_claims(auth_session, ver_config) + assert claims["sub"] == claims_duplicate["sub"] diff --git a/oidc-controller/api/routers/acapy_handler.py b/oidc-controller/api/routers/acapy_handler.py index 4dc45e97..8e77052f 100644 --- a/oidc-controller/api/routers/acapy_handler.py +++ b/oidc-controller/api/routers/acapy_handler.py @@ -26,12 +26,14 @@ async def _parse_webhook_body(request: Request): async def post_topic(request: Request, topic: str, db: Database = Depends(get_db)): """Called by aca-py agent.""" logger.info(f">>> post_topic : topic={topic}") + logger.info(f">>> web hook post_body : {await _parse_webhook_body(request)}") client = AcapyClient() match topic: case "present_proof_v2_0": webhook_body = await _parse_webhook_body(request) logger.info(f">>>> pres_exch_id: {webhook_body['pres_ex_id']}") + # logger.info(f">>>> web hook: {webhook_body}") auth_session: AuthSession = await AuthSessionCRUD(db).get_by_pres_exch_id( webhook_body["pres_ex_id"] ) From 45dba163d9201fcb188122ccee75d9093889cecc Mon Sep 17 00:00:00 2001 From: Gavin Jaeger-Freeborn Date: Fri, 12 Jul 2024 13:14:26 -0700 Subject: [PATCH 24/31] Change default present proof to OOB Signed-off-by: Gavin Jaeger-Freeborn --- docker/manage | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/manage b/docker/manage index 76ba7379..ac2c7697 100755 --- a/docker/manage +++ b/docker/manage @@ -181,7 +181,7 @@ configureEnvironment() { #controller app settings export SET_NON_REVOKED="True" - export USE_OOB_PRESENT_PROOF=${USE_OOB_PRESENT_PROOF:-"false"} + export USE_OOB_PRESENT_PROOF=${USE_OOB_PRESENT_PROOF:-"true"} export USE_OOB_LOCAL_DID_SERVICE=${USE_OOB_LOCAL_DID_SERVICE:-"true"} # agent From a2ff83ca7cc0292cbf976d6d1383b19985104dd9 Mon Sep 17 00:00:00 2001 From: Gavin Jaeger-Freeborn Date: Tue, 16 Jul 2024 13:27:41 -0700 Subject: [PATCH 25/31] Removed unused code Signed-off-by: Gavin Jaeger-Freeborn --- oidc-controller/api/authSessions/models.py | 3 +-- .../api/core/oidc/tests/test_issue_token_service.py | 2 -- oidc-controller/api/routers/acapy_handler.py | 2 -- 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/oidc-controller/api/authSessions/models.py b/oidc-controller/api/authSessions/models.py index 0bd6c9e9..7bc001e7 100644 --- a/oidc-controller/api/authSessions/models.py +++ b/oidc-controller/api/authSessions/models.py @@ -1,8 +1,7 @@ from datetime import datetime, timedelta from enum import StrEnum, auto -from typing import Dict, Optional +from typing import Optional -from api.core.acapy.client import AcapyClient from api.core.models import UUIDModel from pydantic import BaseModel, ConfigDict, Field diff --git a/oidc-controller/api/core/oidc/tests/test_issue_token_service.py b/oidc-controller/api/core/oidc/tests/test_issue_token_service.py index 81358b71..48fc9345 100644 --- a/oidc-controller/api/core/oidc/tests/test_issue_token_service.py +++ b/oidc-controller/api/core/oidc/tests/test_issue_token_service.py @@ -1,6 +1,4 @@ -import mock import pytest -from api.authSessions.models import AuthSession from api.core.oidc.issue_token_service import Token from api.core.oidc.tests.__mocks__ import auth_session, presentation, ver_config diff --git a/oidc-controller/api/routers/acapy_handler.py b/oidc-controller/api/routers/acapy_handler.py index 41f9d62e..2d39e939 100644 --- a/oidc-controller/api/routers/acapy_handler.py +++ b/oidc-controller/api/routers/acapy_handler.py @@ -7,7 +7,6 @@ from ..authSessions.crud import AuthSessionCRUD from ..authSessions.models import AuthSession, AuthSessionPatch, AuthSessionState -from ..core.acapy.client import AcapyClient from ..db.session import get_db from ..core.config import settings @@ -28,7 +27,6 @@ async def post_topic(request: Request, topic: str, db: Database = Depends(get_db logger.info(f">>> post_topic : topic={topic}") logger.info(f">>> web hook post_body : {await _parse_webhook_body(request)}") - client = AcapyClient() match topic: case "present_proof_v2_0": webhook_body = await _parse_webhook_body(request) From 531722ad547ab969982cbaaba18f15d469b469f1 Mon Sep 17 00:00:00 2001 From: Gavin Jaeger-Freeborn Date: Fri, 19 Jul 2024 10:29:43 -0700 Subject: [PATCH 26/31] Removed unnecessary tracing of present proof 2.0 Signed-off-by: Gavin Jaeger-Freeborn --- oidc-controller/api/core/acapy/client.py | 1 - 1 file changed, 1 deletion(-) diff --git a/oidc-controller/api/core/acapy/client.py b/oidc-controller/api/core/acapy/client.py index b31a1044..42fa0a3c 100644 --- a/oidc-controller/api/core/acapy/client.py +++ b/oidc-controller/api/core/acapy/client.py @@ -124,7 +124,6 @@ def oob_create_invitation( } ], "use_public_did": use_public_did, - "trace": True, } resp_raw = requests.post( From 6c6c1bc51366b808521baa35c222b2a147bdefae Mon Sep 17 00:00:00 2001 From: Gavin Jaeger-Freeborn Date: Tue, 23 Jul 2024 15:57:08 -0700 Subject: [PATCH 27/31] Removed outdated attachment creation Signed-off-by: Gavin Jaeger-Freeborn --- oidc-controller/api/routers/oidc.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/oidc-controller/api/routers/oidc.py b/oidc-controller/api/routers/oidc.py index 1bb7a9df..1dc02a44 100644 --- a/oidc-controller/api/routers/oidc.py +++ b/oidc-controller/api/routers/oidc.py @@ -113,9 +113,6 @@ async def get_authorize(request: Request, db: Database = Depends(get_db)): # Prepeare the presentation request use_public_did = not settings.USE_OOB_LOCAL_DID_SERVICE - wallet_did = client.get_wallet_did(public=use_public_did) - - byo_attachment = PresentProofv10Attachment.build(pres_exch_dict["pres_request"]) msg = None if settings.USE_OOB_PRESENT_PROOF: From 9466e88fa991160898cc1e26748a9ff31f75759e Mon Sep 17 00:00:00 2001 From: Gavin Jaeger-Freeborn Date: Thu, 25 Jul 2024 13:58:59 -0700 Subject: [PATCH 28/31] Added missing formats field Signed-off-by: Gavin Jaeger-Freeborn --- oidc-controller/api/core/aries/__init__.py | 2 +- .../api/core/aries/present_proof_attachment.py | 6 +++--- .../api/core/aries/present_proof_presentation.py | 7 ++++--- oidc-controller/api/routers/oidc.py | 11 ++++++++++- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/oidc-controller/api/core/aries/__init__.py b/oidc-controller/api/core/aries/__init__.py index 28d321c5..935f52ff 100644 --- a/oidc-controller/api/core/aries/__init__.py +++ b/oidc-controller/api/core/aries/__init__.py @@ -1,6 +1,6 @@ # flake8: noqa -from .present_proof_attachment import PresentProofv10Attachment +from .present_proof_attachment import PresentProofv20Attachment from .service_decorator import ServiceDecorator, OOBServiceDecorator from .present_proof_presentation import PresentationRequestMessage diff --git a/oidc-controller/api/core/aries/present_proof_attachment.py b/oidc-controller/api/core/aries/present_proof_attachment.py index d27c6c23..f77bf079 100644 --- a/oidc-controller/api/core/aries/present_proof_attachment.py +++ b/oidc-controller/api/core/aries/present_proof_attachment.py @@ -5,8 +5,8 @@ from pydantic import BaseModel, Field -class PresentProofv10Attachment(BaseModel): - # https://github.com/hyperledger/aries-rfcs/blob/main/features/0037-present-proof/README.md#request-presentation +class PresentProofv20Attachment(BaseModel): + # https://github.com/hyperledger/aries-rfcs/tree/eace815c3e8598d4a8dd7881d8c731fdb2bcc0aa/features/0454-present-proof-v2 id: str = Field(default="libindy-request-presentation-0", alias="@id") mime_type: str = Field(default="application/json", alias="mime-type") data: Dict @@ -14,7 +14,7 @@ class PresentProofv10Attachment(BaseModel): @classmethod def build( cls, presentation_request - ) -> "PresentProofv10Attachment": # bundle everything needed for the QR code + ) -> "PresentProofv20Attachment": # bundle everything needed for the QR code return cls( data={ "base64": base64.b64encode( diff --git a/oidc-controller/api/core/aries/present_proof_presentation.py b/oidc-controller/api/core/aries/present_proof_presentation.py index 1eb37afa..ffe49b0c 100644 --- a/oidc-controller/api/core/aries/present_proof_presentation.py +++ b/oidc-controller/api/core/aries/present_proof_presentation.py @@ -1,9 +1,9 @@ import json import base64 -from typing import Optional, List +from typing import Optional, List, Dict from pydantic import BaseModel, ConfigDict, Field -from api.core.aries import PresentProofv10Attachment, ServiceDecorator +from api.core.aries import PresentProofv20Attachment, ServiceDecorator class PresentationRequestMessage(BaseModel): @@ -13,7 +13,8 @@ class PresentationRequestMessage(BaseModel): "https://didcomm.org/present-proof/2.0/request-presentation", alias="@type", ) - request: List[PresentProofv10Attachment] = Field( + formats: List[Dict] + request: List[PresentProofv20Attachment] = Field( alias="request_presentations~attach" ) comment: Optional[str] = None diff --git a/oidc-controller/api/routers/oidc.py b/oidc-controller/api/routers/oidc.py index 1dc02a44..a79e7f68 100644 --- a/oidc-controller/api/routers/oidc.py +++ b/oidc-controller/api/routers/oidc.py @@ -123,12 +123,21 @@ async def get_authorize(request: Request, db: Database = Depends(get_db)): else: wallet_did = client.get_wallet_did(public=use_public_did) - byo_attachment = PresentProofv10Attachment.build(pres_exch_dict["pres_request"]) + byo_attachment = PresentProofv20Attachment( + # As of present proof 2.0 the + data=pres_exch_dict["pres_request"]["request_presentations~attach"][0][ + "data" + ] + ) + s_d = ServiceDecorator( service_endpoint=client.service_endpoint, recipient_keys=[wallet_did.verkey] ) msg = PresentationRequestMessage( id=pres_exch_dict["thread_id"], + formats=[ + {"attach_id": byo_attachment.id, "format": "hlindy/proof-req@v2.0"} + ], request=[byo_attachment], service=s_d, ) From 0860357d1b335d2983e981d8d2a4420ebee7247f Mon Sep 17 00:00:00 2001 From: Gavin Jaeger-Freeborn Date: Thu, 25 Jul 2024 14:20:18 -0700 Subject: [PATCH 29/31] Clarify of constructing an attachment and removed build method Signed-off-by: Gavin Jaeger-Freeborn --- .../api/core/aries/present_proof_attachment.py | 12 ------------ oidc-controller/api/routers/oidc.py | 4 +++- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/oidc-controller/api/core/aries/present_proof_attachment.py b/oidc-controller/api/core/aries/present_proof_attachment.py index f77bf079..fc7a2873 100644 --- a/oidc-controller/api/core/aries/present_proof_attachment.py +++ b/oidc-controller/api/core/aries/present_proof_attachment.py @@ -10,15 +10,3 @@ class PresentProofv20Attachment(BaseModel): id: str = Field(default="libindy-request-presentation-0", alias="@id") mime_type: str = Field(default="application/json", alias="mime-type") data: Dict - - @classmethod - def build( - cls, presentation_request - ) -> "PresentProofv20Attachment": # bundle everything needed for the QR code - return cls( - data={ - "base64": base64.b64encode( - json.dumps(presentation_request).encode("ascii") - ).decode("ascii") - } - ) diff --git a/oidc-controller/api/routers/oidc.py b/oidc-controller/api/routers/oidc.py index b5e1b35d..13564a36 100644 --- a/oidc-controller/api/routers/oidc.py +++ b/oidc-controller/api/routers/oidc.py @@ -141,7 +141,9 @@ async def get_authorize(request: Request, db: Database = Depends(get_db)): wallet_did = client.get_wallet_did(public=use_public_did) byo_attachment = PresentProofv20Attachment( - # As of present proof 2.0 the + # As of present proof 2.0 the pres_exch_dict already + # contains a complete attachment so we only need to + # extract the base64 encoded data data=pres_exch_dict["pres_request"]["request_presentations~attach"][0][ "data" ] From da3b9952a7f5de41f78f3be05a477a65e7b335bf Mon Sep 17 00:00:00 2001 From: Gavin Jaeger-Freeborn Date: Thu, 25 Jul 2024 14:23:24 -0700 Subject: [PATCH 30/31] removed unused imports Signed-off-by: Gavin Jaeger-Freeborn --- oidc-controller/api/core/aries/present_proof_attachment.py | 3 --- oidc-controller/api/routers/oidc.py | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/oidc-controller/api/core/aries/present_proof_attachment.py b/oidc-controller/api/core/aries/present_proof_attachment.py index fc7a2873..b54cebb1 100644 --- a/oidc-controller/api/core/aries/present_proof_attachment.py +++ b/oidc-controller/api/core/aries/present_proof_attachment.py @@ -1,6 +1,3 @@ -import json -import base64 - from typing import Dict from pydantic import BaseModel, Field diff --git a/oidc-controller/api/routers/oidc.py b/oidc-controller/api/routers/oidc.py index 13564a36..808f8cad 100644 --- a/oidc-controller/api/routers/oidc.py +++ b/oidc-controller/api/routers/oidc.py @@ -21,7 +21,7 @@ from ..core.acapy.client import AcapyClient from ..core.aries import ( PresentationRequestMessage, - PresentProofv10Attachment, + PresentProofv20Attachment, ServiceDecorator, ) from ..core.config import settings From 11b81a21bd1be7ef23cfa9fd2742ba1e5985321e Mon Sep 17 00:00:00 2001 From: Gavin Jaeger-Freeborn Date: Fri, 26 Jul 2024 09:29:25 -0700 Subject: [PATCH 31/31] Bump acapy RC Signed-off-by: Gavin Jaeger-Freeborn --- docker/docker-compose.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index c4591cba..8599d6ba 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -100,7 +100,7 @@ services: - vc_auth aca-py: - image: ghcr.io/hyperledger/aries-cloudagent-python:py3.12-1.0.0rc4 + image: ghcr.io/hyperledger/aries-cloudagent-python:py3.12-1.0.0rc5 environment: - ACAPY_LABEL=${AGENT_NAME} - ACAPY_ENDPOINT=${AGENT_ENDPOINT}