Skip to content

Commit

Permalink
feat: cred ex record management
Browse files Browse the repository at this point in the history
Signed-off-by: Adam Burdett <[email protected]>
  • Loading branch information
burdettadam authored and dbluhm committed Oct 31, 2023
1 parent d1a610b commit 3da6ec4
Show file tree
Hide file tree
Showing 7 changed files with 252 additions and 63 deletions.
71 changes: 71 additions & 0 deletions oid4vci/oid4vci/v1_0/cred_ex_record.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
from typing import Any, Dict, Optional
from aries_cloudagent.messaging.models.base_record import BaseRecord, BaseRecordSchema, BaseExchangeRecord
from marshmallow import fields

class OID4VCICredentialExchangeRecord(BaseExchangeRecord):
class Meta:
schema_class = "CredExRecordSchema"

RECORD_ID_NAME = "oid4vci_ex_id"
RECORD_TYPE= "oid4vci"
EVENT_NAMESPACE= "oid4vci"
TAG_NAMES = {"nonce", "pin", "token"}
def __init__(
self,
*,
credential_supported_id=None,
credential_subject: Optional[Dict[str, Any]] = None,
nonce=None,
pin=None,
token=None,
**kwargs,
):
super().__init__(
None,
state="init",
**kwargs,
)
self.credential_supported_id = credential_supported_id
self.credential_subject = credential_subject # (received from submit)
self.nonce = nonce # in offer
self.pin = pin # (when relevant)
self.token = token

@property
def credential_exchange_id(self) -> str:
"""Accessor for the ID associated with this exchange."""
return self._id


# TODO: add validation
class CredExRecordSchema(BaseRecordSchema):
class Meta:
model_class = OID4VCICredentialExchangeRecord

credential_supported_id = fields.Str(
required=True,
metadata={
"description": "Identifier used to identify credential supported record",
},
)
credential_subject = (
fields.Dict(
required=True,
metadata={
"description": "desired claim and value in credential",
},
),
)
nonce = (
fields.Str(
required=False,
),
)
pin = (
fields.Str(
required=False,
),
)
token = fields.Str(
required=False,
)
97 changes: 97 additions & 0 deletions oid4vci/oid4vci/v1_0/cred_sup_record.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
from aries_cloudagent.messaging.models.base_record import BaseRecord, BaseRecordSchema
from marshmallow import fields


class OID4VCICredentialSupported(BaseRecord):
class Meta:
schema_class = "CredSupRecordSchema"

RECORD_ID_NAME = "oid4vci_id"
RECORD_TYPE= "oid4vci"
EVENT_NAMESPACE= "oid4vci"
TAG_NAMES = {"credential_definition_id", "types", "scope"}

def __init__(
self,
*,
credential_definition_id,
format,
types,
cryptographic_binding_methods_supported,
cryptographic_suites_supported,
display,
credential_subject,
scope,
**kwargs,
):
super().__init__(
None,
state="init",
**kwargs,
)
self.credential_definition_id = credential_definition_id
self.format = format
self.types = types
self.cryptographic_binding_methods_supported = (
cryptographic_binding_methods_supported
)
self.cryptographic_suites_supported = cryptographic_suites_supported
self.display = display
self.credential_subject = credential_subject
self.scope = scope

def web_serialize(self) -> dict:
return self.serialize()

@property
def id(self):
return self._id


# TODO: add validation
class CredSupRecordSchema(BaseRecordSchema):
class Meta:
model_class = OID4VCICredentialSupported

credential_definition_id = fields.Str(
required=True, metadata={"example": "UniversityDegree_JWT"}
)
format = fields.Str(required=True, metadata={"example": "jwt_vc_json"})
types = fields.List(
fields.Str(),
metadata={"example": ["VerifiableCredential", "UniversityDegreeCredential"]},
)
cryptographic_binding_methods_supported = fields.List(
fields.Str(), metadata={"example": []}
)
cryptographic_suites_supported = fields.List(
fields.Str(), metadata={"example": ["ES256K"]}
)
display = fields.List(
fields.Dict(),
metadata={
"example": [
{
"name": "University Credential",
"locale": "en-US",
"logo": {
"url": "https://exampleuniversity.com/public/logo.png",
"alt_text": "a square logo of a university",
},
"background_color": "#12107c",
"text_color": "#FFFFFF",
}
]
},
)
credential_subject = fields.Dict(
metadata={
"given_name": {"display": [{"name": "Given Name", "locale": "en-US"}]},
"family_name": {"display": [{"name": "Surname", "locale": "en-US"}]},
"degree": {},
"gpa": {"display": [{"name": "GPA"}]},
}
)
scope = fields.Str(
required=True,
)
49 changes: 0 additions & 49 deletions oid4vci/oid4vci/v1_0/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,6 @@
from aries_cloudagent.messaging.models.base_record import BaseExchangeRecord, BaseRecord


class OID4VCICredentialExchangeRecord(BaseExchangeRecord):
def __init__(
self,
credential_supported_id=None,
credential_subject: Optional[Dict[str, Any]] = None,
nonce=None,
pin=None,
token=None,
):
self.credential_supported_id = credential_supported_id
self.credential_subject = credential_subject # (received from submit)
self.nonce = nonce # in offer
self.pin = pin # (when relevant)
self.token = token

@property
def credential_exchange_id(self) -> str:
"""Accessor for the ID associated with this exchange."""
return self._id


class CredentialOfferRecord(BaseExchangeRecord): # TODO: do we need this?
def __init__(
Expand All @@ -33,32 +13,3 @@ def __init__(
self.credential_issuer = credential_issuer
self.credentials = credentials
self.grants = grants


class OID4VCICredentialSupported(BaseRecord):
def __init__(
self,
credential_definition_id,
format,
types,
cryptographic_binding_methods_supported,
cryptographic_suites_supported,
display,
credentialSubject,
scope,
):
self.credential_definition_id = credential_definition_id
self.format = format
self.types = types
self.cryptographic_binding_methods_supported = (
cryptographic_binding_methods_supported
)
self.cryptographic_suites_supported = cryptographic_suites_supported
self.display = display
self.credentialSubject = credentialSubject
self.scope = scope

TAG_NAMES = {"credential_definition_id", "types", "scope"}

def web_serialize(self) -> dict:
return self.serialize()
3 changes: 1 addition & 2 deletions oid4vci/oid4vci/v1_0/oid4vci_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@
from aries_cloudagent.utils.stats import Collector
from aries_cloudagent.wallet.jwt import jwt_verify
from marshmallow import fields
from .models import OID4VCICredentialSupported

from .cred_sup_record import OID4VCICredentialSupported
LOGGER = logging.getLogger(__name__)


Expand Down
92 changes: 81 additions & 11 deletions oid4vci/oid4vci/v1_0/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,10 @@
from aries_cloudagent.core.profile import Profile
from aries_cloudagent.messaging.models.openapi import OpenAPISchema
from aries_cloudagent.protocols.basicmessage.v1_0.message_types import SPEC_URI
from aries_cloudagent.utils.tracing import AdminAPIMessageTracingSchema
from marshmallow import fields
from .models import (
CredentialOfferRecord,
OID4VCICredentialExchangeRecord,
)
from .models import CredentialOfferRecord
from .cred_sup_record import OID4VCICredentialSupported
from .cred_ex_record import OID4VCICredentialExchangeRecord

LOGGER = logging.getLogger(__name__)
code_size = 8 # TODO: check
Expand All @@ -44,9 +42,7 @@ class CredExRecordListQueryStringSchema(OpenAPISchema):
)


class CreateCredExSchema(AdminAPIMessageTracingSchema):
"""Filter, auto-remove, comment, trace."""

class CreateCredExSchema(OpenAPISchema):
credential_supported_id = fields.Str(
required=True,
metadata={
Expand Down Expand Up @@ -76,6 +72,51 @@ class CreateCredExSchema(AdminAPIMessageTracingSchema):
)


class CreateCredSupSchema(OpenAPISchema):
credential_definition_id = fields.Str(
required=True, metadata={"example": "UniversityDegree_JWT"}
)
format = fields.Str(required=True, metadata={"example": "jwt_vc_json"})
types = fields.List(
fields.Str(),
metadata={"example": ["VerifiableCredential", "UniversityDegreeCredential"]},
)
cryptographic_binding_methods_supported = fields.List(
fields.Str(), metadata={"example": []}
)
cryptographic_suites_supported = fields.List(
fields.Str(), metadata={"example": ["ES256K"]}
)
display = fields.List(
fields.Dict(),
metadata={
"example": [
{
"name": "University Credential",
"locale": "en-US",
"logo": {
"url": "https://exampleuniversity.com/public/logo.png",
"alt_text": "a square logo of a university",
},
"background_color": "#12107c",
"text_color": "#FFFFFF",
}
]
},
)
credential_subject = fields.Dict(
metadata={
"given_name": {"display": [{"name": "Given Name", "locale": "en-US"}]},
"family_name": {"display": [{"name": "Surname", "locale": "en-US"}]},
"degree": {},
"gpa": {"display": [{"name": "GPA"}]},
}
)
scope = fields.Str(
required=True,
)


class CredExIdMatchInfoSchema(OpenAPISchema):
"""Path parameters and validators for request taking credential exchange id."""

Expand Down Expand Up @@ -221,11 +262,40 @@ async def get_cred_offer(request: web.BaseRequest):
return web.json_response(record)


@docs(tags=["oid4vci"], summary="Get a credential offer")
@querystring_schema(GetCredentialOfferSchema())
@docs(tags=["oid4vci"], summary="Register a Oid4vci credential")
@request_schema(CreateCredSupSchema())
async def credential_supported_create(request: web.BaseRequest):
pass
context = request["context"]
profile = context.profile

body = await request.json()

credential_definition_id = body.get("credential_definition_id")
format = body.get("format")
types = body.get("types")
cryptographic_binding_methods_supported = body.get(
"cryptographic_binding_methods_supported"
)
cryptographic_suites_supported = body.get("cryptographic_suites_supported")
display = body.get("display")
credential_subject = body.get("credential_subject")
scope = body.get("scope")

record = OID4VCICredentialSupported(
credential_definition_id= credential_definition_id,
format= format,
types = types,
cryptographic_binding_methods_supported = cryptographic_binding_methods_supported,
cryptographic_suites_supported = cryptographic_suites_supported,
display = display,
credential_subject = credential_subject,
scope = scope,
)

async with profile.session() as session:
await record.save(session, reason="Save credential supported record.")

return web.json_response(record.serialize())

@docs(
tags=["oid4vci"],
Expand Down
2 changes: 1 addition & 1 deletion oid4vci/poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions oid4vci/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ python = "^3.9"
aiohttp = "^3.8.5"
aries-cloudagent = { version = "0.10.3" }
aiohttp-cors = "^0.7.0"
marshmallow = "^3.20.1"

[tool.poetry.dev-dependencies]
ruff="^0.0.285"
Expand Down

0 comments on commit 3da6ec4

Please sign in to comment.