Skip to content

Commit b8b273e

Browse files
Merge pull request #1496 from credebl/feat/auth-and-draft-15
feat: auth and draft 15
2 parents 5c3903c + 2f84577 commit b8b273e

File tree

23 files changed

+1398
-918
lines changed

23 files changed

+1398
-918
lines changed

Dockerfiles/Dockerfile.x509

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Stage 1: Build the application
2+
FROM node:18-alpine as build
3+
# Install OpenSSL
4+
RUN apk add --no-cache openssl
5+
RUN npm install -g pnpm
6+
# Set the working directory
7+
WORKDIR /app
8+
9+
# Copy package.json and package-lock.json
10+
COPY package.json ./
11+
COPY pnpm-workspace.yaml ./
12+
#COPY package-lock.json ./
13+
14+
ENV PUPPETEER_SKIP_DOWNLOAD=true
15+
16+
# Install dependencies while ignoring scripts (including Puppeteer's installation)
17+
RUN pnpm i --ignore-scripts
18+
19+
# Copy the rest of the application code
20+
COPY . .
21+
# RUN cd libs/prisma-service && npx prisma migrate deploy && npx prisma generate
22+
RUN cd libs/prisma-service && npx prisma generate
23+
24+
# Build the x509 service
25+
RUN npm run build x509
26+
27+
28+
# Stage 2: Create the final image
29+
FROM node:18-alpine
30+
# Install OpenSSL
31+
RUN apk add --no-cache openssl
32+
# RUN npm install -g pnpm
33+
# Set the working directory
34+
WORKDIR /app
35+
36+
# Copy the compiled code from the build stage
37+
COPY --from=build /app/dist/apps/x509/ ./dist/apps/x509/
38+
39+
# Copy the libs folder from the build stage
40+
COPY --from=build /app/libs/ ./libs/
41+
#COPY --from=build /app/package.json ./
42+
COPY --from=build /app/node_modules ./node_modules
43+
44+
# Set the command to run the microservice
45+
CMD ["sh", "-c", "cd libs/prisma-service && npx prisma migrate deploy && npx prisma generate && cd ../.. && node dist/apps/x509/main.js"]

apps/api-gateway/src/agent-service/dto/create-schema.dto.ts

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,31 @@ import { ApiProperty } from '@nestjs/swagger';
22
import { IsString, IsNotEmpty, IsArray } from 'class-validator';
33

44
export class CreateTenantSchemaDto {
5-
@ApiProperty()
6-
@IsString({ message: 'tenantId must be a string' }) @IsNotEmpty({ message: 'please provide valid tenantId' })
7-
tenantId: string;
8-
9-
@ApiProperty()
10-
@IsString({ message: 'schema version must be a string' }) @IsNotEmpty({ message: 'please provide valid schema version' })
11-
schemaVersion: string;
5+
@ApiProperty()
6+
@IsString({ message: 'tenantId must be a string' })
7+
@IsNotEmpty({ message: 'please provide valid tenantId' })
8+
tenantId: string;
129

13-
@ApiProperty()
14-
@IsString({ message: 'schema name must be a string' }) @IsNotEmpty({ message: 'please provide valid schema name' })
15-
schemaName: string;
10+
@ApiProperty()
11+
@IsString({ message: 'schema version must be a string' })
12+
@IsNotEmpty({ message: 'please provide valid schema version' })
13+
schemaVersion: string;
1614

17-
@ApiProperty()
18-
@IsArray({ message: 'attributes must be an array' })
19-
@IsString({ each: true })
20-
@IsNotEmpty({ message: 'please provide valid attributes' })
21-
attributes: string[];
15+
@ApiProperty()
16+
@IsString({ message: 'schema name must be a string' })
17+
@IsNotEmpty({ message: 'please provide valid schema name' })
18+
schemaName: string;
2219

23-
@ApiProperty()
24-
25-
@IsNotEmpty({ message: 'please provide orgId' })
26-
orgId: string;
27-
}
20+
@ApiProperty()
21+
@IsArray({ message: 'attributes must be an array' })
22+
@IsString({ each: true })
23+
// TODO: IsNotEmpty won't work for array. Must use @ArrayNotEmpty() instead
24+
// @ArrayNotEmpty({ message: 'please provide at least one attribute' })
25+
// @IsNotEmpty({ each: true, message: 'attribute must not be empty' })
26+
@IsNotEmpty({ message: 'please provide valid attributes' })
27+
attributes: string[];
28+
29+
@ApiProperty()
30+
@IsNotEmpty({ message: 'please provide orgId' })
31+
orgId: string;
32+
}

apps/api-gateway/src/oid4vc-issuance/dtos/issuer-sessions.dto.ts

Lines changed: 66 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@ import {
1717
ValidatorConstraint,
1818
ValidatorConstraintInterface,
1919
ValidationArguments,
20-
Validate
20+
Validate,
21+
IsDate
2122
} from 'class-validator';
2223
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
2324
import { Type } from 'class-transformer';
25+
import { dateToSeconds } from '@credebl/common/date-only';
2426

2527
/* ========= disclosureFrame custom validator ========= */
2628
function isDisclosureFrameValue(v: unknown): boolean {
@@ -117,6 +119,26 @@ function ExactlyOneOf(keys: string[], options?: ValidationOptions) {
117119
return Validate(ExactlyOneOfConstraint, keys, options);
118120
}
119121

122+
export class ValidityInfo {
123+
@ApiProperty({
124+
example: '2025-04-23T14:34:09.188Z',
125+
required: true
126+
})
127+
@IsNotEmpty()
128+
@Type(() => Date)
129+
@IsDate()
130+
validFrom: Date;
131+
132+
@ApiProperty({
133+
example: '2026-05-03T14:34:09.188Z',
134+
required: true
135+
})
136+
@IsNotEmpty()
137+
@Type(() => Date)
138+
@IsDate()
139+
validUntil: Date;
140+
}
141+
120142
/* ========= Request DTOs ========= */
121143
export class CredentialRequestDto {
122144
@ApiProperty({
@@ -137,13 +159,20 @@ export class CredentialRequestDto {
137159
payload!: Record<string, unknown>;
138160

139161
@ApiPropertyOptional({
140-
description: 'Selective disclosure: claim -> boolean (or nested map)',
141-
example: { name: true, DOB: true, additionalProp3: false },
162+
example: { validFrom: '2025-04-23T14:34:09.188Z', validUntil: '2026-05-03T14:34:09.188Z' },
142163
required: false
143164
})
144165
@IsOptional()
145-
@IsDisclosureFrame()
146-
disclosureFrame?: Record<string, boolean | Record<string, boolean>>;
166+
validityInfo?: ValidityInfo;
167+
168+
// @ApiPropertyOptional({
169+
// description: 'Selective disclosure: claim -> boolean (or nested map)',
170+
// example: { name: true, DOB: true, additionalProp3: false },
171+
// required: false
172+
// })
173+
// @IsOptional()
174+
// @IsDisclosureFrame()
175+
// disclosureFrame?: Record<string, boolean | Record<string, boolean>>;
147176
}
148177

149178
export class CreateOidcCredentialOfferDto {
@@ -157,25 +186,16 @@ export class CreateOidcCredentialOfferDto {
157186
@Type(() => CredentialRequestDto)
158187
credentials!: CredentialRequestDto[];
159188

160-
// XOR: exactly one present
161-
@ApiPropertyOptional({ type: PreAuthorizedCodeFlowConfigDto })
162-
@IsOptional()
163-
@ValidateNested()
164-
@Type(() => PreAuthorizedCodeFlowConfigDto)
165-
preAuthorizedCodeFlowConfig?: PreAuthorizedCodeFlowConfigDto;
166-
167-
@IsOptional()
168-
@ValidateNested()
169-
@Type(() => AuthorizationCodeFlowConfigDto)
170-
authorizationCodeFlowConfig?: AuthorizationCodeFlowConfigDto;
189+
@ApiProperty({
190+
example: 'preAuthorizedCodeFlow',
191+
enum: ['preAuthorizedCodeFlow', 'authorizationCodeFlow'],
192+
description: 'Authorization type'
193+
})
194+
@IsString()
195+
@IsIn(['preAuthorizedCodeFlow', 'authorizationCodeFlow'])
196+
authorizationType!: 'preAuthorizedCodeFlow' | 'authorizationCodeFlow';
171197

172198
issuerId?: string;
173-
174-
// host XOR rule
175-
@ExactlyOneOf(['preAuthorizedCodeFlowConfig', 'authorizationCodeFlowConfig'], {
176-
message: 'Provide exactly one of preAuthorizedCodeFlowConfig or authorizationCodeFlowConfig.'
177-
})
178-
private readonly _exactlyOne?: unknown;
179199
}
180200

181201
export class GetAllCredentialOfferDto {
@@ -266,20 +286,33 @@ export class CredentialDto {
266286

267287
@ApiProperty({
268288
description: 'Credential payload (namespace data, validity info, etc.)',
269-
example: {
270-
namespaces: {
271-
'org.iso.23220.photoID.1': {
272-
birth_date: '1970-02-14',
273-
family_name: 'Müller-Lüdenscheid',
274-
given_name: 'Ford Praxibetel',
275-
document_number: 'LA001801M'
289+
example: [
290+
{
291+
namespaces: {
292+
'org.iso.23220.photoID.1': {
293+
birth_date: '1970-02-14',
294+
family_name: 'Müller-Lüdenscheid',
295+
given_name: 'Ford Praxibetel',
296+
document_number: 'LA001801M'
297+
}
298+
},
299+
validityInfo: {
300+
validFrom: '2025-04-23T14:34:09.188Z',
301+
validUntil: '2026-05-03T14:34:09.188Z'
276302
}
277303
},
278-
validityInfo: {
279-
validFrom: '2025-04-23T14:34:09.188Z',
280-
validUntil: '2026-05-03T14:34:09.188Z'
304+
{
305+
full_name: 'Garry',
306+
address: {
307+
street_address: 'M.G. Road',
308+
locality: 'Pune',
309+
country: 'India'
310+
},
311+
iat: 1698151532,
312+
nbf: dateToSeconds(new Date()),
313+
exp: dateToSeconds(new Date(Date.now() + 5 * 365 * 24 * 60 * 60 * 1000))
281314
}
282-
}
315+
]
283316
})
284317
@ValidateNested()
285318
payload: object;

apps/api-gateway/src/oid4vc-issuance/dtos/oid4vc-credential-wh.dto.ts

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,10 @@ import { ApiProperty } from '@nestjs/swagger';
22
import { IsArray, IsObject, IsString } from 'class-validator';
33

44
export class CredentialOfferPayloadDto {
5-
@ApiProperty()
6-
@IsString()
7-
// eslint-disable-next-line camelcase
8-
credential_issuer!: string;
9-
105
@ApiProperty({ type: [String] })
116
@IsArray()
127
// eslint-disable-next-line camelcase
138
credential_configuration_ids!: string[];
14-
15-
@ApiProperty({ type: 'object', additionalProperties: true })
16-
@IsObject()
17-
grants!: Record<string, unknown>;
18-
19-
@ApiProperty({ type: [Object] })
20-
@IsArray()
21-
credentials!: Record<string, unknown>[];
229
}
2310

2411
export class IssuanceMetadataDto {
@@ -40,11 +27,48 @@ export class OidcIssueCredentialDto {
4027
@IsString()
4128
credentialOfferId!: string;
4229

30+
@ApiProperty({ type: [Object] })
31+
@IsArray()
32+
issuedCredentials!: Record<string, unknown>[];
33+
34+
@ApiProperty({ type: CredentialOfferPayloadDto })
35+
@IsObject()
36+
credentialOfferPayload!: CredentialOfferPayloadDto;
37+
4338
@ApiProperty()
4439
@IsString()
4540
state!: string;
4641

42+
@ApiProperty()
43+
@IsString()
44+
createdAt!: string;
45+
46+
@ApiProperty()
47+
@IsString()
48+
updatedAt!: string;
49+
4750
@ApiProperty()
4851
@IsString()
4952
contextCorrelationId!: string;
5053
}
54+
55+
/**
56+
* Utility: return only credential_configuration_ids from a webhook payload
57+
*/
58+
export function extractCredentialConfigurationIds(payload: Partial<OidcIssueCredentialDto>): string[] {
59+
const cfg = payload?.credentialOfferPayload?.credential_configuration_ids;
60+
return Array.isArray(cfg) ? cfg : [];
61+
}
62+
63+
export function sanitizeOidcIssueCredentialDto(
64+
payload: Partial<OidcIssueCredentialDto>
65+
): Partial<OidcIssueCredentialDto> {
66+
const ids = extractCredentialConfigurationIds(payload);
67+
return {
68+
...payload,
69+
credentialOfferPayload: {
70+
// eslint-disable-next-line camelcase
71+
credential_configuration_ids: ids
72+
}
73+
};
74+
}

0 commit comments

Comments
 (0)