[{"data":1,"prerenderedAt":652},["ShallowReactive",2],{"/blog/product-update-34":3},{"id":4,"title":5,"archived":6,"author":7,"body":8,"category":640,"coverImage":7,"description":641,"extension":642,"meta":643,"navigation":644,"ogImage":645,"path":646,"publishedAt":647,"seo":648,"stem":649,"tags":650,"__hash__":651},"blog/blog/product-update-34.md","Product Update #34",false,"",{"type":9,"value":10,"toc":624},"minimark",[11,19,35,40,58,62,67,72,84,91,96,99,105,110,129,135,140,147,153,157,359,362,366,378,381,384,388,397,403,407,410,416,420,434,440,444,457,463,468,483,489,494,508,514,517,570,572,577,586,593,595,601,608,614,616],[12,13,15],"h1",{"id":14},"tldr",[16,17,18],"strong",{},"TL;DR",[20,21,22,29],"ul",{},[23,24,25,28],"li",{},[16,26,27],{},"New release"," - Key Attestation, Wallet2 Batch Issuance, Mandatory PAR, and more",[23,30,31,34],{},[16,32,33],{},"walt.id Verifier x Google Wallet"," – Demo showcasing the request of a digital credential from the Google Wallet",[36,37,39],"h2",{"id":38},"community-stack-120","Community Stack (1.2.0)",[41,42,43,44,51,52,57],"p",{},"Below are the highlights available through 1.2.0 of the identity lib. Check out the full change log for 1.2.0 ",[45,46,50],"a",{"href":47,"rel":48},"https://github.com/walt-id/waltid-identity/releases/tag/v1.2.0",[49],"nofollow","here",". Want to learn more about the identity lib in general? Check out our ",[45,53,56],{"href":54,"rel":55},"https://youtu.be/5T7M63apfdg",[49],"intro video",".",[36,59,61],{"id":60},"_120","1.2.0",[63,64,66],"h3",{"id":65},"features","Features",[41,68,69],{},[16,70,71],{},"Require Key Attestations in Issuer2",[41,73,74,75,79,80,83],{},"Issuer2 can now verify a key attestation carried in a JWT proof or sent as a standalone attestation proof. Set the trusted attester once in ",[76,77,78],"code",{},"keyAttestationConfig"," with a public JWK or trusted X.509 CA certificates, and use ",[76,81,82],{},"key_attestations_required"," to make an attestation mandatory for a credential configuration. OAuth client attestation stays a separate configuration.",[41,85,86],{},[45,87,90],{"href":88,"rel":89},"https://docs.walt.id/community-stack/issuer2/configurations/config-files/credential-issuer-metadata#binding-methods-and-proof-types",[49],"Learn more",[41,92,93],{},[16,94,95],{},"Batch Issuance in Wallet2",[41,97,98],{},"Choose which offered credentials to receive and request several copies of one, each bound to its own holder key. Copies of one credential go out in a single Credential Request with several JWT proofs, while different credentials stay separate requests. Without a selection, Wallet2 still receives every granted credential once with a single holder binding.",[41,100,101],{},[45,102,90],{"href":103,"rel":104},"https://docs.walt.id/community-stack/wallet2/credential-receiving/batch-issuance#select-targets-and-copies",[49],[41,106,107],{},[16,108,109],{},"Enforce Pushed Authorization Requests (PAR) in Issuer2",[41,111,112,113,116,117,120,121,124,125,128],{},"Set ",[76,114,115],{},"enforcePushedAuthorizationRequests"," to ",[76,118,119],{},"true"," in ",[76,122,123],{},"issuer-service.conf"," and wallets must post the authorization parameters to the PAR endpoint before opening the authorization endpoint. Direct authorization requests are rejected. The default is ",[76,126,127],{},"false",", so existing issuers keep allowing both.",[41,130,131],{},[45,132,90],{"href":133,"rel":134},"https://docs.walt.id/community-stack/issuer2/configurations/config-files/issuer-service#enforcepushedauthorizationrequests",[49],[41,136,137],{},[16,138,139],{},"Choose the Signing Key for Signed Metadata in Issuer2",[41,141,142,143,146],{},"Pick the key and certificate chain Issuer2 uses to sign its Credential Issuer metadata. The same ",[76,144,145],{},"signedMetadata"," configuration now also signs OAuth authorization server metadata. Leave it out and Issuer2 keeps signing Credential Issuer metadata with its token key.",[41,148,149],{},[45,150,90],{"href":151,"rel":152},"https://docs.walt.id/community-stack/issuer2/configurations/config-files/issuer-service#signedmetadata",[49],[63,154,156],{"id":155},"fixes-and-improvements","Fixes and Improvements",[20,158,159,167,179,191,214,234,242,254,266,274,282,290,304,312,320,328,336,351],{},[23,160,161,162],{},"Verifier2 accepts an IETF Token Status List signed by the same x5c leaf certificate as the credential, in addition to matching DID and https issuers. ",[45,163,166],{"href":164,"rel":165},"https://github.com/walt-id/waltid-identity/pull/2272",[49],"#2272",[23,168,169,170,173,174],{},"Verifier2 reuses a fetched status list while its ",[76,171,172],{},"Cache-Control: max-age"," is current. ",[45,175,178],{"href":176,"rel":177},"https://github.com/walt-id/waltid-identity/pull/2327",[49],"#2327",[23,180,181,182,185,186],{},"W3C credentials whose ",[76,183,184],{},"@context"," mixes URLs with inline JSON-LD objects, as Gaia-X credentials do, no longer fail on import or VC Data Model 2.0 issuance. ",[45,187,190],{"href":188,"rel":189},"https://github.com/walt-id/waltid-identity/pull/2344",[49],"#2344",[23,192,193,194,197,198,200,201,204,205,208,209],{},"DCQL ",[76,195,196],{},"type_values"," now match W3C types after JSON-LD ",[76,199,184],{}," expansion, so a query for an expanded IRI matches a compact type such as ",[76,202,203],{},"gx:LegalPerson",". Compact values such as ",[76,206,207],{},"VerifiableCredential"," still match. ",[45,210,213],{"href":211,"rel":212},"https://github.com/walt-id/waltid-identity/pull/2345",[49],"#2345",[23,215,216,217,220,221,224,225,228,229],{},"Wallet2 accepts ",[76,218,219],{},"jwt_vc_json-ld"," and ",[76,222,223],{},"jwt_vc"," credentials again. JWT-secured W3C credentials stay stored as ",[76,226,227],{},"jwt_vc_json",". ",[45,230,233],{"href":231,"rel":232},"https://github.com/walt-id/waltid-identity/pull/2378",[49],"#2378",[23,235,236,237],{},"A presentation is signed with the holder key stored on that credential. Sending a different key fails before the verifier sees the proof, while one OpenID4VP request can still ask for several credentials bound to different keys. ",[45,238,241],{"href":239,"rel":240},"https://github.com/walt-id/waltid-identity/pull/2336",[49],"#2336",[23,243,244,245,248,249],{},"Issuer2 credential responses are marked ",[76,246,247],{},"Cache-Control: no-store",", and iOS wallets bypass Foundation’s HTTP cache for credential responses. ",[45,250,253],{"href":251,"rel":252},"https://github.com/walt-id/waltid-identity/pull/2299",[49],"#2299",[23,255,256,257,260,261],{},"Authorization and credential request timestamps are always encoded, so ",[76,258,259],{},"requestedAt"," is no longer dropped by a same-tick clock comparison. ",[45,262,265],{"href":263,"rel":264},"https://github.com/walt-id/waltid-identity/pull/2371",[49],"#2371",[23,267,268,269],{},"Issuer2 Swagger examples for transaction-code and runtime-override offers match the JSON the API accepts. ",[45,270,273],{"href":271,"rel":272},"https://github.com/walt-id/waltid-identity/pull/2334",[49],"#2334",[23,275,276,277],{},"X.509 certificate and PKCS#10 CSR signatures are interpreted with the algorithm the object declares. ",[45,278,281],{"href":279,"rel":280},"https://github.com/walt-id/waltid-identity/pull/2322",[49],"#2322",[23,283,284,285],{},"A trust store can hold several CAs with the same subject DN and different keys. Path validation selects the CA by key, and certificates are no longer stored only by serial number. ",[45,286,289],{"href":287,"rel":288},"https://github.com/walt-id/waltid-identity/pull/2349",[49],"#2349",[23,291,292,295,296,228,299],{},[76,293,294],{},"ktor-authnz"," uses one Valkey connection for session and token stores. Failed LDAP and Web3 logins return 401 and failed logins are rate limited. Opaque tokens end with their session, a used TOTP code is refused inside its validity window, and new OIDC accounts get a random account id instead of ",[76,297,298],{},"sub",[45,300,303],{"href":301,"rel":302},"https://github.com/walt-id/waltid-identity/pull/2310",[49],"#2310",[23,305,306,307],{},"Reopening a mobile wallet initializes DID resolvers again, so a signed DID presentation request works after a process restart. ",[45,308,311],{"href":309,"rel":310},"https://github.com/walt-id/waltid-identity/pull/2314",[49],"#2314",[23,313,314,315],{},"When a presentation error cannot be reported safely, the original OpenID4VP error is preserved instead of being replaced by the reporting failure. ",[45,316,319],{"href":317,"rel":318},"https://github.com/walt-id/waltid-identity/pull/2317",[49],"#2317",[23,321,322,323],{},"TestFlight builds of the native and Compose wallet demos are no longer killed on suspend for holding a SQLCipher shared-memory lock. ",[45,324,327],{"href":325,"rel":326},"https://github.com/walt-id/waltid-identity/pull/2346",[49],"#2346",[23,329,330,331],{},"A superseded Android biometric signing check is cancelled off the main thread, so activity recreation no longer closes an in-flight HTTP call. ",[45,332,335],{"href":333,"rel":334},"https://github.com/walt-id/waltid-identity/pull/2341",[49],"#2341",[23,337,338,339,220,342,345,346],{},"The Compose web wallet can target the Enterprise Wallet API with ",[76,340,341],{},"WALLET_API_FLAVOR",[76,343,344],{},"WALLET_API2_PUBLIC_URL",". The open-source API remains the default. ",[45,347,350],{"href":348,"rel":349},"https://github.com/walt-id/waltid-identity/pull/2335",[49],"#2335",[23,352,353,354],{},"The Compose and native iOS demo wallets can attach a test key attestation for the known ITB and EUDI test issuers. This is demo-only. ",[45,355,358],{"href":356,"rel":357},"https://github.com/walt-id/waltid-identity/pull/2351",[49],"#2351",[360,361],"hr",{},[36,363,365],{"id":364},"enterprise-stack-120","Enterprise Stack (1.2.0)",[41,367,368,369,373,374,57],{},"Below are the new feature highlights available through 1.2.0 of the Enterprise Stack. Check out the full change log for 1.2.0 ",[45,370,50],{"href":371,"rel":372},"https://docs.walt.id/enterprise-stack/release-notes/releases/1.2.x",[49],". Want to learn more about the enterprise stack in general? Check out our ",[45,375,56],{"href":376,"rel":377},"https://youtu.be/FKzoD9F23VE",[49],[36,379,61],{"id":380},"_120-1",[63,382,66],{"id":383},"features-1",[41,385,386],{},[16,387,71],{},[41,389,390,391,393,394,396],{},"Issuer2 can now verify a key attestation carried in a JWT proof or sent as a standalone attestation proof, and a credential configuration can require one with ",[76,392,82],{},". Set the trusted attester in ",[76,395,78],{}," as a public JWK, trusted X.509 root certificates, or a KMS verification key.",[41,398,399],{},[45,400,90],{"href":401,"rel":402},"https://docs.walt.id/enterprise-stack/services/issuer2-service/configurations/credential-types#binding-methods-and-proof-types",[49],[41,404,405],{},[16,406,95],{},[41,408,409],{},"One receive call can select offered credential configurations and request several copies, each bound to its own holder key. Copies of one credential go out as one Credential Request with several JWT proofs, while different configurations stay separate requests. A single holder binding remains the default.",[41,411,412],{},[45,413,90],{"href":414,"rel":415},"https://docs.walt.id/enterprise-stack/services/wallet2-service/credential-receiving/batch-issuance#select-targets-and-copies",[49],[41,417,418],{},[16,419,109],{},[41,421,112,422,116,425,427,428,431,432,128],{},[76,423,424],{},"requirePushedAuthorizationRequests",[76,426,119],{}," when you create or update an Issuer2 service and wallets must post the authorization parameters to the pushed authorization endpoint first. Direct authorization requests are rejected with ",[76,429,430],{},"invalid_request",". The default is ",[76,433,127],{},[41,435,436],{},[45,437,90],{"href":438,"rel":439},"https://docs.walt.id/enterprise-stack/services/issuer2-service/setup#create-an-issuer2-service",[49],[41,441,442],{},[16,443,139],{},[41,445,446,447,449,450,453,454,57],{},"Choose the key that signs Credential Issuer metadata with ",[76,448,145],{},": an inline JWK, an inline PEM key with a certificate chain, or a KMS key, optionally with ",[76,451,452],{},"x5c"," certificates from the X.509 store. The same configuration now also signs OAuth authorization server metadata. Leave it out and Issuer2 keeps signing Credential Issuer metadata with ",[76,455,456],{},"tokenKeyId",[41,458,459],{},[45,460,90],{"href":461,"rel":462},"https://docs.walt.id/enterprise-stack/services/issuer2-service/configurations/signed-metadata#configure-metadata-signing",[49],[41,464,465],{},[16,466,467],{},"Maintenance Mode for Organizations and Tenants",[41,469,470,471,474,475,478,479,482],{},"Make an organization, or a single tenant, read-only for a time-boxed window while reads keep working. A window lasts 1 day by default and at most 30 days, and you can end it early. It is off unless the ",[76,472,473],{},"maintenance"," feature is enabled, and accounts holding ",[76,476,477],{},"organization-management-maintenance-override",", which the ",[76,480,481],{},"all"," permission includes, can keep writing.",[41,484,485],{},[45,486,90],{"href":487,"rel":488},"https://docs.walt.id/enterprise-stack/administration/maintenance-mode",[49],[41,490,491],{},[16,492,493],{},"Status List Lifetime and Rollover in the Credential Status Service",[41,495,112,496,499,500,503,504,507],{},[76,497,498],{},"ttl"," in seconds on an IETF Token Status List configuration. The published token uses it, objects in S3, Azure Blob Storage and Google Cloud Storage carry a matching ",[76,501,502],{},"Cache-Control: public, max-age",", and Verifier2 reuses a downloaded list until it elapses. Creating a status entry now returns ",[76,505,506],{},"statusListRef",", the list that holds the new index, and after automatic rollover later index updates must use that list.",[41,509,510],{},[45,511,90],{"href":512,"rel":513},"https://docs.walt.id/enterprise-stack/services/credential-status-service/manage#create-a-status-credential-configuration",[49],[63,515,156],{"id":516},"fixes-and-improvements-1",[20,518,519,522,525,528,531,537,542,545,548,555,558,561,564,567],{},[23,520,521],{},"Wallet2 pre-authorized receive on DocumentDB no longer fails with a storage error after the issuer has already returned the credential.",[23,523,524],{},"Concurrent receives into one Wallet2 wallet retry a contended credential write instead of failing an issuance that already succeeded.",[23,526,527],{},"A presentation is signed with the holder key the credential was issued to. A different key is rejected before it is sent.",[23,529,530],{},"Revoking or reading a status index after automatic rollover now works on the list that holds that index, not the base list that allocated earlier entries.",[23,532,533,534,536],{},"Verifier2 accepts an external IETF status list whose ",[76,535,452],{}," leaf matches the credential. Lists hosted by this deployment stay allowed as well.",[23,538,539,540,57],{},"Issuer2 credential responses, including failures before the response writer, send ",[76,541,247],{},[23,543,544],{},"Deleting an organization also removes its host aliases, role entries on accounts, events and Issuer2 OAuth state. The accounts remain and the eIDAS audit chain is kept.",[23,546,547],{},"Assigning a role to a user or API key requires every permission that role grants. Removing a deny rule requires the permission it withheld, while removing a grant does not.",[23,549,550,551,554],{},"Wallet2 receive defaults ",[76,552,553],{},"clientId"," to the wallet client id the wallet library uses, so an authorization-code exchange no longer fails with a client mismatch.",[23,556,557],{},"Issuer2 OpenAPI examples match the request bodies the API accepts.",[23,559,560],{},"Listing tenant accounts checks that the tenant path is inside the authenticated organization.",[23,562,563],{},"The admin organization list honors the advertised pagination parameters and rejects invalid ones.",[23,565,566],{},"Organization and tenant configuration updates validate organization ids and DID registry references and return the documented errors.",[23,568,569],{},"A migration scheduling bug found while documenting PostgreSQL is fixed, and database profile fields that the API never read are marked unused. Profile behavior is otherwise unchanged.",[360,571],{},[36,573,575],{"id":574},"waltid-verifier-x-google-wallet",[16,576,33],{},[41,578,579,580,585],{},"This demo shows interoperability between the ",[45,581,584],{"href":582,"rel":583},"https://walt.id",[49],"walt.id"," Verifier API and Google Wallet.",[41,587,588],{},[45,589,592],{"href":590,"rel":591},"https://www.youtube.com/watch?v=48qc71JA12w",[49],"Watch the video",[360,594],{},[36,596,598],{"id":597},"digital-id-end-to-end-tutorial",[16,599,600],{},"Digital ID End-to-End Tutorial",[41,602,603,604,607],{},"A practical, end-to-end tutorial with the ",[45,605,584],{"href":582,"rel":606},[49]," Community Stack, showing how Issuer, Wallet and Verifier work together to issue, hold and verify digital credentials. You will start the stack with Docker Compose, issue a W3C Open Badge Credential via OID4VCI, and request and verify it with OID4VP and verification policies.",[41,609,610],{},[45,611,592],{"href":612,"rel":613},"https://www.youtube.com/watch?v=lWSF_S-ID9Y",[49],[360,615],{},[41,617,618,619],{},"PS: If you enjoy working with our tools, make sure to leave us a ⭐ ",[45,620,623],{"href":621,"rel":622},"https://github.com/walt-id/waltid-identity",[49],"on GitHub",{"title":7,"searchDepth":625,"depth":625,"links":626},2,[627,628,633,634,638,639],{"id":38,"depth":625,"text":39},{"id":60,"depth":625,"text":61,"children":629},[630,632],{"id":65,"depth":631,"text":66},3,{"id":155,"depth":631,"text":156},{"id":364,"depth":625,"text":365},{"id":380,"depth":625,"text":61,"children":635},[636,637],{"id":383,"depth":631,"text":66},{"id":516,"depth":631,"text":156},{"id":574,"depth":625,"text":33},{"id":597,"depth":625,"text":600},"Product Updates","Key Attestation, Wallet2 Batch Issuance, Mandatory PAR, and more","md",{},true,null,"/blog/product-update-34","2026-10-20",{"title":5,"description":641},"blog/product-update-34",[],"xB0ZYOr6Q3yy0LIwIPHEM4VK7szZlN_SaV6J9uSwKtA",1792487721906]