src/issuer/issuance/oid4vci/dto/offer-request.dto.ts

Extends

OfferRequestBase

Relationships

Used by

No results matching.

Depends on

Index

Properties

Properties

Optional authorization_server
Type : string
Decorators :
@ApiPropertyOptional({description: 'Authorization server id from issuer configuration. If omitted, the first enabled server is used.', example: 'issuer-built-in'})

Optional authorization server id to be used for this issuance flow.

Optional credentialClaims
Type : Record<string, ClaimsSource>
Decorators :
@ApiProperty({description: 'Credential claims configuration per credential. Keys must match credentialConfigurationIds.', type: 'object', additionalProperties: undefined, example: undefined})

Credential claims configuration per credential. Each credential can have claims provided inline or fetched via webhook. Keys must be a subset of credentialConfigurationIds.

credentialConfigurationIds
Type : string[]

List of credential configuration ids to be included in the offer.

flow
Type : FlowType

The flow type for the offer request.

response_type
Type : ResponseTypeValue
Decorators :
@ApiProperty({examples: undefined, description: 'The type of response expected for the offer request.'})
Optional tx_code
Type : string

Transaction code for pre-authorized code flow.

Optional tx_code_description
Type : string

Description for the transaction code (e.g., "Please enter the PIN sent to your email").

Optional webhookEndpointId
Type : string

ID of the webhook endpoint to notify about the status of the issuance process.

import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
import { createZodDto } from "nestjs-zod";
import { z } from "zod";
import {
    WebhookConfig,
    WebhookConfigSchema,
} from "../../../../webhook/webhook.dto";
import {
    ResponseType,
    type ResponseTypeValue,
} from "../../../../verifier/oid4vp/dto/presentation-request.dto";

export const FlowType = {
    AUTH_CODE: "authorization_code",
    PRE_AUTH_CODE: "pre_authorized_code",
} as const;

export type FlowType = (typeof FlowType)[keyof typeof FlowType];

/**
 * Inline claims source - claims provided directly in the request.
 */
class InlineClaimsSource {
    type!: "inline";

    claims!: Record<string, any>;
}

const InlineClaimsSourceSchema = z
    .object({
        type: z.literal("inline"),
        claims: z.record(z.string(), z.unknown()),
    })
    .strict();

const AttributeProviderClaimsSourceSchema = z
    .object({
        type: z.literal("attributeProvider"),
        attributeProviderId: z.string(),
    })
    .strict();

const WebhookClaimsSourceSchema = z
    .object({
        type: z.literal("webhook"),
        webhook: WebhookConfigSchema,
    })
    .strict();

const ClaimsSourceSchema = z.union([
    InlineClaimsSourceSchema,
    AttributeProviderClaimsSourceSchema,
    WebhookClaimsSourceSchema,
]);

const OfferRequestSchema = z
    .object({
        response_type: z.union([
            z.literal(ResponseType.URI),
            z.literal(ResponseType.DC_API),
            z.literal(ResponseType.ISO_18013_7),
        ]),
        flow: z.union([
            z.literal(FlowType.AUTH_CODE),
            z.literal(FlowType.PRE_AUTH_CODE),
        ]),
        tx_code: z.string().optional(),
        tx_code_description: z.string().optional(),
        credentialConfigurationIds: z.array(z.string()),
        authorization_server: z.string().optional(),
        credentialClaims: z.record(z.string(), ClaimsSourceSchema).optional(),
        webhookEndpointId: z.string().optional(),
    })
    .strict()
    .superRefine((data, ctx) => {
        if (!data.credentialClaims) {
            return;
        }

        const allowed = new Set(data.credentialConfigurationIds);
        const invalidKeys = Object.keys(data.credentialClaims).filter(
            (key) => !allowed.has(key),
        );

        if (invalidKeys.length > 0) {
            ctx.addIssue({
                code: z.ZodIssueCode.custom,
                path: ["credentialClaims"],
                message: `credentialClaims contains keys [${invalidKeys.join(", ")}] that are not in credentialConfigurationIds [${data.credentialConfigurationIds.join(", ")}]`,
            });
        }
    });

interface OfferRequestData {
    response_type: ResponseTypeValue;
    flow: FlowType;
    tx_code?: string;
    tx_code_description?: string;
    credentialConfigurationIds: string[];
    authorization_server?: string;
    credentialClaims?: Record<string, ClaimsSource>;
    webhookEndpointId?: string;
}

type OfferRequestConstructor = new () => OfferRequestData;

const OfferRequestBase: OfferRequestConstructor = createZodDto(
    OfferRequestSchema,
) as OfferRequestConstructor;

class AttributeProviderClaimsSource {
    type!: "attributeProvider";

    attributeProviderId!: string;
}

class WebhookClaimsSource {
    type!: "webhook";

    webhook!: WebhookConfig;
}

/**
 * Union type for all claims source types.
 */
export type ClaimsSource =
    | InlineClaimsSource
    | AttributeProviderClaimsSource
    | WebhookClaimsSource;

export class OfferRequestDto extends OfferRequestBase {
    @ApiProperty({
        examples: [
            {
                value: "qrcode",
            },
        ],
        description: "The type of response expected for the offer request.",
    })
    response_type!: ResponseTypeValue;

    /**
     * The flow type for the offer request.
     */
    flow!: FlowType;

    /**
     * Transaction code for pre-authorized code flow.
     */
    tx_code?: string;

    /**
     * Description for the transaction code (e.g., "Please enter the PIN sent to your email").
     */
    tx_code_description?: string;

    /**
     * List of credential configuration ids to be included in the offer.
     */
    credentialConfigurationIds!: string[];

    /**
     * Optional authorization server id to be used for this issuance flow.
     */
    @ApiPropertyOptional({
        description:
            "Authorization server id from issuer configuration. If omitted, the first enabled server is used.",
        example: "issuer-built-in",
    })
    authorization_server?: string;

    /**
     * Credential claims configuration per credential.
     * Each credential can have claims provided inline or fetched via webhook.
     * Keys must be a subset of credentialConfigurationIds.
     */
    @ApiProperty({
        description:
            "Credential claims configuration per credential. Keys must match credentialConfigurationIds.",
        type: "object",
        additionalProperties: {
            oneOf: [
                {
                    type: "object",
                    properties: {
                        type: { type: "string", enum: ["inline"] },
                        claims: {
                            type: "object",
                            additionalProperties: true,
                        },
                    },
                    required: ["type", "claims"],
                },
                {
                    type: "object",
                    properties: {
                        type: {
                            type: "string",
                            enum: ["attributeProvider"],
                        },
                        attributeProviderId: { type: "string" },
                    },
                    required: ["type", "attributeProviderId"],
                },
                {
                    type: "object",
                    properties: {
                        type: {
                            type: "string",
                            enum: ["webhook"],
                        },
                        webhook: {
                            type: "object",
                            properties: {
                                url: { type: "string" },
                                auth: { type: "object" },
                            },
                            required: ["url"],
                        },
                    },
                    required: ["type", "webhook"],
                },
            ],
        },
        example: {
            citizen: {
                type: "inline",
                claims: { given_name: "John", family_name: "Doe" },
            },
        },
    })
    credentialClaims?: Record<string, ClaimsSource>;

    /**
     * ID of the webhook endpoint to notify about the status of the issuance process.
     */
    webhookEndpointId?: string;
}

export class OfferResponse {
    uri!: string;
    /** URI for cross-device flows (no redirect after completion) */
    crossDeviceUri?: string;
    session!: string;
}

results matching ""

    No results matching ""