Skip to content

Commit

Permalink
feat: Add expiration to offers
Browse files Browse the repository at this point in the history
  • Loading branch information
nklomp committed Feb 15, 2025
1 parent 1020d26 commit bbd8d7e
Show file tree
Hide file tree
Showing 5 changed files with 21 additions and 6 deletions.
1 change: 1 addition & 0 deletions packages/issuer-rest/lib/oid4vci-api-functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export function getIssueStatusEndpoint(router: Router, issuer: VcIssuer, opts: I
const authStatusBody: IssueStatusResponse = {
createdAt: session.createdAt,
lastUpdatedAt: session.lastUpdatedAt,
expiresAt: session.expiresAt,
status: session.status,
statusLists: session.statusLists,
...(session.error && { error: session.error }),
Expand Down
15 changes: 10 additions & 5 deletions packages/issuer/lib/VcIssuer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,10 @@ export class VcIssuer {
}

public async getCredentialOfferSessionById(id: string, lookup?: 'uri' | 'id' | 'preAuthorizedCode'): Promise<CredentialOfferSession> {
if (!this.uris) {
return Promise.reject(Error('Cannot lookup credential offer by id if URI state manager is not set'))
}
if (lookup) {
if (!this.uris) {
return Promise.reject(Error('Cannot lookup credential offer by id if URI state manager is not set'))
}
return new LookupStateManager<URIState, CredentialOfferSession>(this.uris, this._credentialOfferSessions, lookup).getAsserted(id)
}
const session = await this._credentialOfferSessions.get(id)
Expand Down Expand Up @@ -165,6 +165,7 @@ export class VcIssuer {
qrCodeOpts?: QRCodeOpts
correlationId?: string
statusListOpts?: Array<StatusListOpts>
sessionLifeTimeInSec?: number
}): Promise<CreateCredentialOfferURIResult> {
const { offerMode = 'VALUE', correlationId = shortUUID.generate(), credential_configuration_ids, statusListOpts, credentialOfferUri } = opts
if (offerMode === 'REFERENCE' && !credentialOfferUri) {
Expand Down Expand Up @@ -213,9 +214,11 @@ export class VcIssuer {
}
const createdAt = +new Date()
const lastUpdatedAt = createdAt
const expirationInMs = (opts.sessionLifeTimeInSec ?? 10*60) * 1000
const expiresAt = createdAt + Math.abs(expirationInMs)
if (offerMode === 'REFERENCE') {
if (!this.uris) {
throw Error('No URI state manager set, whilst apparently credential offer URIs are being used')
throw Error('No URI state manager set, whilst apparently credential offer by reference is being used')
}

const offerUri = opts.credentialOfferUri?.replace(':id', correlationId) // TODO how is this going to work with auth code flow?
Expand All @@ -227,6 +230,7 @@ export class VcIssuer {
await this.uris.set(correlationId, {
uri: offerUri,
createdAt: createdAt,
expiresAt,
preAuthorizedCode,
issuerState,
credentialOfferCorrelationId: correlationId,
Expand All @@ -250,6 +254,7 @@ export class VcIssuer {
issuerState,
createdAt,
lastUpdatedAt,
expiresAt,
status,
notification_id: uuidv4(),
...(opts.client_id && { clientId: opts.client_id }),
Expand All @@ -276,7 +281,7 @@ export class VcIssuer {
}
EVENTS.emit(CredentialOfferEventNames.OID4VCI_OFFER_CREATED, {
eventName: CredentialOfferEventNames.OID4VCI_OFFER_CREATED,
id: uuidv4(),
id: correlationId,
data: uri,
initiator: '<unknown>',
initiatorType: InitiatorType.EXTERNAL,
Expand Down
8 changes: 7 additions & 1 deletion packages/issuer/lib/state-manager/MemoryStates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export class MemoryStates<T extends StateType> implements IStateManager<T> {
this.expiresInMS = opts?.expiresInSec !== undefined ? opts?.expiresInSec * 1000 : 180000
this.states = new Map()
}

async clearAll(): Promise<void> {
this.states.clear()
}
Expand All @@ -17,8 +18,13 @@ export class MemoryStates<T extends StateType> implements IStateManager<T> {
const states = Array.from(this.states.entries())
const ts = timestamp ?? +new Date()
for (const [id, state] of states) {
if (state.createdAt + this.expiresInMS < ts) {
if (state.expiresAt && state.expiresAt < ts) {
this.states.delete(id)
} else if (!state.expiresAt) {
// If there is no expiration set on the state itself, we will use the state manager expiresInMS value
if (state.createdAt + this.expiresInMS < ts) {
this.states.delete(id)
}
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions packages/oid4vci-common/lib/types/StateManager.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { CredentialDataSupplierInput, NotificationRequest, StatusListOpts } from

export interface StateType {
createdAt: number;
expiresAt?: number;
}

export interface CredentialOfferSession extends StateType {
Expand Down Expand Up @@ -49,6 +50,7 @@ export interface URIState extends StateType {
export interface IssueStatusResponse {
createdAt: number;
lastUpdatedAt: number;
expiresAt?: number;
status: IssueStatus;
error?: string;
clientId?: string;
Expand Down
1 change: 1 addition & 0 deletions packages/oid4vci-common/lib/types/v1_0_13.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ export interface CredentialOfferRESTRequest extends Partial<CredentialOfferPaylo
// auth_session?: string; Would be a nice extension to support, to allow external systems to determine what the auth_session value should be
// @Deprecated use tx_code in the grant object
correlationId?: string;
sessionLifeTimeInSec?: number
pinLength?: number;
qrCodeOpts?: QRCodeOpts;
client_id?: string;
Expand Down

0 comments on commit bbd8d7e

Please sign in to comment.