@sphereon/oid4vci-issuer
Version:
OpenID 4 Verifiable Credential Issuance issuer REST endpoints
784 lines (741 loc) • 26.4 kB
text/typescript
import { uuidv4 } from '@sphereon/oid4vc-common'
import { OpenID4VCIClientV1_0_13 } from '@sphereon/oid4vci-client'
import {
Alg,
ALG_ERROR,
CredentialConfigurationSupportedV1_0_13,
CredentialOfferSession,
IssuerCredentialSubjectDisplay,
IssueStatus,
STATE_MISSING_ERROR,
} from '@sphereon/oid4vci-common'
import { IProofPurpose, IProofType } from '@sphereon/ssi-types'
import { VcIssuer } from '../VcIssuer'
import { CredentialSupportedBuilderV1_13, VcIssuerBuilder } from '../builder'
import { AuthorizationServerMetadataBuilder } from '../builder/AuthorizationServerMetadataBuilder'
import { MemoryStates } from '../state-manager'
const IDENTIPROOF_ISSUER_URL = 'https://issuer.research.identiproof.io'
const verifiableCredential = {
'@context': ['https://www.w3.org/2018/credentials/v1', 'https://w3id.org/security/suites/jws-2020/v1'],
id: 'http://university.example/credentials/1872',
type: ['VerifiableCredential', 'ExampleAlumniCredential'],
issuer: 'https://university.example/issuers/565049',
issuanceDate: new Date().toISOString(),
credentialSubject: {
id: 'did:example:ebfeb1f712ebc6f1c276e12ec21',
alumniOf: {
id: 'did:example:c276e12ec21ebfeb1f712ebc6f1',
name: 'Example University',
},
},
}
const verifiableCredential_withoutDid = {
'@context': ['https://www.w3.org/2018/credentials/v1', 'https://w3id.org/security/suites/jws-2020/v1'],
id: 'http://university.example/credentials/1872',
type: ['VerifiableCredential', 'ExampleAlumniCredential'],
issuer: 'https://university.example/issuers/565049',
issuanceDate: new Date().toISOString(),
credentialSubject: {
id: 'ebfeb1f712ebc6f1c276e12ec21',
alumniOf: {
id: 'c276e12ec21ebfeb1f712ebc6f1',
name: 'Example University',
},
},
}
const authorizationServerMetadata = new AuthorizationServerMetadataBuilder()
.withIssuer(IDENTIPROOF_ISSUER_URL)
.withCredentialEndpoint('http://localhost:3456/test/credential-endpoint')
.withTokenEndpoint('http://localhost:3456/test/token')
.withAuthorizationEndpoint('https://token-endpoint.example.com/authorize')
.withTokenEndpointAuthMethodsSupported(['none', 'client_secret_basic', 'client_secret_jwt', 'client_secret_post'])
.withResponseTypesSupported(['code', 'token', 'id_token'])
.withScopesSupported(['openid', 'abcdef'])
.build()
describe('VcIssuer', () => {
let vcIssuer: VcIssuer
const issuerState = 'previously-created-state'
const clientId = 'sphereon:wallet'
const preAuthorizedCode = 'test_code'
const jwtVerifyCallback: jest.Mock = jest.fn()
beforeEach(async () => {
jest.clearAllMocks()
const credentialsSupported: Record<string, CredentialConfigurationSupportedV1_0_13> = new CredentialSupportedBuilderV1_13()
.withCredentialSigningAlgValuesSupported('ES256K')
.withCryptographicBindingMethod('did')
.withFormat('jwt_vc_json')
.withCredentialName('UniversityDegree_JWT')
.withCredentialDefinition({
type: ['VerifiableCredential', 'UniversityDegree_JWT'],
})
.withCredentialSupportedDisplay({
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',
})
.addCredentialSubjectPropertyDisplay('given_name', {
name: 'given name',
locale: 'en-US',
} as IssuerCredentialSubjectDisplay)
.build()
const stateManager = new MemoryStates<CredentialOfferSession>()
await stateManager.set('previously-created-state', {
issuerState,
clientId,
preAuthorizedCode,
createdAt: +new Date(),
lastUpdatedAt: +new Date(),
status: IssueStatus.OFFER_CREATED,
notification_id: uuidv4(),
txCode: '123456',
credentialOffer: {
credential_offer: {
credential_issuer: 'did:key:test',
credentials: [
{
format: 'ldp_vc',
credential_definition: {
types: ['VerifiableCredential'],
'@context': ['https://www.w3.org/2018/credentials/v1'],
credentialSubject: {},
},
},
],
grants: {
authorization_code: { issuer_state: issuerState },
'urn:ietf:params:oauth:grant-type:pre-authorized_code': {
'pre-authorized_code': preAuthorizedCode,
tx_code: {
input_mode: 'text',
length: 4,
},
},
},
},
},
})
vcIssuer = new VcIssuerBuilder()
.withAuthorizationServers('https://authorization-server')
.withCredentialEndpoint('https://credential-endpoint')
.withCredentialIssuer(IDENTIPROOF_ISSUER_URL)
.withAuthorizationMetadata(authorizationServerMetadata)
.withIssuerDisplay({
name: 'example issuer',
locale: 'en-US',
})
.withCredentialConfigurationsSupported(credentialsSupported)
.withCredentialOfferStateManager(stateManager)
.withInMemoryCNonceState()
.withInMemoryCredentialOfferURIState()
.withCredentialSignerCallback(() =>
Promise.resolve({
'@context': ['https://www.w3.org/2018/credentials/v1'],
type: ['VerifiableCredential'],
issuer: 'did:key:test',
issuanceDate: new Date().toISOString(),
credentialSubject: {},
proof: {
type: IProofType.JwtProof2020,
jwt: 'ye.ye.ye',
created: new Date().toISOString(),
proofPurpose: IProofPurpose.assertionMethod,
verificationMethod: 'sdfsdfasdfasdfasdfasdfassdfasdf',
},
}),
)
.withJWTVerifyCallback(jwtVerifyCallback)
.build()
})
afterAll(async () => {
jest.clearAllMocks()
// await new Promise((resolve) => setTimeout((v: void) => resolve(v), 500))
})
it.skip('should create credential offer', async () => {
const { uri, ...rest } = await vcIssuer.createCredentialOfferURI({
offerMode: 'VALUE',
grants: {
authorization_code: {
issuer_state: issuerState,
},
'urn:ietf:params:oauth:grant-type:pre-authorized_code': {
'pre-authorized_code': preAuthorizedCode,
user_pin_required: true,
},
},
scheme: 'http',
baseUri: 'issuer-example.com',
qrCodeOpts: {
size: 400,
colorDark: '#000000',
colorLight: '#ffffff',
correctLevel: 2,
},
})
console.log(JSON.stringify(rest, null, 2))
expect(uri).toEqual(
'http://issuer-example.com?credential_offer=%7B%22grants%22%3A%7B%22authorization_code%22%3A%7B%22issuer_state%22%3A%22previously-created-state%22%7D%2C%22urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Apre-authorized_code%22%3A%7B%22pre-authorized_code%22%3A%22test_code%22%2C%22user_pin_required%22%3Atrue%7D%7D%2C%22credential_issuer%22%3A%22https%3A%2F%2Fissuer.research.identiproof.io%22%2C%22credentials%22%3A%5B%7B%22format%22%3A%22jwt_vc_json%22%2C%22types%22%3A%5B%22VerifiableCredential%22%5D%2C%22credentialSubject%22%3A%7B%22given_name%22%3A%7B%22name%22%3A%22given%20name%22%2C%22locale%22%3A%22en-US%22%7D%7D%2C%22cryptographic_suites_supported%22%3A%5B%22ES256K%22%5D%2C%22cryptographic_binding_methods_supported%22%3A%5B%22did%22%5D%2C%22id%22%3A%22UniversityDegree_JWT%22%2C%22display%22%3A%5B%7B%22name%22%3A%22University%20Credential%22%2C%22locale%22%3A%22en-US%22%2C%22logo%22%3A%7B%22url%22%3A%22https%3A%2F%2Fexampleuniversity.com%2Fpublic%2Flogo.png%22%2C%22alt_text%22%3A%22a%20square%20logo%20of%20a%20university%22%7D%2C%22background_color%22%3A%22%2312107c%22%2C%22text_color%22%3A%22%23FFFFFF%22%7D%5D%7D%5D%7D',
)
const client = await OpenID4VCIClientV1_0_13.fromURI({ uri })
expect(client.credentialOffer).toEqual({
baseUrl: 'http://issuer-example.com',
credential_offer: {
credential_issuer: 'https://issuer.research.identiproof.io',
credentials: [
{
credentialSubject: {
given_name: {
locale: 'en-US',
name: 'given name',
},
},
cryptographic_binding_methods_supported: ['did'],
cryptographic_suites_supported: ['ES256K'],
display: [
{
background_color: '#12107c',
locale: 'en-US',
logo: {
alt_text: 'a square logo of a university',
url: 'https://exampleuniversity.com/public/logo.png',
},
name: 'University Credential',
text_color: '#FFFFFF',
},
],
format: 'jwt_vc_json',
id: 'UniversityDegree_JWT',
types: ['VerifiableCredential'],
},
],
grants: {
authorization_code: {
issuer_state: 'previously-created-state',
},
'urn:ietf:params:oauth:grant-type:pre-authorized_code': {
'pre-authorized_code': 'test_code',
user_pin_required: true,
},
},
},
issuerState: 'previously-created-state',
original_credential_offer: {
credential_issuer: 'https://issuer.research.identiproof.io',
credentials: [
{
credentialSubject: {
given_name: {
locale: 'en-US',
name: 'given name',
},
},
cryptographic_binding_methods_supported: ['did'],
cryptographic_suites_supported: ['ES256K'],
display: [
{
background_color: '#12107c',
locale: 'en-US',
logo: {
alt_text: 'a square logo of a university',
url: 'https://exampleuniversity.com/public/logo.png',
},
name: 'University Credential',
text_color: '#FFFFFF',
},
],
format: 'jwt_vc_json',
id: 'UniversityDegree_JWT',
types: ['VerifiableCredential'],
},
],
grants: {
authorization_code: {
issuer_state: 'previously-created-state',
},
'urn:ietf:params:oauth:grant-type:pre-authorized_code': {
'pre-authorized_code': 'test_code',
user_pin_required: true,
},
},
},
preAuthorizedCode: 'test_code',
scheme: 'http',
supportedFlows: ['Authorization Code Flow', 'Pre-Authorized Code Flow'],
userPinRequired: true,
version: 1011,
})
})
it('should create credential offer uri', async () => {
await expect(
vcIssuer
.createCredentialOfferURI({
offerMode: 'REFERENCE',
credentialOfferUri: 'http://issuer-example.com/:id',
grants: {
authorization_code: {
issuer_state: issuerState,
},
},
scheme: 'http',
baseUri: 'issuer-example.com',
credential_configuration_ids: ['VerifiableCredential'],
})
.then((response) => response.uri),
).resolves.toContain('http://issuer-example.com?credential_offer_uri=http%3A%2F%2Fissuer-example.com%2F')
})
// Of course this doesn't work. The state is part of the proof to begin with
it('should fail issuing credential if an invalid state is used', async () => {
jwtVerifyCallback.mockResolvedValue({
did: 'did:example:1234',
kid: 'did:example:1234#auth',
alg: Alg.ES256K,
didDocument: {
'@context': 'https://www.w3.org/ns/did/v1',
id: 'did:example:1234',
},
jwt: {
header: {
typ: 'openid4vci-proof+jwt',
alg: Alg.ES256K,
kid: 'test-kid',
},
payload: {
aud: IDENTIPROOF_ISSUER_URL,
iat: +new Date() / 1000,
nonce: 'test-nonce',
},
},
})
await expect(
vcIssuer.issueCredential({
credentialRequest: {
credential_identifier: 'VerifiableCredential',
proof: {
proof_type: 'jwt',
jwt: 'ye.ye.ye',
},
},
// issuerState: 'invalid state',
}),
).rejects.toThrow(Error(STATE_MISSING_ERROR + ' (test-nonce)'))
})
it.each([...Object.values<string>(Alg), 'CUSTOM'])('should issue %s signed credential if a valid state is passed in', async (alg: string) => {
jwtVerifyCallback.mockResolvedValue({
did: 'did:example:1234',
kid: 'did:example:1234#auth',
alg: alg,
didDocument: {
'@context': 'https://www.w3.org/ns/did/v1',
id: 'did:example:1234',
},
jwt: {
header: {
typ: 'openid4vci-proof+jwt',
alg: alg,
kid: 'test-kid',
},
payload: {
aud: IDENTIPROOF_ISSUER_URL,
iat: +new Date() / 1000,
nonce: 'test-nonce',
},
},
})
const createdAt = +new Date()
await vcIssuer.cNonces.set('test-nonce', {
cNonce: 'test-nonce',
preAuthorizedCode: 'test-pre-authorized-code',
createdAt: createdAt,
})
await vcIssuer.credentialOfferSessions.set('test-pre-authorized-code', {
createdAt: createdAt,
notification_id: '43243',
preAuthorizedCode: 'test-pre-authorized-code',
credentialOffer: {
credential_offer: {
credential_issuer: 'did:key:test',
credentials: [],
},
},
lastUpdatedAt: createdAt,
status: IssueStatus.ACCESS_TOKEN_CREATED,
})
expect(
vcIssuer.issueCredential({
credential: verifiableCredential,
credentialRequest: {
credential_identifier: 'VerifiableCredential',
proof: {
proof_type: 'jwt',
jwt: 'ye.ye.ye',
},
},
newCNonce: 'new-test-nonce',
}),
).resolves.toEqual({
c_nonce: 'new-test-nonce',
c_nonce_expires_in: 300,
notification_id: '43243',
credential: {
'@context': ['https://www.w3.org/2018/credentials/v1'],
credentialSubject: {},
issuanceDate: expect.any(String),
issuer: 'did:key:test',
proof: {
created: expect.any(String),
jwt: 'ye.ye.ye',
proofPurpose: 'assertionMethod',
type: 'JwtProof2020',
verificationMethod: 'sdfsdfasdfasdfasdfasdfassdfasdf',
},
type: ['VerifiableCredential'],
},
// format: 'jwt_vc_json',
})
})
it('should fail issuing credential if the signing algorithm is missing', async () => {
const createdAt = +new Date()
await vcIssuer.cNonces.set('test-nonce', {
cNonce: 'test-nonce',
preAuthorizedCode: 'test-pre-authorized-code',
createdAt: createdAt,
})
jwtVerifyCallback.mockResolvedValue({
did: 'did:example:1234',
kid: 'did:example:1234#auth',
alg: undefined,
didDocument: {
'@context': 'https://www.w3.org/ns/did/v1',
id: 'did:example:1234',
},
jwt: {
header: {
typ: 'openid4vci-proof+jwt',
alg: undefined,
kid: 'test-kid',
},
payload: {
aud: IDENTIPROOF_ISSUER_URL,
iat: +new Date() / 1000,
nonce: 'test-nonce',
},
},
})
expect(
vcIssuer.issueCredential({
credentialRequest: {
credential_identifier: 'VerifiableCredential',
proof: {
proof_type: 'jwt',
jwt: 'ye.ye.ye',
},
},
}),
).rejects.toThrow(Error(ALG_ERROR))
})
})
describe('VcIssuer without did', () => {
let vcIssuer: VcIssuer
const issuerState = 'previously-created-state'
const clientId = 'sphereon:wallet'
const preAuthorizedCode = 'test_code'
const jwtVerifyCallback: jest.Mock = jest.fn()
beforeEach(async () => {
jest.clearAllMocks()
const credentialsSupported: Record<string, CredentialConfigurationSupportedV1_0_13> = new CredentialSupportedBuilderV1_13()
.withCredentialSigningAlgValuesSupported('ES256K')
.withCryptographicBindingMethod('jwk')
.withFormat('jwt_vc_json')
.withCredentialName('UniversityDegree_JWT')
.withCredentialDefinition({
type: ['VerifiableCredential', 'UniversityDegree_JWT'],
})
.withCredentialSupportedDisplay({
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',
})
.addCredentialSubjectPropertyDisplay('given_name', {
name: 'given name',
locale: 'en-US',
} as IssuerCredentialSubjectDisplay)
.build()
const stateManager = new MemoryStates<CredentialOfferSession>()
await stateManager.set('previously-created-state', {
issuerState,
clientId,
preAuthorizedCode,
createdAt: +new Date(),
lastUpdatedAt: +new Date(),
status: IssueStatus.OFFER_CREATED,
notification_id: uuidv4(),
txCode: '123456',
credentialOffer: {
credential_offer: {
credential_issuer: 'test.com',
credentials: [
{
format: 'ldp_vc',
credential_definition: {
types: ['VerifiableCredential'],
'@context': ['https://www.w3.org/2018/credentials/v1'],
credentialSubject: {},
},
},
],
grants: {
authorization_code: { issuer_state: issuerState },
'urn:ietf:params:oauth:grant-type:pre-authorized_code': {
'pre-authorized_code': preAuthorizedCode,
tx_code: {
input_mode: 'text',
length: 4,
},
},
},
},
},
})
vcIssuer = new VcIssuerBuilder()
.withAuthorizationServers('https://authorization-server')
.withCredentialEndpoint('https://credential-endpoint')
.withCredentialIssuer(IDENTIPROOF_ISSUER_URL)
.withAuthorizationMetadata(authorizationServerMetadata)
.withIssuerDisplay({
name: 'example issuer',
locale: 'en-US',
})
.withCredentialConfigurationsSupported(credentialsSupported)
.withCredentialOfferStateManager(stateManager)
.withInMemoryCNonceState()
.withInMemoryCredentialOfferURIState()
.withCredentialSignerCallback(() =>
Promise.resolve({
'@context': ['https://www.w3.org/2018/credentials/v1'],
type: ['VerifiableCredential'],
issuer: 'test.com',
issuanceDate: new Date().toISOString(),
credentialSubject: {},
proof: {
type: IProofType.JwtProof2020,
jwt: 'ye.ye.ye',
created: new Date().toISOString(),
proofPurpose: IProofPurpose.assertionMethod,
verificationMethod: 'sdfsdfasdfasdfasdfasdfassdfasdf',
},
}),
)
.withJWTVerifyCallback(jwtVerifyCallback)
.build()
})
afterAll(async () => {
jest.clearAllMocks()
// await new Promise((resolve) => setTimeout((v: void) => resolve(v), 500))
})
// Of course this doesn't work. The state is part of the proof to begin with
it('should fail issuing credential if an invalid state is used', async () => {
jwtVerifyCallback.mockResolvedValue({
alg: Alg.ES256K,
jwt: {
header: {
typ: 'openid4vci-proof+jwt',
alg: Alg.ES256K,
x5c: ['12', '34', '56'],
},
payload: {
aud: IDENTIPROOF_ISSUER_URL,
iat: +new Date() / 1000,
nonce: 'test-nonce',
},
},
})
await expect(
vcIssuer.issueCredential({
credentialRequest: {
credential_identifier: 'VerifiableCredential',
proof: {
proof_type: 'jwt',
jwt: 'ye.ye.ye',
},
},
// issuerState: 'invalid state',
}),
).rejects.toThrow(Error(STATE_MISSING_ERROR + ' (test-nonce)'))
})
it.each([...Object.values<string>(Alg), 'CUSTOM'])('should issue %s signed credential if a valid state is passed in', async (alg: string) => {
jwtVerifyCallback.mockResolvedValue({
alg: alg,
jwt: {
header: {
typ: 'openid4vci-proof+jwt',
alg: alg,
x5c: ['12', '34', '56'],
},
payload: {
aud: IDENTIPROOF_ISSUER_URL,
iat: +new Date() / 1000,
nonce: 'test-nonce',
},
},
})
const createdAt = +new Date()
await vcIssuer.cNonces.set('test-nonce', {
cNonce: 'test-nonce',
preAuthorizedCode: 'test-pre-authorized-code',
createdAt: createdAt,
})
await vcIssuer.credentialOfferSessions.set('test-pre-authorized-code', {
createdAt: createdAt,
notification_id: '43243',
preAuthorizedCode: 'test-pre-authorized-code',
credentialOffer: {
credential_offer: {
credential_issuer: 'test.com',
credentials: [],
},
},
lastUpdatedAt: createdAt,
status: IssueStatus.ACCESS_TOKEN_CREATED,
})
expect(
vcIssuer.issueCredential({
credential: verifiableCredential_withoutDid,
credentialRequest: {
credential_identifier: 'VerifiableCredential',
proof: {
proof_type: 'jwt',
jwt: 'ye.ye.ye',
},
},
newCNonce: 'new-test-nonce',
}),
).resolves.toEqual({
c_nonce: 'new-test-nonce',
c_nonce_expires_in: 300,
notification_id: '43243',
credential: {
'@context': ['https://www.w3.org/2018/credentials/v1'],
credentialSubject: {},
issuanceDate: expect.any(String),
issuer: 'test.com',
proof: {
created: expect.any(String),
jwt: 'ye.ye.ye',
proofPurpose: 'assertionMethod',
type: 'JwtProof2020',
verificationMethod: 'sdfsdfasdfasdfasdfasdfassdfasdf',
},
type: ['VerifiableCredential'],
},
// format: 'jwt_vc_json',
})
})
it('should fail issuing credential if the signing algorithm is missing', async () => {
const createdAt = +new Date()
await vcIssuer.cNonces.set('test-nonce', {
cNonce: 'test-nonce',
preAuthorizedCode: 'test-pre-authorized-code',
createdAt: createdAt,
})
jwtVerifyCallback.mockResolvedValue({
alg: undefined,
jwt: {
header: {
typ: 'openid4vci-proof+jwt',
alg: undefined,
x5c: ['12', '34', '56'],
},
payload: {
aud: IDENTIPROOF_ISSUER_URL,
iat: +new Date() / 1000,
nonce: 'test-nonce',
},
},
})
expect(
vcIssuer.issueCredential({
credentialRequest: {
credential_identifier: 'VerifiableCredential',
proof: {
proof_type: 'jwt',
jwt: 'ye.ye.ye',
},
},
}),
).rejects.toThrow(Error(ALG_ERROR))
})
it('should create credential offer uri with REFERENCE mode', async () => {
const result = await vcIssuer.createCredentialOfferURI({
offerMode: 'REFERENCE',
credentialOfferUri: 'https://example.com/api/credentials/:id',
grants: {
authorization_code: {
issuer_state: issuerState,
},
},
scheme: 'http',
baseUri: 'issuer-example.com',
})
expect(result.uri).toMatch(/http:\/\/issuer-example\.com\?credential_offer_uri=https%3A%2F%2Fexample\.com%2Fapi%2Fcredentials%2F[\w-]+/)
expect(result.session).toBeDefined()
expect(result.session.credentialOffer.credential_offer_uri).toMatch(/https:\/\/example\.com\/api\/credentials\/[\w-]+/)
})
it('should throw error if credential offer Uri is missing with REFERENCE mode', async () => {
await expect(
vcIssuer.createCredentialOfferURI({
offerMode: 'REFERENCE',
grants: {
authorization_code: {
issuer_state: issuerState,
},
},
}),
).rejects.toThrow('credentialOfferUri must be supplied for offerMode REFERENCE!')
})
it('should get credential offer session by uri', async () => {
const result = await vcIssuer.createCredentialOfferURI({
offerMode: 'REFERENCE',
credentialOfferUri: 'https://example.com/api/credentials/:id',
grants: {
authorization_code: {
issuer_state: issuerState,
},
'urn:ietf:params:oauth:grant-type:pre-authorized_code': {
'pre-authorized_code': 'preAuthCode',
},
},
})
const session = await vcIssuer.getCredentialOfferSessionById(result.session.preAuthorizedCode!, ['uri'])
expect(session).toBeDefined()
expect(session.credentialOffer).toEqual(result.session.credentialOffer)
})
it('should throw error when getting session with invalid uri', async () => {
await expect(vcIssuer.getCredentialOfferSessionById('https://example.com/invalid-uri')).rejects.toThrow(
'no value found for id https://example.com/invalid-uri',
)
})
it('should throw error when getting session by uri without uri state manager', async () => {
// Create issuer without URI state manager
const vcIssuerWithoutUriState = new VcIssuerBuilder()
.withAuthorizationServers('https://authorization-server')
.withCredentialEndpoint('https://credential-endpoint')
.withCredentialIssuer(IDENTIPROOF_ISSUER_URL)
.withAuthorizationMetadata(authorizationServerMetadata)
.withCredentialConfigurationsSupported({})
.withCredentialOfferStateManager(new MemoryStates<CredentialOfferSession>())
.withInMemoryCNonceState()
.build()
await expect(vcIssuerWithoutUriState.getCredentialOfferSessionById('https://example.com/some-uri')).rejects.toThrow(
'no value found for id https://example.com/some-uri',
)
})
})