-
Notifications
You must be signed in to change notification settings - Fork 68
feat: auth and draft 15 #1496
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: auth and draft 15 #1496
Changes from 11 commits
e5b12d4
9942032
69b6685
ee5c472
a1c0335
83c6de7
acafc89
eaf18d4
b304eaf
2eaaeee
cbcaae9
48a5346
2f84577
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| # Stage 1: Build the application | ||
| FROM node:18-alpine as build | ||
| # Install OpenSSL | ||
| RUN apk add --no-cache openssl | ||
| RUN npm install -g pnpm | ||
| # Set the working directory | ||
| WORKDIR /app | ||
|
|
||
| # Copy package.json and package-lock.json | ||
| COPY package.json ./ | ||
| COPY pnpm-workspace.yaml ./ | ||
| #COPY package-lock.json ./ | ||
|
|
||
| ENV PUPPETEER_SKIP_DOWNLOAD=true | ||
|
|
||
| # Install dependencies while ignoring scripts (including Puppeteer's installation) | ||
| RUN pnpm i --ignore-scripts | ||
|
|
||
| # Copy the rest of the application code | ||
| COPY . . | ||
| # RUN cd libs/prisma-service && npx prisma migrate deploy && npx prisma generate | ||
| RUN cd libs/prisma-service && npx prisma generate | ||
|
|
||
| # Build the x509 service | ||
| RUN npm run build x509 | ||
|
|
||
|
|
||
| # Stage 2: Create the final image | ||
| FROM node:18-alpine | ||
| # Install OpenSSL | ||
| RUN apk add --no-cache openssl | ||
| # RUN npm install -g pnpm | ||
| # Set the working directory | ||
| WORKDIR /app | ||
|
|
||
| # Copy the compiled code from the build stage | ||
| COPY --from=build /app/dist/apps/x509/ ./dist/apps/x509/ | ||
|
|
||
| # Copy the libs folder from the build stage | ||
| COPY --from=build /app/libs/ ./libs/ | ||
| #COPY --from=build /app/package.json ./ | ||
| COPY --from=build /app/node_modules ./node_modules | ||
|
|
||
| # Set the command to run the microservice | ||
| CMD ["sh", "-c", "cd libs/prisma-service && npx prisma migrate deploy && npx prisma generate && cd ../.. && node dist/apps/x509/main.js"] | ||
|
Comment on lines
+44
to
+45
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Move Prisma migrations to deployment pipeline, not container startup. Running Consider restructuring to:
For now, simplify the CMD: -CMD ["sh", "-c", "cd libs/prisma-service && npx prisma migrate deploy && npx prisma generate && cd ../.. && node dist/apps/x509/main.js"]
+CMD ["node", "dist/apps/x509/main.js"]
🤖 Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -2,26 +2,29 @@ import { ApiProperty } from '@nestjs/swagger'; | |||||||||||||||
| import { IsString, IsNotEmpty, IsArray } from 'class-validator'; | ||||||||||||||||
|
|
||||||||||||||||
| export class CreateTenantSchemaDto { | ||||||||||||||||
| @ApiProperty() | ||||||||||||||||
| @IsString({ message: 'tenantId must be a string' }) @IsNotEmpty({ message: 'please provide valid tenantId' }) | ||||||||||||||||
| tenantId: string; | ||||||||||||||||
|
|
||||||||||||||||
| @ApiProperty() | ||||||||||||||||
| @IsString({ message: 'schema version must be a string' }) @IsNotEmpty({ message: 'please provide valid schema version' }) | ||||||||||||||||
| schemaVersion: string; | ||||||||||||||||
| @ApiProperty() | ||||||||||||||||
| @IsString({ message: 'tenantId must be a string' }) | ||||||||||||||||
| @IsNotEmpty({ message: 'please provide valid tenantId' }) | ||||||||||||||||
| tenantId: string; | ||||||||||||||||
|
|
||||||||||||||||
| @ApiProperty() | ||||||||||||||||
| @IsString({ message: 'schema name must be a string' }) @IsNotEmpty({ message: 'please provide valid schema name' }) | ||||||||||||||||
| schemaName: string; | ||||||||||||||||
| @ApiProperty() | ||||||||||||||||
| @IsString({ message: 'schema version must be a string' }) | ||||||||||||||||
| @IsNotEmpty({ message: 'please provide valid schema version' }) | ||||||||||||||||
| schemaVersion: string; | ||||||||||||||||
|
|
||||||||||||||||
| @ApiProperty() | ||||||||||||||||
| @IsArray({ message: 'attributes must be an array' }) | ||||||||||||||||
| @IsString({ each: true }) | ||||||||||||||||
| @IsNotEmpty({ message: 'please provide valid attributes' }) | ||||||||||||||||
| attributes: string[]; | ||||||||||||||||
| @ApiProperty() | ||||||||||||||||
| @IsString({ message: 'schema name must be a string' }) | ||||||||||||||||
| @IsNotEmpty({ message: 'please provide valid schema name' }) | ||||||||||||||||
| schemaName: string; | ||||||||||||||||
|
|
||||||||||||||||
| @ApiProperty() | ||||||||||||||||
|
|
||||||||||||||||
| @IsNotEmpty({ message: 'please provide orgId' }) | ||||||||||||||||
| orgId: string; | ||||||||||||||||
| } | ||||||||||||||||
| @ApiProperty() | ||||||||||||||||
| @IsArray({ message: 'attributes must be an array' }) | ||||||||||||||||
| @IsString({ each: true }) | ||||||||||||||||
| // TODO: IsNotEmpty won't work for array. Must use @ArrayNotEmpty() instead | ||||||||||||||||
| @IsNotEmpty({ message: 'please provide valid attributes' }) | ||||||||||||||||
| attributes: string[]; | ||||||||||||||||
|
Comment on lines
20
to
27
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix array validation: @isnotempty doesn’t enforce non-empty arrays; use @ArrayNotEmpty and element checks Empty arrays will currently pass. Enforce at least one attribute and non-empty strings per element. Also set Swagger type for arrays. Apply: - @ApiProperty()
+ @ApiProperty({ type: [String] })
@IsArray({ message: 'attributes must be an array' })
- @IsString({ each: true })
- // TODO: IsNotEmpty won't work for array. Must use @ArrayNotEmpty() instead
- @IsNotEmpty({ message: 'please provide valid attributes' })
+ @IsString({ each: true })
+ @ArrayNotEmpty({ message: 'please provide at least one attribute' })
+ @IsNotEmpty({ each: true, message: 'attribute must not be empty' })
attributes: string[];And update imports (outside this hunk): -import { IsString, IsNotEmpty, IsArray } from 'class-validator';
+import { IsString, IsNotEmpty, IsArray, ArrayNotEmpty } from 'class-validator';Optional (if duplicates are not allowed): // @ArrayUnique({ message: 'attributes must be unique' })🤖 Prompt for AI Agents |
||||||||||||||||
|
|
||||||||||||||||
| @ApiProperty() | ||||||||||||||||
| @IsNotEmpty({ message: 'please provide orgId' }) | ||||||||||||||||
| orgId: string; | ||||||||||||||||
|
Comment on lines
+29
to
+31
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. orgId missing type validation: add @IsString (or specific ID validator) Without @IsString, numbers/objects could pass if non-empty, risking downstream failures. @ApiProperty()
+ @IsString({ message: 'orgId must be a string' })
@IsNotEmpty({ message: 'please provide orgId' })
orgId: string;Optional (if UUID): - @IsString({ message: 'orgId must be a string' })
+ @IsUUID('4', { message: 'orgId must be a valid UUID v4' })Remember to import IsUUID if used. 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||
| } | ||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -21,6 +21,7 @@ import { | |||||||||||||||||||||||||||||||||||||
| } from 'class-validator'; | ||||||||||||||||||||||||||||||||||||||
| import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; | ||||||||||||||||||||||||||||||||||||||
| import { Type } from 'class-transformer'; | ||||||||||||||||||||||||||||||||||||||
| import { dateToSeconds } from '@credebl/common/date-only'; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| /* ========= disclosureFrame custom validator ========= */ | ||||||||||||||||||||||||||||||||||||||
| function isDisclosureFrameValue(v: unknown): boolean { | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -117,6 +118,24 @@ function ExactlyOneOf(keys: string[], options?: ValidationOptions) { | |||||||||||||||||||||||||||||||||||||
| return Validate(ExactlyOneOfConstraint, keys, options); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| export class ValidityInfo { | ||||||||||||||||||||||||||||||||||||||
| @ApiProperty({ | ||||||||||||||||||||||||||||||||||||||
| example: '2025-04-23T14:34:09.188Z', | ||||||||||||||||||||||||||||||||||||||
| required: true | ||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||
| @IsString() | ||||||||||||||||||||||||||||||||||||||
| @IsNotEmpty() | ||||||||||||||||||||||||||||||||||||||
| validFrom: Date; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| @ApiProperty({ | ||||||||||||||||||||||||||||||||||||||
| example: '2026-05-03T14:34:09.188Z', | ||||||||||||||||||||||||||||||||||||||
| required: true | ||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||
| @IsString() | ||||||||||||||||||||||||||||||||||||||
| @IsNotEmpty() | ||||||||||||||||||||||||||||||||||||||
| validUntil: Date; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| /* ========= Request DTOs ========= */ | ||||||||||||||||||||||||||||||||||||||
| export class CredentialRequestDto { | ||||||||||||||||||||||||||||||||||||||
| @ApiProperty({ | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -137,13 +156,20 @@ export class CredentialRequestDto { | |||||||||||||||||||||||||||||||||||||
| payload!: Record<string, unknown>; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| @ApiPropertyOptional({ | ||||||||||||||||||||||||||||||||||||||
| description: 'Selective disclosure: claim -> boolean (or nested map)', | ||||||||||||||||||||||||||||||||||||||
| example: { name: true, DOB: true, additionalProp3: false }, | ||||||||||||||||||||||||||||||||||||||
| example: { validFrom: '2025-04-23T14:34:09.188Z', validUntil: '2026-05-03T14:34:09.188Z' }, | ||||||||||||||||||||||||||||||||||||||
| required: false | ||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||
| @IsOptional() | ||||||||||||||||||||||||||||||||||||||
| @IsDisclosureFrame() | ||||||||||||||||||||||||||||||||||||||
| disclosureFrame?: Record<string, boolean | Record<string, boolean>>; | ||||||||||||||||||||||||||||||||||||||
| validityInfo?: ValidityInfo; | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
139
to
+166
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major Add nested validation for The optional Apply this diff: @ApiPropertyOptional({
example: { validFrom: '2025-04-23T14:34:09.188Z', validUntil: '2026-05-03T14:34:09.188Z' },
required: false
})
@IsOptional()
+ @ValidateNested()
+ @Type(() => ValidityInfo)
validityInfo?: ValidityInfo;📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| // @ApiPropertyOptional({ | ||||||||||||||||||||||||||||||||||||||
| // description: 'Selective disclosure: claim -> boolean (or nested map)', | ||||||||||||||||||||||||||||||||||||||
| // example: { name: true, DOB: true, additionalProp3: false }, | ||||||||||||||||||||||||||||||||||||||
| // required: false | ||||||||||||||||||||||||||||||||||||||
| // }) | ||||||||||||||||||||||||||||||||||||||
| // @IsOptional() | ||||||||||||||||||||||||||||||||||||||
| // @IsDisclosureFrame() | ||||||||||||||||||||||||||||||||||||||
| // disclosureFrame?: Record<string, boolean | Record<string, boolean>>; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| export class CreateOidcCredentialOfferDto { | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -157,25 +183,16 @@ export class CreateOidcCredentialOfferDto { | |||||||||||||||||||||||||||||||||||||
| @Type(() => CredentialRequestDto) | ||||||||||||||||||||||||||||||||||||||
| credentials!: CredentialRequestDto[]; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| // XOR: exactly one present | ||||||||||||||||||||||||||||||||||||||
| @ApiPropertyOptional({ type: PreAuthorizedCodeFlowConfigDto }) | ||||||||||||||||||||||||||||||||||||||
| @IsOptional() | ||||||||||||||||||||||||||||||||||||||
| @ValidateNested() | ||||||||||||||||||||||||||||||||||||||
| @Type(() => PreAuthorizedCodeFlowConfigDto) | ||||||||||||||||||||||||||||||||||||||
| preAuthorizedCodeFlowConfig?: PreAuthorizedCodeFlowConfigDto; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| @IsOptional() | ||||||||||||||||||||||||||||||||||||||
| @ValidateNested() | ||||||||||||||||||||||||||||||||||||||
| @Type(() => AuthorizationCodeFlowConfigDto) | ||||||||||||||||||||||||||||||||||||||
| authorizationCodeFlowConfig?: AuthorizationCodeFlowConfigDto; | ||||||||||||||||||||||||||||||||||||||
| @ApiProperty({ | ||||||||||||||||||||||||||||||||||||||
| example: 'preAuthorizedCodeFlow', | ||||||||||||||||||||||||||||||||||||||
| enum: ['preAuthorizedCodeFlow', 'authorizationCodeFlow'], | ||||||||||||||||||||||||||||||||||||||
| description: 'Authorization type' | ||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||
| @IsString() | ||||||||||||||||||||||||||||||||||||||
| @IsIn(['preAuthorizedCodeFlow', 'authorizationCodeFlow']) | ||||||||||||||||||||||||||||||||||||||
| authorizationType!: 'preAuthorizedCodeFlow' | 'authorizationCodeFlow'; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| issuerId?: string; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| // host XOR rule | ||||||||||||||||||||||||||||||||||||||
| @ExactlyOneOf(['preAuthorizedCodeFlowConfig', 'authorizationCodeFlowConfig'], { | ||||||||||||||||||||||||||||||||||||||
| message: 'Provide exactly one of preAuthorizedCodeFlowConfig or authorizationCodeFlowConfig.' | ||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||
| private readonly _exactlyOne?: unknown; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| export class GetAllCredentialOfferDto { | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -266,20 +283,33 @@ export class CredentialDto { | |||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| @ApiProperty({ | ||||||||||||||||||||||||||||||||||||||
| description: 'Credential payload (namespace data, validity info, etc.)', | ||||||||||||||||||||||||||||||||||||||
| example: { | ||||||||||||||||||||||||||||||||||||||
| namespaces: { | ||||||||||||||||||||||||||||||||||||||
| 'org.iso.23220.photoID.1': { | ||||||||||||||||||||||||||||||||||||||
| birth_date: '1970-02-14', | ||||||||||||||||||||||||||||||||||||||
| family_name: 'Müller-Lüdenscheid', | ||||||||||||||||||||||||||||||||||||||
| given_name: 'Ford Praxibetel', | ||||||||||||||||||||||||||||||||||||||
| document_number: 'LA001801M' | ||||||||||||||||||||||||||||||||||||||
| example: [ | ||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||
| namespaces: { | ||||||||||||||||||||||||||||||||||||||
| 'org.iso.23220.photoID.1': { | ||||||||||||||||||||||||||||||||||||||
| birth_date: '1970-02-14', | ||||||||||||||||||||||||||||||||||||||
| family_name: 'Müller-Lüdenscheid', | ||||||||||||||||||||||||||||||||||||||
| given_name: 'Ford Praxibetel', | ||||||||||||||||||||||||||||||||||||||
| document_number: 'LA001801M' | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||
| validityInfo: { | ||||||||||||||||||||||||||||||||||||||
| validFrom: '2025-04-23T14:34:09.188Z', | ||||||||||||||||||||||||||||||||||||||
| validUntil: '2026-05-03T14:34:09.188Z' | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||
| validityInfo: { | ||||||||||||||||||||||||||||||||||||||
| validFrom: '2025-04-23T14:34:09.188Z', | ||||||||||||||||||||||||||||||||||||||
| validUntil: '2026-05-03T14:34:09.188Z' | ||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||
| full_name: 'Garry', | ||||||||||||||||||||||||||||||||||||||
| address: { | ||||||||||||||||||||||||||||||||||||||
| street_address: 'M.G. Road', | ||||||||||||||||||||||||||||||||||||||
| locality: 'Pune', | ||||||||||||||||||||||||||||||||||||||
| country: 'India' | ||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||
| iat: 1698151532, | ||||||||||||||||||||||||||||||||||||||
| nbf: dateToSeconds(new Date()), | ||||||||||||||||||||||||||||||||||||||
| exp: dateToSeconds(new Date(Date.now() + 5 * 365 * 24 * 60 * 60 * 1000)) | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||
| @ValidateNested() | ||||||||||||||||||||||||||||||||||||||
| payload: object; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove redundant Prisma generate from runtime CMD.
Prisma artifacts are already generated during the build stage (line 22). Running
npx prisma generateagain at runtime (line 45) is unnecessary and adds startup overhead.If Prisma generate is truly needed at runtime (e.g., due to schema changes), consider regenerating it in the build stage only and relying on that output in the final image.
Also applies to: 45-45
🤖 Prompt for AI Agents