@@ -327,32 +327,36 @@ class CredOfferSchema(OpenAPISchema):
327327 grants = fields .Nested (CredOfferGrantSchema (), required = True )
328328
329329
330- class CredOfferResponseSchema (OpenAPISchema ):
330+ class CredOfferResponseSchemaVal (OpenAPISchema ):
331331 """Credential Offer Schema."""
332332
333- offer_uri = fields .Str (
333+ credential_offer = fields .Str (
334334 required = True ,
335335 metadata = {
336- "description" : "The URL of the credential issuer ." ,
336+ "description" : "The URL of the credential value for display by QR code ." ,
337337 "example" : "openid-credential-offer://..." ,
338338 },
339339 )
340340 offer = fields .Nested (CredOfferSchema (), required = True )
341341
342+ class CredOfferResponseSchemaRef (OpenAPISchema ):
343+ """Credential Offer Schema."""
342344
343- @docs (tags = ["oid4vci" ], summary = "Get a credential offer" )
344- @querystring_schema (CredOfferQuerySchema ())
345- @response_schema (CredOfferResponseSchema (), 200 )
346- @tenant_authentication
347- async def get_cred_offer (request : web .BaseRequest ):
348- """Endpoint to retrieve an OpenID4VCI compliant offer.
345+ credential_offer_uri = fields .Str (
346+ required = True ,
347+ metadata = {
348+ "description" : "A URL which references the credential for display." ,
349+ "example" : "openid-credential-offer://..." ,
350+ },
351+ )
352+ offer = fields .Nested (CredOfferSchema (), required = True )
349353
350- For example, can be used in QR-Code presented to a compliant wallet.
354+ async def _parse_cred_offer (context : AdminRequestContext , exchange_id : str ) -> dict :
355+ """Helper function for cred_offer request parsing.
356+
357+ Used in get_cred_offer and public_routes.dereference_cred_offer endpoints.
351358 """
352- context : AdminRequestContext = request ["context" ]
353359 config = Config .from_settings (context .settings )
354- exchange_id = request .query ["exchange_id" ]
355-
356360 code = secrets .token_urlsafe (CODE_BYTES )
357361
358362 try :
@@ -375,7 +379,7 @@ async def get_cred_offer(request: web.BaseRequest):
375379 else None
376380 )
377381 subpath = f"/tenant/{ wallet_id } " if wallet_id else ""
378- offer = {
382+ return {
379383 "credential_issuer" : f"{ config .endpoint } { subpath } " ,
380384 "credentials" : [supported .identifier ],
381385 "grants" : {
@@ -385,15 +389,57 @@ async def get_cred_offer(request: web.BaseRequest):
385389 }
386390 },
387391 }
392+
393+ @docs (tags = ["oid4vci" ], summary = "Get a credential offer by value" )
394+ @querystring_schema (CredOfferQuerySchema ())
395+ @response_schema (CredOfferResponseSchemaVal (), 200 )
396+ @tenant_authentication
397+ async def get_cred_offer (request : web .BaseRequest ):
398+ """Endpoint to retrieve an OpenID4VCI compliant offer by value.
399+
400+ For example, can be used in QR-Code presented to a compliant wallet.
401+ """
402+ context : AdminRequestContext = request ["context" ]
403+ exchange_id = request .query ["exchange_id" ]
404+
405+ offer = await _parse_cred_offer (context , exchange_id )
388406 offer_uri = quote (json .dumps (offer ))
389- full_uri = f"openid-credential-offer://?credential_offer={ offer_uri } "
390407 offer_response = {
391408 "offer" : offer ,
392- "offer_uri " : full_uri ,
409+ "credential_offer " : f"openid-credential-offer://?credential_offer= { offer_uri } "
393410 }
394-
395411 return web .json_response (offer_response )
396412
413+ @docs (tags = ["oid4vci" ], summary = "Get a credential offer by reference" )
414+ @querystring_schema (CredOfferQuerySchema ())
415+ @response_schema (CredOfferResponseSchemaRef (), 200 )
416+ @tenant_authentication
417+ async def get_cred_offer_by_ref (request : web .BaseRequest ):
418+ """Endpoint to retrieve an OpenID4VCI compliant offer by reference.
419+
420+ credential_offer_uri can be dereferenced at the /oid4vc/dereference-credential-offer
421+ (see public_routes.dereference_cred_offer)
422+
423+ For example, can be used in QR-Code presented to a compliant wallet.
424+ """
425+ context : AdminRequestContext = request ["context" ]
426+ exchange_id = request .query ["exchange_id" ]
427+ wallet_id = (
428+ context .profile .settings .get ("wallet.id" )
429+ if context .profile .settings .get ("multitenant.enabled" )
430+ else None
431+ )
432+
433+ offer = await _parse_cred_offer (context , exchange_id )
434+
435+ config = Config .from_settings (context .settings )
436+ subpath = f"/tenant/{ wallet_id } " if wallet_id else ""
437+ ref_uri = f"{ config .endpoint } { subpath } /oid4vci/dereference-credential-offer"
438+ offer_response = {
439+ "offer" : offer ,
440+ "credential_offer_uri" : f"openid-credential-offer://?credential_offer={ quote (ref_uri )} "
441+ }
442+ return web .json_response (offer_response )
397443
398444class SupportedCredCreateRequestSchema (OpenAPISchema ):
399445 """Schema for SupportedCredCreateRequestSchema."""
@@ -1401,6 +1447,11 @@ async def register(app: web.Application):
14011447 app .add_routes (
14021448 [
14031449 web .get ("/oid4vci/credential-offer" , get_cred_offer , allow_head = False ),
1450+ web .get (
1451+ "/oid4vci/credential-offer-by-ref" ,
1452+ get_cred_offer_by_ref ,
1453+ allow_head = False
1454+ ),
14041455 web .get (
14051456 "/oid4vci/exchange/records" ,
14061457 list_exchange_records ,
0 commit comments