From 93bc18022a2239f9db1ecab6bea3d01890bf8ec3 Mon Sep 17 00:00:00 2001 From: trev Date: Sun, 23 Mar 2025 14:33:25 -0400 Subject: [PATCH] WIP: passkey auth --- client/package-lock.json | 42 ++- client/package.json | 2 + client/src/lib/services/item/v1/item_pb.ts | 338 +++++++++--------- client/src/lib/services/user/v1/auth_pb.ts | 252 ++++++++----- client/src/lib/services/user/v1/user_pb.ts | 298 ++++++++------- client/src/lib/ui/Button.svelte | 6 +- client/src/lib/webauthn.ts | 120 +++++++ client/src/routes/(app)/settings/+page.svelte | 16 +- client/static/openapi/openapi.yaml | 163 +++++++++ proto/user/v1/auth.proto | 20 +- proto/user/v1/user.proto | 7 + server/go.mod | 4 + server/go.sum | 10 +- server/internal/database/migrate.go | 2 +- server/internal/handlers/user/v1/auth.go | 91 +++++ server/internal/handlers/user/v1/user.go | 30 ++ server/internal/models/passkey.go | 16 + server/internal/models/user.go | 8 +- server/internal/services/user/v1/auth.pb.go | 276 ++++++++++++-- server/internal/services/user/v1/user.pb.go | 165 +++++++-- .../user/v1/userv1connect/auth.connect.go | 64 +++- .../user/v1/userv1connect/user.connect.go | 29 ++ 22 files changed, 1500 insertions(+), 459 deletions(-) create mode 100644 client/src/lib/webauthn.ts create mode 100644 server/internal/models/passkey.go diff --git a/client/package-lock.json b/client/package-lock.json index dcfe9cc..6f4d7f0 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -14,11 +14,13 @@ "@eslint/js": "^9.18.0", "@lucide/svelte": "^0.479.0", "@scalar/api-reference": "^1.28.5", + "@simplewebauthn/browser": "^13.1.0", "@sveltejs/adapter-static": "^3.0.8", "@sveltejs/kit": "^2.20.1", "@sveltejs/vite-plugin-svelte": "^5.0.3", "@tailwindcss/vite": "^4.0.14", "bits-ui": "^1.3.13", + "cbor2": "^1.12.0", "clsx": "^2.1.1", "eslint": "^9.22.0", "eslint-config-prettier": "^10.1.1", @@ -2069,6 +2071,13 @@ "node": ">=18" } }, + "node_modules/@simplewebauthn/browser": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/@simplewebauthn/browser/-/browser-13.1.0.tgz", + "integrity": "sha512-WuHZ/PYvyPJ9nxSzgHtOEjogBhwJfC8xzYkPC+rR/+8chl/ft4ngjiK8kSU5HtRJfczupyOh33b25TjYbvwAcg==", + "dev": true, + "license": "MIT" + }, "node_modules/@sveltejs/acorn-typescript": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.5.tgz", @@ -3331,6 +3340,16 @@ "node": ">=6" } }, + "node_modules/cbor2": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/cbor2/-/cbor2-1.12.0.tgz", + "integrity": "sha512-3Cco8XQhi27DogSp9Ri6LYNZLi/TBY/JVnDe+mj06NkBjW/ZYOtekaEU4wZ4xcRMNrFkDv8KNtOAqHyDfz3lYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.7" + } + }, "node_modules/ccount": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", @@ -6118,6 +6137,19 @@ "node": ">=8.6" } }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -6336,13 +6368,15 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { - "node": ">=8.6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" diff --git a/client/package.json b/client/package.json index bcf3d20..a20db5f 100644 --- a/client/package.json +++ b/client/package.json @@ -20,11 +20,13 @@ "@eslint/js": "^9.18.0", "@lucide/svelte": "^0.479.0", "@scalar/api-reference": "^1.28.5", + "@simplewebauthn/browser": "^13.1.0", "@sveltejs/adapter-static": "^3.0.8", "@sveltejs/kit": "^2.20.1", "@sveltejs/vite-plugin-svelte": "^5.0.3", "@tailwindcss/vite": "^4.0.14", "bits-ui": "^1.3.13", + "cbor2": "^1.12.0", "clsx": "^2.1.1", "eslint": "^9.22.0", "eslint-config-prettier": "^10.1.1", diff --git a/client/src/lib/services/item/v1/item_pb.ts b/client/src/lib/services/item/v1/item_pb.ts index ef168e4..9fe15c9 100644 --- a/client/src/lib/services/item/v1/item_pb.ts +++ b/client/src/lib/services/item/v1/item_pb.ts @@ -2,305 +2,295 @@ // @generated from file item/v1/item.proto (package item.v1, syntax proto3) /* eslint-disable */ -import type { GenFile, GenMessage, GenService } from '@bufbuild/protobuf/codegenv1'; -import { fileDesc, messageDesc, serviceDesc } from '@bufbuild/protobuf/codegenv1'; -import type { Timestamp } from '@bufbuild/protobuf/wkt'; -import { file_google_protobuf_timestamp } from '@bufbuild/protobuf/wkt'; -import type { Message } from '@bufbuild/protobuf'; +import type { GenFile, GenMessage, GenService } from "@bufbuild/protobuf/codegenv1"; +import { fileDesc, messageDesc, serviceDesc } from "@bufbuild/protobuf/codegenv1"; +import type { Timestamp } from "@bufbuild/protobuf/wkt"; +import { file_google_protobuf_timestamp } from "@bufbuild/protobuf/wkt"; +import type { Message } from "@bufbuild/protobuf"; /** * Describes the file item/v1/item.proto. */ -export const file_item_v1_item: GenFile = - /*@__PURE__*/ - fileDesc( - 'ChJpdGVtL3YxL2l0ZW0ucHJvdG8SB2l0ZW0udjEinAEKBEl0ZW0SDwoCaWQYASABKA1IAIgBARIMCgRuYW1lGAIgASgJEhMKC2Rlc2NyaXB0aW9uGAMgASgJEg0KBXByaWNlGAQgASgCEhAKCHF1YW50aXR5GAUgASgNEi4KBWFkZGVkGAYgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcEgBiAEBQgUKA19pZEIICgZfYWRkZWQiHAoOR2V0SXRlbVJlcXVlc3QSCgoCaWQYASABKA0iLgoPR2V0SXRlbVJlc3BvbnNlEhsKBGl0ZW0YASABKAsyDS5pdGVtLnYxLkl0ZW0i3wEKD0dldEl0ZW1zUmVxdWVzdBIuCgVzdGFydBgBIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXBIAIgBARIsCgNlbmQYAiABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wSAGIAQESEwoGZmlsdGVyGAMgASgJSAKIAQESEgoFbGltaXQYBCABKA1IA4gBARITCgZvZmZzZXQYBSABKA1IBIgBAUIICgZfc3RhcnRCBgoEX2VuZEIJCgdfZmlsdGVyQggKBl9saW1pdEIJCgdfb2Zmc2V0Ij8KEEdldEl0ZW1zUmVzcG9uc2USHAoFaXRlbXMYASADKAsyDS5pdGVtLnYxLkl0ZW0SDQoFY291bnQYAiABKAQiMAoRQ3JlYXRlSXRlbVJlcXVlc3QSGwoEaXRlbRgBIAEoCzINLml0ZW0udjEuSXRlbSIxChJDcmVhdGVJdGVtUmVzcG9uc2USGwoEaXRlbRgBIAEoCzINLml0ZW0udjEuSXRlbSIwChFVcGRhdGVJdGVtUmVxdWVzdBIbCgRpdGVtGAEgASgLMg0uaXRlbS52MS5JdGVtIjEKElVwZGF0ZUl0ZW1SZXNwb25zZRIbCgRpdGVtGAEgASgLMg0uaXRlbS52MS5JdGVtIh8KEURlbGV0ZUl0ZW1SZXF1ZXN0EgoKAmlkGAEgASgNIhQKEkRlbGV0ZUl0ZW1SZXNwb25zZTLrAgoLSXRlbVNlcnZpY2USPgoHR2V0SXRlbRIXLml0ZW0udjEuR2V0SXRlbVJlcXVlc3QaGC5pdGVtLnYxLkdldEl0ZW1SZXNwb25zZSIAEkEKCEdldEl0ZW1zEhguaXRlbS52MS5HZXRJdGVtc1JlcXVlc3QaGS5pdGVtLnYxLkdldEl0ZW1zUmVzcG9uc2UiABJHCgpDcmVhdGVJdGVtEhouaXRlbS52MS5DcmVhdGVJdGVtUmVxdWVzdBobLml0ZW0udjEuQ3JlYXRlSXRlbVJlc3BvbnNlIgASRwoKVXBkYXRlSXRlbRIaLml0ZW0udjEuVXBkYXRlSXRlbVJlcXVlc3QaGy5pdGVtLnYxLlVwZGF0ZUl0ZW1SZXNwb25zZSIAEkcKCkRlbGV0ZUl0ZW0SGi5pdGVtLnYxLkRlbGV0ZUl0ZW1SZXF1ZXN0GhsuaXRlbS52MS5EZWxldGVJdGVtUmVzcG9uc2UiAEKdAQoLY29tLml0ZW0udjFCCUl0ZW1Qcm90b1ABWkZnaXRodWIuY29tL3Nwb3RkZW1vNC90cmV2c3RhY2svc2VydmVyL2ludGVybmFsL3NlcnZpY2VzL2l0ZW0vdjE7aXRlbXYxogIDSVhYqgIHSXRlbS5WMcoCB0l0ZW1cVjHiAhNJdGVtXFYxXEdQQk1ldGFkYXRh6gIISXRlbTo6VjFiBnByb3RvMw', - [file_google_protobuf_timestamp] - ); +export const file_item_v1_item: GenFile = /*@__PURE__*/ + fileDesc("ChJpdGVtL3YxL2l0ZW0ucHJvdG8SB2l0ZW0udjEinAEKBEl0ZW0SDwoCaWQYASABKA1IAIgBARIMCgRuYW1lGAIgASgJEhMKC2Rlc2NyaXB0aW9uGAMgASgJEg0KBXByaWNlGAQgASgCEhAKCHF1YW50aXR5GAUgASgNEi4KBWFkZGVkGAYgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcEgBiAEBQgUKA19pZEIICgZfYWRkZWQiHAoOR2V0SXRlbVJlcXVlc3QSCgoCaWQYASABKA0iLgoPR2V0SXRlbVJlc3BvbnNlEhsKBGl0ZW0YASABKAsyDS5pdGVtLnYxLkl0ZW0i3wEKD0dldEl0ZW1zUmVxdWVzdBIuCgVzdGFydBgBIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXBIAIgBARIsCgNlbmQYAiABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wSAGIAQESEwoGZmlsdGVyGAMgASgJSAKIAQESEgoFbGltaXQYBCABKA1IA4gBARITCgZvZmZzZXQYBSABKA1IBIgBAUIICgZfc3RhcnRCBgoEX2VuZEIJCgdfZmlsdGVyQggKBl9saW1pdEIJCgdfb2Zmc2V0Ij8KEEdldEl0ZW1zUmVzcG9uc2USHAoFaXRlbXMYASADKAsyDS5pdGVtLnYxLkl0ZW0SDQoFY291bnQYAiABKAQiMAoRQ3JlYXRlSXRlbVJlcXVlc3QSGwoEaXRlbRgBIAEoCzINLml0ZW0udjEuSXRlbSIxChJDcmVhdGVJdGVtUmVzcG9uc2USGwoEaXRlbRgBIAEoCzINLml0ZW0udjEuSXRlbSIwChFVcGRhdGVJdGVtUmVxdWVzdBIbCgRpdGVtGAEgASgLMg0uaXRlbS52MS5JdGVtIjEKElVwZGF0ZUl0ZW1SZXNwb25zZRIbCgRpdGVtGAEgASgLMg0uaXRlbS52MS5JdGVtIh8KEURlbGV0ZUl0ZW1SZXF1ZXN0EgoKAmlkGAEgASgNIhQKEkRlbGV0ZUl0ZW1SZXNwb25zZTLrAgoLSXRlbVNlcnZpY2USPgoHR2V0SXRlbRIXLml0ZW0udjEuR2V0SXRlbVJlcXVlc3QaGC5pdGVtLnYxLkdldEl0ZW1SZXNwb25zZSIAEkEKCEdldEl0ZW1zEhguaXRlbS52MS5HZXRJdGVtc1JlcXVlc3QaGS5pdGVtLnYxLkdldEl0ZW1zUmVzcG9uc2UiABJHCgpDcmVhdGVJdGVtEhouaXRlbS52MS5DcmVhdGVJdGVtUmVxdWVzdBobLml0ZW0udjEuQ3JlYXRlSXRlbVJlc3BvbnNlIgASRwoKVXBkYXRlSXRlbRIaLml0ZW0udjEuVXBkYXRlSXRlbVJlcXVlc3QaGy5pdGVtLnYxLlVwZGF0ZUl0ZW1SZXNwb25zZSIAEkcKCkRlbGV0ZUl0ZW0SGi5pdGVtLnYxLkRlbGV0ZUl0ZW1SZXF1ZXN0GhsuaXRlbS52MS5EZWxldGVJdGVtUmVzcG9uc2UiAEKdAQoLY29tLml0ZW0udjFCCUl0ZW1Qcm90b1ABWkZnaXRodWIuY29tL3Nwb3RkZW1vNC90cmV2c3RhY2svc2VydmVyL2ludGVybmFsL3NlcnZpY2VzL2l0ZW0vdjE7aXRlbXYxogIDSVhYqgIHSXRlbS5WMcoCB0l0ZW1cVjHiAhNJdGVtXFYxXEdQQk1ldGFkYXRh6gIISXRlbTo6VjFiBnByb3RvMw", [file_google_protobuf_timestamp]); /** * @generated from message item.v1.Item */ -export type Item = Message<'item.v1.Item'> & { - /** - * @generated from field: optional uint32 id = 1; - */ - id?: number; +export type Item = Message<"item.v1.Item"> & { + /** + * @generated from field: optional uint32 id = 1; + */ + id?: number; - /** - * @generated from field: string name = 2; - */ - name: string; + /** + * @generated from field: string name = 2; + */ + name: string; - /** - * @generated from field: string description = 3; - */ - description: string; + /** + * @generated from field: string description = 3; + */ + description: string; - /** - * @generated from field: float price = 4; - */ - price: number; + /** + * @generated from field: float price = 4; + */ + price: number; - /** - * @generated from field: uint32 quantity = 5; - */ - quantity: number; + /** + * @generated from field: uint32 quantity = 5; + */ + quantity: number; - /** - * @generated from field: optional google.protobuf.Timestamp added = 6; - */ - added?: Timestamp; + /** + * @generated from field: optional google.protobuf.Timestamp added = 6; + */ + added?: Timestamp; }; /** * Describes the message item.v1.Item. * Use `create(ItemSchema)` to create a new message. */ -export const ItemSchema: GenMessage = /*@__PURE__*/ messageDesc(file_item_v1_item, 0); +export const ItemSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_item_v1_item, 0); /** * @generated from message item.v1.GetItemRequest */ -export type GetItemRequest = Message<'item.v1.GetItemRequest'> & { - /** - * @generated from field: uint32 id = 1; - */ - id: number; +export type GetItemRequest = Message<"item.v1.GetItemRequest"> & { + /** + * @generated from field: uint32 id = 1; + */ + id: number; }; /** * Describes the message item.v1.GetItemRequest. * Use `create(GetItemRequestSchema)` to create a new message. */ -export const GetItemRequestSchema: GenMessage = - /*@__PURE__*/ - messageDesc(file_item_v1_item, 1); +export const GetItemRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_item_v1_item, 1); /** * @generated from message item.v1.GetItemResponse */ -export type GetItemResponse = Message<'item.v1.GetItemResponse'> & { - /** - * @generated from field: item.v1.Item item = 1; - */ - item?: Item; +export type GetItemResponse = Message<"item.v1.GetItemResponse"> & { + /** + * @generated from field: item.v1.Item item = 1; + */ + item?: Item; }; /** * Describes the message item.v1.GetItemResponse. * Use `create(GetItemResponseSchema)` to create a new message. */ -export const GetItemResponseSchema: GenMessage = - /*@__PURE__*/ - messageDesc(file_item_v1_item, 2); +export const GetItemResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_item_v1_item, 2); /** * @generated from message item.v1.GetItemsRequest */ -export type GetItemsRequest = Message<'item.v1.GetItemsRequest'> & { - /** - * @generated from field: optional google.protobuf.Timestamp start = 1; - */ - start?: Timestamp; +export type GetItemsRequest = Message<"item.v1.GetItemsRequest"> & { + /** + * @generated from field: optional google.protobuf.Timestamp start = 1; + */ + start?: Timestamp; - /** - * @generated from field: optional google.protobuf.Timestamp end = 2; - */ - end?: Timestamp; + /** + * @generated from field: optional google.protobuf.Timestamp end = 2; + */ + end?: Timestamp; - /** - * @generated from field: optional string filter = 3; - */ - filter?: string; + /** + * @generated from field: optional string filter = 3; + */ + filter?: string; - /** - * @generated from field: optional uint32 limit = 4; - */ - limit?: number; + /** + * @generated from field: optional uint32 limit = 4; + */ + limit?: number; - /** - * @generated from field: optional uint32 offset = 5; - */ - offset?: number; + /** + * @generated from field: optional uint32 offset = 5; + */ + offset?: number; }; /** * Describes the message item.v1.GetItemsRequest. * Use `create(GetItemsRequestSchema)` to create a new message. */ -export const GetItemsRequestSchema: GenMessage = - /*@__PURE__*/ - messageDesc(file_item_v1_item, 3); +export const GetItemsRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_item_v1_item, 3); /** * @generated from message item.v1.GetItemsResponse */ -export type GetItemsResponse = Message<'item.v1.GetItemsResponse'> & { - /** - * @generated from field: repeated item.v1.Item items = 1; - */ - items: Item[]; +export type GetItemsResponse = Message<"item.v1.GetItemsResponse"> & { + /** + * @generated from field: repeated item.v1.Item items = 1; + */ + items: Item[]; - /** - * @generated from field: uint64 count = 2; - */ - count: bigint; + /** + * @generated from field: uint64 count = 2; + */ + count: bigint; }; /** * Describes the message item.v1.GetItemsResponse. * Use `create(GetItemsResponseSchema)` to create a new message. */ -export const GetItemsResponseSchema: GenMessage = - /*@__PURE__*/ - messageDesc(file_item_v1_item, 4); +export const GetItemsResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_item_v1_item, 4); /** * @generated from message item.v1.CreateItemRequest */ -export type CreateItemRequest = Message<'item.v1.CreateItemRequest'> & { - /** - * @generated from field: item.v1.Item item = 1; - */ - item?: Item; +export type CreateItemRequest = Message<"item.v1.CreateItemRequest"> & { + /** + * @generated from field: item.v1.Item item = 1; + */ + item?: Item; }; /** * Describes the message item.v1.CreateItemRequest. * Use `create(CreateItemRequestSchema)` to create a new message. */ -export const CreateItemRequestSchema: GenMessage = - /*@__PURE__*/ - messageDesc(file_item_v1_item, 5); +export const CreateItemRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_item_v1_item, 5); /** * @generated from message item.v1.CreateItemResponse */ -export type CreateItemResponse = Message<'item.v1.CreateItemResponse'> & { - /** - * @generated from field: item.v1.Item item = 1; - */ - item?: Item; +export type CreateItemResponse = Message<"item.v1.CreateItemResponse"> & { + /** + * @generated from field: item.v1.Item item = 1; + */ + item?: Item; }; /** * Describes the message item.v1.CreateItemResponse. * Use `create(CreateItemResponseSchema)` to create a new message. */ -export const CreateItemResponseSchema: GenMessage = - /*@__PURE__*/ - messageDesc(file_item_v1_item, 6); +export const CreateItemResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_item_v1_item, 6); /** * @generated from message item.v1.UpdateItemRequest */ -export type UpdateItemRequest = Message<'item.v1.UpdateItemRequest'> & { - /** - * @generated from field: item.v1.Item item = 1; - */ - item?: Item; +export type UpdateItemRequest = Message<"item.v1.UpdateItemRequest"> & { + /** + * @generated from field: item.v1.Item item = 1; + */ + item?: Item; }; /** * Describes the message item.v1.UpdateItemRequest. * Use `create(UpdateItemRequestSchema)` to create a new message. */ -export const UpdateItemRequestSchema: GenMessage = - /*@__PURE__*/ - messageDesc(file_item_v1_item, 7); +export const UpdateItemRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_item_v1_item, 7); /** * @generated from message item.v1.UpdateItemResponse */ -export type UpdateItemResponse = Message<'item.v1.UpdateItemResponse'> & { - /** - * @generated from field: item.v1.Item item = 1; - */ - item?: Item; +export type UpdateItemResponse = Message<"item.v1.UpdateItemResponse"> & { + /** + * @generated from field: item.v1.Item item = 1; + */ + item?: Item; }; /** * Describes the message item.v1.UpdateItemResponse. * Use `create(UpdateItemResponseSchema)` to create a new message. */ -export const UpdateItemResponseSchema: GenMessage = - /*@__PURE__*/ - messageDesc(file_item_v1_item, 8); +export const UpdateItemResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_item_v1_item, 8); /** * @generated from message item.v1.DeleteItemRequest */ -export type DeleteItemRequest = Message<'item.v1.DeleteItemRequest'> & { - /** - * @generated from field: uint32 id = 1; - */ - id: number; +export type DeleteItemRequest = Message<"item.v1.DeleteItemRequest"> & { + /** + * @generated from field: uint32 id = 1; + */ + id: number; }; /** * Describes the message item.v1.DeleteItemRequest. * Use `create(DeleteItemRequestSchema)` to create a new message. */ -export const DeleteItemRequestSchema: GenMessage = - /*@__PURE__*/ - messageDesc(file_item_v1_item, 9); +export const DeleteItemRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_item_v1_item, 9); /** * @generated from message item.v1.DeleteItemResponse */ -export type DeleteItemResponse = Message<'item.v1.DeleteItemResponse'> & {}; +export type DeleteItemResponse = Message<"item.v1.DeleteItemResponse"> & { +}; /** * Describes the message item.v1.DeleteItemResponse. * Use `create(DeleteItemResponseSchema)` to create a new message. */ -export const DeleteItemResponseSchema: GenMessage = - /*@__PURE__*/ - messageDesc(file_item_v1_item, 10); +export const DeleteItemResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_item_v1_item, 10); /** * @generated from service item.v1.ItemService */ export const ItemService: GenService<{ - /** - * @generated from rpc item.v1.ItemService.GetItem - */ - getItem: { - methodKind: 'unary'; - input: typeof GetItemRequestSchema; - output: typeof GetItemResponseSchema; - }; - /** - * @generated from rpc item.v1.ItemService.GetItems - */ - getItems: { - methodKind: 'unary'; - input: typeof GetItemsRequestSchema; - output: typeof GetItemsResponseSchema; - }; - /** - * @generated from rpc item.v1.ItemService.CreateItem - */ - createItem: { - methodKind: 'unary'; - input: typeof CreateItemRequestSchema; - output: typeof CreateItemResponseSchema; - }; - /** - * @generated from rpc item.v1.ItemService.UpdateItem - */ - updateItem: { - methodKind: 'unary'; - input: typeof UpdateItemRequestSchema; - output: typeof UpdateItemResponseSchema; - }; - /** - * @generated from rpc item.v1.ItemService.DeleteItem - */ - deleteItem: { - methodKind: 'unary'; - input: typeof DeleteItemRequestSchema; - output: typeof DeleteItemResponseSchema; - }; -}> = /*@__PURE__*/ serviceDesc(file_item_v1_item, 0); + /** + * @generated from rpc item.v1.ItemService.GetItem + */ + getItem: { + methodKind: "unary"; + input: typeof GetItemRequestSchema; + output: typeof GetItemResponseSchema; + }, + /** + * @generated from rpc item.v1.ItemService.GetItems + */ + getItems: { + methodKind: "unary"; + input: typeof GetItemsRequestSchema; + output: typeof GetItemsResponseSchema; + }, + /** + * @generated from rpc item.v1.ItemService.CreateItem + */ + createItem: { + methodKind: "unary"; + input: typeof CreateItemRequestSchema; + output: typeof CreateItemResponseSchema; + }, + /** + * @generated from rpc item.v1.ItemService.UpdateItem + */ + updateItem: { + methodKind: "unary"; + input: typeof UpdateItemRequestSchema; + output: typeof UpdateItemResponseSchema; + }, + /** + * @generated from rpc item.v1.ItemService.DeleteItem + */ + deleteItem: { + methodKind: "unary"; + input: typeof DeleteItemRequestSchema; + output: typeof DeleteItemResponseSchema; + }, +}> = /*@__PURE__*/ + serviceDesc(file_item_v1_item, 0); + diff --git a/client/src/lib/services/user/v1/auth_pb.ts b/client/src/lib/services/user/v1/auth_pb.ts index 3d70cff..5951b25 100644 --- a/client/src/lib/services/user/v1/auth_pb.ts +++ b/client/src/lib/services/user/v1/auth_pb.ts @@ -2,153 +2,243 @@ // @generated from file user/v1/auth.proto (package user.v1, syntax proto3) /* eslint-disable */ -import type { GenFile, GenMessage, GenService } from '@bufbuild/protobuf/codegenv1'; -import { fileDesc, messageDesc, serviceDesc } from '@bufbuild/protobuf/codegenv1'; -import type { Message } from '@bufbuild/protobuf'; +import type { GenFile, GenMessage, GenService } from "@bufbuild/protobuf/codegenv1"; +import { fileDesc, messageDesc, serviceDesc } from "@bufbuild/protobuf/codegenv1"; +import type { Message } from "@bufbuild/protobuf"; /** * Describes the file user/v1/auth.proto. */ -export const file_user_v1_auth: GenFile = - /*@__PURE__*/ - fileDesc( - 'ChJ1c2VyL3YxL2F1dGgucHJvdG8SB3VzZXIudjEiMgoMTG9naW5SZXF1ZXN0EhAKCHVzZXJuYW1lGAEgASgJEhAKCHBhc3N3b3JkGAIgASgJIh4KDUxvZ2luUmVzcG9uc2USDQoFdG9rZW4YASABKAkiTQoNU2lnblVwUmVxdWVzdBIQCgh1c2VybmFtZRgBIAEoCRIQCghwYXNzd29yZBgCIAEoCRIYChBjb25maXJtX3Bhc3N3b3JkGAMgASgJIhAKDlNpZ25VcFJlc3BvbnNlIg8KDUxvZ291dFJlcXVlc3QiEAoOTG9nb3V0UmVzcG9uc2UywQEKC0F1dGhTZXJ2aWNlEjgKBUxvZ2luEhUudXNlci52MS5Mb2dpblJlcXVlc3QaFi51c2VyLnYxLkxvZ2luUmVzcG9uc2UiABI7CgZTaWduVXASFi51c2VyLnYxLlNpZ25VcFJlcXVlc3QaFy51c2VyLnYxLlNpZ25VcFJlc3BvbnNlIgASOwoGTG9nb3V0EhYudXNlci52MS5Mb2dvdXRSZXF1ZXN0GhcudXNlci52MS5Mb2dvdXRSZXNwb25zZSIAQp0BCgtjb20udXNlci52MUIJQXV0aFByb3RvUAFaRmdpdGh1Yi5jb20vc3BvdGRlbW80L3RyZXZzdGFjay9zZXJ2ZXIvaW50ZXJuYWwvc2VydmljZXMvdXNlci92MTt1c2VydjGiAgNVWFiqAgdVc2VyLlYxygIHVXNlclxWMeICE1VzZXJcVjFcR1BCTWV0YWRhdGHqAghVc2VyOjpWMWIGcHJvdG8z' - ); +export const file_user_v1_auth: GenFile = /*@__PURE__*/ + fileDesc("ChJ1c2VyL3YxL2F1dGgucHJvdG8SB3VzZXIudjEiMgoMTG9naW5SZXF1ZXN0EhAKCHVzZXJuYW1lGAEgASgJEhAKCHBhc3N3b3JkGAIgASgJIh4KDUxvZ2luUmVzcG9uc2USDQoFdG9rZW4YASABKAkiTQoNU2lnblVwUmVxdWVzdBIQCgh1c2VybmFtZRgBIAEoCRIQCghwYXNzd29yZBgCIAEoCRIYChBjb25maXJtX3Bhc3N3b3JkGAMgASgJIhAKDlNpZ25VcFJlc3BvbnNlIg8KDUxvZ291dFJlcXVlc3QiEAoOTG9nb3V0UmVzcG9uc2UiKAoUR2V0UGFzc2tleUlEc1JlcXVlc3QSEAoIdXNlcm5hbWUYASABKAkiLAoVR2V0UGFzc2tleUlEc1Jlc3BvbnNlEhMKC3Bhc3NrZXlfaWRzGAEgAygJIkcKE1Bhc3NrZXlMb2dpblJlcXVlc3QSCgoCaWQYASABKAkSEQoJc2lnbmF0dXJlGAIgASgMEhEKCWFsZ29yaXRobRgDIAEoBSIlChRQYXNza2V5TG9naW5SZXNwb25zZRINCgV0b2tlbhgBIAEoCTLiAgoLQXV0aFNlcnZpY2USOAoFTG9naW4SFS51c2VyLnYxLkxvZ2luUmVxdWVzdBoWLnVzZXIudjEuTG9naW5SZXNwb25zZSIAEjsKBlNpZ25VcBIWLnVzZXIudjEuU2lnblVwUmVxdWVzdBoXLnVzZXIudjEuU2lnblVwUmVzcG9uc2UiABI7CgZMb2dvdXQSFi51c2VyLnYxLkxvZ291dFJlcXVlc3QaFy51c2VyLnYxLkxvZ291dFJlc3BvbnNlIgASUAoNR2V0UGFzc2tleUlEcxIdLnVzZXIudjEuR2V0UGFzc2tleUlEc1JlcXVlc3QaHi51c2VyLnYxLkdldFBhc3NrZXlJRHNSZXNwb25zZSIAEk0KDFBhc3NrZXlMb2dpbhIcLnVzZXIudjEuUGFzc2tleUxvZ2luUmVxdWVzdBodLnVzZXIudjEuUGFzc2tleUxvZ2luUmVzcG9uc2UiAEKdAQoLY29tLnVzZXIudjFCCUF1dGhQcm90b1ABWkZnaXRodWIuY29tL3Nwb3RkZW1vNC90cmV2c3RhY2svc2VydmVyL2ludGVybmFsL3NlcnZpY2VzL3VzZXIvdjE7dXNlcnYxogIDVVhYqgIHVXNlci5WMcoCB1VzZXJcVjHiAhNVc2VyXFYxXEdQQk1ldGFkYXRh6gIIVXNlcjo6VjFiBnByb3RvMw"); /** * @generated from message user.v1.LoginRequest */ -export type LoginRequest = Message<'user.v1.LoginRequest'> & { - /** - * @generated from field: string username = 1; - */ - username: string; +export type LoginRequest = Message<"user.v1.LoginRequest"> & { + /** + * @generated from field: string username = 1; + */ + username: string; - /** - * @generated from field: string password = 2; - */ - password: string; + /** + * @generated from field: string password = 2; + */ + password: string; }; /** * Describes the message user.v1.LoginRequest. * Use `create(LoginRequestSchema)` to create a new message. */ -export const LoginRequestSchema: GenMessage = - /*@__PURE__*/ - messageDesc(file_user_v1_auth, 0); +export const LoginRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_user_v1_auth, 0); /** * @generated from message user.v1.LoginResponse */ -export type LoginResponse = Message<'user.v1.LoginResponse'> & { - /** - * @generated from field: string token = 1; - */ - token: string; +export type LoginResponse = Message<"user.v1.LoginResponse"> & { + /** + * @generated from field: string token = 1; + */ + token: string; }; /** * Describes the message user.v1.LoginResponse. * Use `create(LoginResponseSchema)` to create a new message. */ -export const LoginResponseSchema: GenMessage = - /*@__PURE__*/ - messageDesc(file_user_v1_auth, 1); +export const LoginResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_user_v1_auth, 1); /** * @generated from message user.v1.SignUpRequest */ -export type SignUpRequest = Message<'user.v1.SignUpRequest'> & { - /** - * @generated from field: string username = 1; - */ - username: string; +export type SignUpRequest = Message<"user.v1.SignUpRequest"> & { + /** + * @generated from field: string username = 1; + */ + username: string; - /** - * @generated from field: string password = 2; - */ - password: string; + /** + * @generated from field: string password = 2; + */ + password: string; - /** - * @generated from field: string confirm_password = 3; - */ - confirmPassword: string; + /** + * @generated from field: string confirm_password = 3; + */ + confirmPassword: string; }; /** * Describes the message user.v1.SignUpRequest. * Use `create(SignUpRequestSchema)` to create a new message. */ -export const SignUpRequestSchema: GenMessage = - /*@__PURE__*/ - messageDesc(file_user_v1_auth, 2); +export const SignUpRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_user_v1_auth, 2); /** * @generated from message user.v1.SignUpResponse */ -export type SignUpResponse = Message<'user.v1.SignUpResponse'> & {}; +export type SignUpResponse = Message<"user.v1.SignUpResponse"> & { +}; /** * Describes the message user.v1.SignUpResponse. * Use `create(SignUpResponseSchema)` to create a new message. */ -export const SignUpResponseSchema: GenMessage = - /*@__PURE__*/ - messageDesc(file_user_v1_auth, 3); +export const SignUpResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_user_v1_auth, 3); /** * @generated from message user.v1.LogoutRequest */ -export type LogoutRequest = Message<'user.v1.LogoutRequest'> & {}; +export type LogoutRequest = Message<"user.v1.LogoutRequest"> & { +}; /** * Describes the message user.v1.LogoutRequest. * Use `create(LogoutRequestSchema)` to create a new message. */ -export const LogoutRequestSchema: GenMessage = - /*@__PURE__*/ - messageDesc(file_user_v1_auth, 4); +export const LogoutRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_user_v1_auth, 4); /** * @generated from message user.v1.LogoutResponse */ -export type LogoutResponse = Message<'user.v1.LogoutResponse'> & {}; +export type LogoutResponse = Message<"user.v1.LogoutResponse"> & { +}; /** * Describes the message user.v1.LogoutResponse. * Use `create(LogoutResponseSchema)` to create a new message. */ -export const LogoutResponseSchema: GenMessage = - /*@__PURE__*/ - messageDesc(file_user_v1_auth, 5); +export const LogoutResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_user_v1_auth, 5); + +/** + * @generated from message user.v1.GetPasskeyIDsRequest + */ +export type GetPasskeyIDsRequest = Message<"user.v1.GetPasskeyIDsRequest"> & { + /** + * @generated from field: string username = 1; + */ + username: string; +}; + +/** + * Describes the message user.v1.GetPasskeyIDsRequest. + * Use `create(GetPasskeyIDsRequestSchema)` to create a new message. + */ +export const GetPasskeyIDsRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_user_v1_auth, 6); + +/** + * @generated from message user.v1.GetPasskeyIDsResponse + */ +export type GetPasskeyIDsResponse = Message<"user.v1.GetPasskeyIDsResponse"> & { + /** + * @generated from field: repeated string passkey_ids = 1; + */ + passkeyIds: string[]; +}; + +/** + * Describes the message user.v1.GetPasskeyIDsResponse. + * Use `create(GetPasskeyIDsResponseSchema)` to create a new message. + */ +export const GetPasskeyIDsResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_user_v1_auth, 7); + +/** + * @generated from message user.v1.PasskeyLoginRequest + */ +export type PasskeyLoginRequest = Message<"user.v1.PasskeyLoginRequest"> & { + /** + * @generated from field: string id = 1; + */ + id: string; + + /** + * @generated from field: bytes signature = 2; + */ + signature: Uint8Array; + + /** + * @generated from field: int32 algorithm = 3; + */ + algorithm: number; +}; + +/** + * Describes the message user.v1.PasskeyLoginRequest. + * Use `create(PasskeyLoginRequestSchema)` to create a new message. + */ +export const PasskeyLoginRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_user_v1_auth, 8); + +/** + * @generated from message user.v1.PasskeyLoginResponse + */ +export type PasskeyLoginResponse = Message<"user.v1.PasskeyLoginResponse"> & { + /** + * @generated from field: string token = 1; + */ + token: string; +}; + +/** + * Describes the message user.v1.PasskeyLoginResponse. + * Use `create(PasskeyLoginResponseSchema)` to create a new message. + */ +export const PasskeyLoginResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_user_v1_auth, 9); /** * @generated from service user.v1.AuthService */ export const AuthService: GenService<{ - /** - * @generated from rpc user.v1.AuthService.Login - */ - login: { - methodKind: 'unary'; - input: typeof LoginRequestSchema; - output: typeof LoginResponseSchema; - }; - /** - * @generated from rpc user.v1.AuthService.SignUp - */ - signUp: { - methodKind: 'unary'; - input: typeof SignUpRequestSchema; - output: typeof SignUpResponseSchema; - }; - /** - * @generated from rpc user.v1.AuthService.Logout - */ - logout: { - methodKind: 'unary'; - input: typeof LogoutRequestSchema; - output: typeof LogoutResponseSchema; - }; -}> = /*@__PURE__*/ serviceDesc(file_user_v1_auth, 0); + /** + * @generated from rpc user.v1.AuthService.Login + */ + login: { + methodKind: "unary"; + input: typeof LoginRequestSchema; + output: typeof LoginResponseSchema; + }, + /** + * @generated from rpc user.v1.AuthService.SignUp + */ + signUp: { + methodKind: "unary"; + input: typeof SignUpRequestSchema; + output: typeof SignUpResponseSchema; + }, + /** + * @generated from rpc user.v1.AuthService.Logout + */ + logout: { + methodKind: "unary"; + input: typeof LogoutRequestSchema; + output: typeof LogoutResponseSchema; + }, + /** + * @generated from rpc user.v1.AuthService.GetPasskeyIDs + */ + getPasskeyIDs: { + methodKind: "unary"; + input: typeof GetPasskeyIDsRequestSchema; + output: typeof GetPasskeyIDsResponseSchema; + }, + /** + * @generated from rpc user.v1.AuthService.PasskeyLogin + */ + passkeyLogin: { + methodKind: "unary"; + input: typeof PasskeyLoginRequestSchema; + output: typeof PasskeyLoginResponseSchema; + }, +}> = /*@__PURE__*/ + serviceDesc(file_user_v1_auth, 0); + diff --git a/client/src/lib/services/user/v1/user_pb.ts b/client/src/lib/services/user/v1/user_pb.ts index 6ead24e..3d2fc39 100644 --- a/client/src/lib/services/user/v1/user_pb.ts +++ b/client/src/lib/services/user/v1/user_pb.ts @@ -2,238 +2,274 @@ // @generated from file user/v1/user.proto (package user.v1, syntax proto3) /* eslint-disable */ -import type { GenFile, GenMessage, GenService } from '@bufbuild/protobuf/codegenv1'; -import { fileDesc, messageDesc, serviceDesc } from '@bufbuild/protobuf/codegenv1'; -import type { Message } from '@bufbuild/protobuf'; +import type { GenFile, GenMessage, GenService } from "@bufbuild/protobuf/codegenv1"; +import { fileDesc, messageDesc, serviceDesc } from "@bufbuild/protobuf/codegenv1"; +import type { Message } from "@bufbuild/protobuf"; /** * Describes the file user/v1/user.proto. */ -export const file_user_v1_user: GenFile = - /*@__PURE__*/ - fileDesc( - 'ChJ1c2VyL3YxL3VzZXIucHJvdG8SB3VzZXIudjEiVgoEVXNlchIKCgJpZBgBIAEoDRIQCgh1c2VybmFtZRgCIAEoCRIcCg9wcm9maWxlX3BpY3R1cmUYAyABKAlIAIgBAUISChBfcHJvZmlsZV9waWN0dXJlIhAKDkdldFVzZXJSZXF1ZXN0Ii4KD0dldFVzZXJSZXNwb25zZRIbCgR1c2VyGAEgASgLMg0udXNlci52MS5Vc2VyIl0KFVVwZGF0ZVBhc3N3b3JkUmVxdWVzdBIUCgxvbGRfcGFzc3dvcmQYASABKAkSFAoMbmV3X3Bhc3N3b3JkGAIgASgJEhgKEGNvbmZpcm1fcGFzc3dvcmQYAyABKAkiNQoWVXBkYXRlUGFzc3dvcmRSZXNwb25zZRIbCgR1c2VyGAEgASgLMg0udXNlci52MS5Vc2VyIj4KEEdldEFQSUtleVJlcXVlc3QSEAoIcGFzc3dvcmQYASABKAkSGAoQY29uZmlybV9wYXNzd29yZBgCIAEoCSIgChFHZXRBUElLZXlSZXNwb25zZRILCgNrZXkYASABKAkiPgobVXBkYXRlUHJvZmlsZVBpY3R1cmVSZXF1ZXN0EhEKCWZpbGVfbmFtZRgBIAEoCRIMCgRkYXRhGAIgASgMIjsKHFVwZGF0ZVByb2ZpbGVQaWN0dXJlUmVzcG9uc2USGwoEdXNlchgBIAEoCzINLnVzZXIudjEuVXNlcjLPAgoLVXNlclNlcnZpY2USPgoHR2V0VXNlchIXLnVzZXIudjEuR2V0VXNlclJlcXVlc3QaGC51c2VyLnYxLkdldFVzZXJSZXNwb25zZSIAElMKDlVwZGF0ZVBhc3N3b3JkEh4udXNlci52MS5VcGRhdGVQYXNzd29yZFJlcXVlc3QaHy51c2VyLnYxLlVwZGF0ZVBhc3N3b3JkUmVzcG9uc2UiABJECglHZXRBUElLZXkSGS51c2VyLnYxLkdldEFQSUtleVJlcXVlc3QaGi51c2VyLnYxLkdldEFQSUtleVJlc3BvbnNlIgASZQoUVXBkYXRlUHJvZmlsZVBpY3R1cmUSJC51c2VyLnYxLlVwZGF0ZVByb2ZpbGVQaWN0dXJlUmVxdWVzdBolLnVzZXIudjEuVXBkYXRlUHJvZmlsZVBpY3R1cmVSZXNwb25zZSIAQp0BCgtjb20udXNlci52MUIJVXNlclByb3RvUAFaRmdpdGh1Yi5jb20vc3BvdGRlbW80L3RyZXZzdGFjay9zZXJ2ZXIvaW50ZXJuYWwvc2VydmljZXMvdXNlci92MTt1c2VydjGiAgNVWFiqAgdVc2VyLlYxygIHVXNlclxWMeICE1VzZXJcVjFcR1BCTWV0YWRhdGHqAghVc2VyOjpWMWIGcHJvdG8z' - ); +export const file_user_v1_user: GenFile = /*@__PURE__*/ + fileDesc("ChJ1c2VyL3YxL3VzZXIucHJvdG8SB3VzZXIudjEiVgoEVXNlchIKCgJpZBgBIAEoDRIQCgh1c2VybmFtZRgCIAEoCRIcCg9wcm9maWxlX3BpY3R1cmUYAyABKAlIAIgBAUISChBfcHJvZmlsZV9waWN0dXJlIhAKDkdldFVzZXJSZXF1ZXN0Ii4KD0dldFVzZXJSZXNwb25zZRIbCgR1c2VyGAEgASgLMg0udXNlci52MS5Vc2VyIl0KFVVwZGF0ZVBhc3N3b3JkUmVxdWVzdBIUCgxvbGRfcGFzc3dvcmQYASABKAkSFAoMbmV3X3Bhc3N3b3JkGAIgASgJEhgKEGNvbmZpcm1fcGFzc3dvcmQYAyABKAkiNQoWVXBkYXRlUGFzc3dvcmRSZXNwb25zZRIbCgR1c2VyGAEgASgLMg0udXNlci52MS5Vc2VyIj4KEEdldEFQSUtleVJlcXVlc3QSEAoIcGFzc3dvcmQYASABKAkSGAoQY29uZmlybV9wYXNzd29yZBgCIAEoCSIgChFHZXRBUElLZXlSZXNwb25zZRILCgNrZXkYASABKAkiPgobVXBkYXRlUHJvZmlsZVBpY3R1cmVSZXF1ZXN0EhEKCWZpbGVfbmFtZRgBIAEoCRIMCgRkYXRhGAIgASgMIjsKHFVwZGF0ZVByb2ZpbGVQaWN0dXJlUmVzcG9uc2USGwoEdXNlchgBIAEoCzINLnVzZXIudjEuVXNlciI2ChRDcmVhdGVQYXNza2V5UmVxdWVzdBIKCgJpZBgBIAEoCRISCgpwdWJsaWNfa2V5GAIgASgMIhcKFUNyZWF0ZVBhc3NrZXlSZXNwb25zZTKhAwoLVXNlclNlcnZpY2USPgoHR2V0VXNlchIXLnVzZXIudjEuR2V0VXNlclJlcXVlc3QaGC51c2VyLnYxLkdldFVzZXJSZXNwb25zZSIAElMKDlVwZGF0ZVBhc3N3b3JkEh4udXNlci52MS5VcGRhdGVQYXNzd29yZFJlcXVlc3QaHy51c2VyLnYxLlVwZGF0ZVBhc3N3b3JkUmVzcG9uc2UiABJECglHZXRBUElLZXkSGS51c2VyLnYxLkdldEFQSUtleVJlcXVlc3QaGi51c2VyLnYxLkdldEFQSUtleVJlc3BvbnNlIgASZQoUVXBkYXRlUHJvZmlsZVBpY3R1cmUSJC51c2VyLnYxLlVwZGF0ZVByb2ZpbGVQaWN0dXJlUmVxdWVzdBolLnVzZXIudjEuVXBkYXRlUHJvZmlsZVBpY3R1cmVSZXNwb25zZSIAElAKDUNyZWF0ZVBhc3NrZXkSHS51c2VyLnYxLkNyZWF0ZVBhc3NrZXlSZXF1ZXN0Gh4udXNlci52MS5DcmVhdGVQYXNza2V5UmVzcG9uc2UiAEKdAQoLY29tLnVzZXIudjFCCVVzZXJQcm90b1ABWkZnaXRodWIuY29tL3Nwb3RkZW1vNC90cmV2c3RhY2svc2VydmVyL2ludGVybmFsL3NlcnZpY2VzL3VzZXIvdjE7dXNlcnYxogIDVVhYqgIHVXNlci5WMcoCB1VzZXJcVjHiAhNVc2VyXFYxXEdQQk1ldGFkYXRh6gIIVXNlcjo6VjFiBnByb3RvMw"); /** * @generated from message user.v1.User */ -export type User = Message<'user.v1.User'> & { - /** - * @generated from field: uint32 id = 1; - */ - id: number; +export type User = Message<"user.v1.User"> & { + /** + * @generated from field: uint32 id = 1; + */ + id: number; - /** - * @generated from field: string username = 2; - */ - username: string; + /** + * @generated from field: string username = 2; + */ + username: string; - /** - * @generated from field: optional string profile_picture = 3; - */ - profilePicture?: string; + /** + * @generated from field: optional string profile_picture = 3; + */ + profilePicture?: string; }; /** * Describes the message user.v1.User. * Use `create(UserSchema)` to create a new message. */ -export const UserSchema: GenMessage = /*@__PURE__*/ messageDesc(file_user_v1_user, 0); +export const UserSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_user_v1_user, 0); /** * @generated from message user.v1.GetUserRequest */ -export type GetUserRequest = Message<'user.v1.GetUserRequest'> & {}; +export type GetUserRequest = Message<"user.v1.GetUserRequest"> & { +}; /** * Describes the message user.v1.GetUserRequest. * Use `create(GetUserRequestSchema)` to create a new message. */ -export const GetUserRequestSchema: GenMessage = - /*@__PURE__*/ - messageDesc(file_user_v1_user, 1); +export const GetUserRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_user_v1_user, 1); /** * @generated from message user.v1.GetUserResponse */ -export type GetUserResponse = Message<'user.v1.GetUserResponse'> & { - /** - * @generated from field: user.v1.User user = 1; - */ - user?: User; +export type GetUserResponse = Message<"user.v1.GetUserResponse"> & { + /** + * @generated from field: user.v1.User user = 1; + */ + user?: User; }; /** * Describes the message user.v1.GetUserResponse. * Use `create(GetUserResponseSchema)` to create a new message. */ -export const GetUserResponseSchema: GenMessage = - /*@__PURE__*/ - messageDesc(file_user_v1_user, 2); +export const GetUserResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_user_v1_user, 2); /** * @generated from message user.v1.UpdatePasswordRequest */ -export type UpdatePasswordRequest = Message<'user.v1.UpdatePasswordRequest'> & { - /** - * @generated from field: string old_password = 1; - */ - oldPassword: string; +export type UpdatePasswordRequest = Message<"user.v1.UpdatePasswordRequest"> & { + /** + * @generated from field: string old_password = 1; + */ + oldPassword: string; - /** - * @generated from field: string new_password = 2; - */ - newPassword: string; + /** + * @generated from field: string new_password = 2; + */ + newPassword: string; - /** - * @generated from field: string confirm_password = 3; - */ - confirmPassword: string; + /** + * @generated from field: string confirm_password = 3; + */ + confirmPassword: string; }; /** * Describes the message user.v1.UpdatePasswordRequest. * Use `create(UpdatePasswordRequestSchema)` to create a new message. */ -export const UpdatePasswordRequestSchema: GenMessage = - /*@__PURE__*/ - messageDesc(file_user_v1_user, 3); +export const UpdatePasswordRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_user_v1_user, 3); /** * @generated from message user.v1.UpdatePasswordResponse */ -export type UpdatePasswordResponse = Message<'user.v1.UpdatePasswordResponse'> & { - /** - * @generated from field: user.v1.User user = 1; - */ - user?: User; +export type UpdatePasswordResponse = Message<"user.v1.UpdatePasswordResponse"> & { + /** + * @generated from field: user.v1.User user = 1; + */ + user?: User; }; /** * Describes the message user.v1.UpdatePasswordResponse. * Use `create(UpdatePasswordResponseSchema)` to create a new message. */ -export const UpdatePasswordResponseSchema: GenMessage = - /*@__PURE__*/ - messageDesc(file_user_v1_user, 4); +export const UpdatePasswordResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_user_v1_user, 4); /** * @generated from message user.v1.GetAPIKeyRequest */ -export type GetAPIKeyRequest = Message<'user.v1.GetAPIKeyRequest'> & { - /** - * @generated from field: string password = 1; - */ - password: string; +export type GetAPIKeyRequest = Message<"user.v1.GetAPIKeyRequest"> & { + /** + * @generated from field: string password = 1; + */ + password: string; - /** - * @generated from field: string confirm_password = 2; - */ - confirmPassword: string; + /** + * @generated from field: string confirm_password = 2; + */ + confirmPassword: string; }; /** * Describes the message user.v1.GetAPIKeyRequest. * Use `create(GetAPIKeyRequestSchema)` to create a new message. */ -export const GetAPIKeyRequestSchema: GenMessage = - /*@__PURE__*/ - messageDesc(file_user_v1_user, 5); +export const GetAPIKeyRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_user_v1_user, 5); /** * @generated from message user.v1.GetAPIKeyResponse */ -export type GetAPIKeyResponse = Message<'user.v1.GetAPIKeyResponse'> & { - /** - * @generated from field: string key = 1; - */ - key: string; +export type GetAPIKeyResponse = Message<"user.v1.GetAPIKeyResponse"> & { + /** + * @generated from field: string key = 1; + */ + key: string; }; /** * Describes the message user.v1.GetAPIKeyResponse. * Use `create(GetAPIKeyResponseSchema)` to create a new message. */ -export const GetAPIKeyResponseSchema: GenMessage = - /*@__PURE__*/ - messageDesc(file_user_v1_user, 6); +export const GetAPIKeyResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_user_v1_user, 6); /** * @generated from message user.v1.UpdateProfilePictureRequest */ -export type UpdateProfilePictureRequest = Message<'user.v1.UpdateProfilePictureRequest'> & { - /** - * @generated from field: string file_name = 1; - */ - fileName: string; +export type UpdateProfilePictureRequest = Message<"user.v1.UpdateProfilePictureRequest"> & { + /** + * @generated from field: string file_name = 1; + */ + fileName: string; - /** - * @generated from field: bytes data = 2; - */ - data: Uint8Array; + /** + * @generated from field: bytes data = 2; + */ + data: Uint8Array; }; /** * Describes the message user.v1.UpdateProfilePictureRequest. * Use `create(UpdateProfilePictureRequestSchema)` to create a new message. */ -export const UpdateProfilePictureRequestSchema: GenMessage = - /*@__PURE__*/ - messageDesc(file_user_v1_user, 7); +export const UpdateProfilePictureRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_user_v1_user, 7); /** * @generated from message user.v1.UpdateProfilePictureResponse */ -export type UpdateProfilePictureResponse = Message<'user.v1.UpdateProfilePictureResponse'> & { - /** - * @generated from field: user.v1.User user = 1; - */ - user?: User; +export type UpdateProfilePictureResponse = Message<"user.v1.UpdateProfilePictureResponse"> & { + /** + * @generated from field: user.v1.User user = 1; + */ + user?: User; }; /** * Describes the message user.v1.UpdateProfilePictureResponse. * Use `create(UpdateProfilePictureResponseSchema)` to create a new message. */ -export const UpdateProfilePictureResponseSchema: GenMessage = - /*@__PURE__*/ - messageDesc(file_user_v1_user, 8); +export const UpdateProfilePictureResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_user_v1_user, 8); + +/** + * @generated from message user.v1.CreatePasskeyRequest + */ +export type CreatePasskeyRequest = Message<"user.v1.CreatePasskeyRequest"> & { + /** + * @generated from field: string id = 1; + */ + id: string; + + /** + * @generated from field: bytes public_key = 2; + */ + publicKey: Uint8Array; +}; + +/** + * Describes the message user.v1.CreatePasskeyRequest. + * Use `create(CreatePasskeyRequestSchema)` to create a new message. + */ +export const CreatePasskeyRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_user_v1_user, 9); + +/** + * @generated from message user.v1.CreatePasskeyResponse + */ +export type CreatePasskeyResponse = Message<"user.v1.CreatePasskeyResponse"> & { +}; + +/** + * Describes the message user.v1.CreatePasskeyResponse. + * Use `create(CreatePasskeyResponseSchema)` to create a new message. + */ +export const CreatePasskeyResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_user_v1_user, 10); /** * @generated from service user.v1.UserService */ export const UserService: GenService<{ - /** - * @generated from rpc user.v1.UserService.GetUser - */ - getUser: { - methodKind: 'unary'; - input: typeof GetUserRequestSchema; - output: typeof GetUserResponseSchema; - }; - /** - * @generated from rpc user.v1.UserService.UpdatePassword - */ - updatePassword: { - methodKind: 'unary'; - input: typeof UpdatePasswordRequestSchema; - output: typeof UpdatePasswordResponseSchema; - }; - /** - * @generated from rpc user.v1.UserService.GetAPIKey - */ - getAPIKey: { - methodKind: 'unary'; - input: typeof GetAPIKeyRequestSchema; - output: typeof GetAPIKeyResponseSchema; - }; - /** - * @generated from rpc user.v1.UserService.UpdateProfilePicture - */ - updateProfilePicture: { - methodKind: 'unary'; - input: typeof UpdateProfilePictureRequestSchema; - output: typeof UpdateProfilePictureResponseSchema; - }; -}> = /*@__PURE__*/ serviceDesc(file_user_v1_user, 0); + /** + * @generated from rpc user.v1.UserService.GetUser + */ + getUser: { + methodKind: "unary"; + input: typeof GetUserRequestSchema; + output: typeof GetUserResponseSchema; + }, + /** + * @generated from rpc user.v1.UserService.UpdatePassword + */ + updatePassword: { + methodKind: "unary"; + input: typeof UpdatePasswordRequestSchema; + output: typeof UpdatePasswordResponseSchema; + }, + /** + * @generated from rpc user.v1.UserService.GetAPIKey + */ + getAPIKey: { + methodKind: "unary"; + input: typeof GetAPIKeyRequestSchema; + output: typeof GetAPIKeyResponseSchema; + }, + /** + * @generated from rpc user.v1.UserService.UpdateProfilePicture + */ + updateProfilePicture: { + methodKind: "unary"; + input: typeof UpdateProfilePictureRequestSchema; + output: typeof UpdateProfilePictureResponseSchema; + }, + /** + * @generated from rpc user.v1.UserService.CreatePasskey + */ + createPasskey: { + methodKind: "unary"; + input: typeof CreatePasskeyRequestSchema; + output: typeof CreatePasskeyResponseSchema; + }, +}> = /*@__PURE__*/ + serviceDesc(file_user_v1_user, 0); + diff --git a/client/src/lib/ui/Button.svelte b/client/src/lib/ui/Button.svelte index 220ec74..5df9006 100644 --- a/client/src/lib/ui/Button.svelte +++ b/client/src/lib/ui/Button.svelte @@ -12,7 +12,7 @@ }: { className?: string; type?: 'submit' | 'reset' | 'button' | null; - onclick?: () => MouseEventHandler | null | undefined; + onclick?: () => void; children?: Snippet<[]>; } = $props(); @@ -23,7 +23,9 @@ 'bg-sky text-crust focus:outline-sky flex w-fit cursor-pointer items-center justify-center rounded p-2 px-4 text-sm font-medium transition-all hover:brightness-120 focus:outline-2 focus:outline-offset-1', className )} - {onclick} + onclick={() => { + onclick?.(); + }} > {@render children?.()} diff --git a/client/src/lib/webauthn.ts b/client/src/lib/webauthn.ts new file mode 100644 index 0000000..d3af675 --- /dev/null +++ b/client/src/lib/webauthn.ts @@ -0,0 +1,120 @@ +import { decode } from 'cbor2'; +import { page } from '$app/state'; + +interface CreateCredential extends Credential { + response: AuthenticatorAttestationResponse +} + +interface AttestationObject { + authData: Uint8Array, + fmt: string, + attStmt: any +} + +interface DecodedPublicKeyObject { + [key: number]: number | Uint8Array +} + +export async function createPasskey(username: string, userid: number, challenge: string) { + const challengeBuffer = Uint8Array.from(challenge, c => c.charCodeAt(0)); + const idBuffer = Uint8Array.from(userid.toString(), c => c.charCodeAt(0)); + + const credential = await navigator.credentials.create({ + publicKey: { + challenge: challengeBuffer, + rp: { id: page.url.hostname, name: "TrevStack" }, + user: { + id: idBuffer, + name: username, + displayName: username + }, + pubKeyCredParams: [ + { + type: 'public-key', + alg: -7 + }, + { + type: 'public-key', + alg: -257 + } + ], + timeout: 60000, + attestation: 'none' + } + }) as CreateCredential | null; + + if (!credential) { + throw new Error('Could not create passkey'); + } + + console.log(credential.id) + //console.log(credential.type); + + const utf8Decoder = new TextDecoder('utf-8'); + const decodedClientData = utf8Decoder.decode(credential.response.clientDataJSON) + const clientDataObj = JSON.parse(decodedClientData); + + console.log(clientDataObj); + + const attestationObject = new Uint8Array(credential.response.attestationObject) + const decodedAttestationObject = decode(attestationObject) as AttestationObject; + + const { authData } = decodedAttestationObject; + + // get the length of the credential ID + const dataView = new DataView(new ArrayBuffer(2)); + const idLenBytes = authData.slice(53, 55); + idLenBytes.forEach((value, index) => dataView.setUint8(index, value)); + const credentialIdLength = dataView.getUint16(0); + + // get the credential ID + const credentialId = authData.slice(55, 55 + credentialIdLength); + + // get the public key object + const publicKeyBytes = authData.slice(55 + credentialIdLength); + + console.log(publicKeyBytes); + + // the publicKeyBytes are encoded again as CBOR + const publicKeyObject = new Uint8Array(publicKeyBytes.buffer) + const decodedPublicKeyObject = decode(publicKeyObject) as DecodedPublicKeyObject; + + console.log(decodedPublicKeyObject); + + return { + id: credential.id, + publicKey: publicKeyBytes, + algorithm: decodedPublicKeyObject[3] + } +} + +interface GetCredential extends Credential { + response: AuthenticatorAssertionResponse +} + +export async function getPasskey(passkeyids: string[], challenge: string) { + const challengeBuffer = Uint8Array.from(challenge, c => c.charCodeAt(0)); + + const credential = await navigator.credentials.get({ + publicKey: { + challenge: challengeBuffer, + allowCredentials: passkeyids.map((passkeyid) => { + return { + id: Uint8Array.from(passkeyid, c => c.charCodeAt(0)), + type: 'public-key', + } + }), + timeout: 60000, + } + }) as GetCredential | null; + + if (!credential) { + throw new Error('Could not get passkey'); + } + + const signature = credential.response.signature; + + return { + signature + } +} \ No newline at end of file diff --git a/client/src/routes/(app)/settings/+page.svelte b/client/src/routes/(app)/settings/+page.svelte index c5e791b..c0639aa 100644 --- a/client/src/routes/(app)/settings/+page.svelte +++ b/client/src/routes/(app)/settings/+page.svelte @@ -7,6 +7,8 @@ import { Separator } from 'bits-ui'; import { toast } from 'svelte-sonner'; import { userState } from '$lib/sharedState.svelte'; + import { createPasskey } from '$lib/webauthn'; + import { page } from '$app/state'; import Avatar from '$lib/ui/Avatar.svelte'; let openChangeProfilePicture = $state(false); @@ -17,7 +19,7 @@
@@ -26,7 +28,7 @@ -
+
{#snippet trigger(props)} @@ -133,6 +135,16 @@ {/snippet} + +
user.v1.LoginRequest 2, // 1: user.v1.AuthService.SignUp:input_type -> user.v1.SignUpRequest 4, // 2: user.v1.AuthService.Logout:input_type -> user.v1.LogoutRequest - 1, // 3: user.v1.AuthService.Login:output_type -> user.v1.LoginResponse - 3, // 4: user.v1.AuthService.SignUp:output_type -> user.v1.SignUpResponse - 5, // 5: user.v1.AuthService.Logout:output_type -> user.v1.LogoutResponse - 3, // [3:6] is the sub-list for method output_type - 0, // [0:3] is the sub-list for method input_type + 6, // 3: user.v1.AuthService.GetPasskeyIDs:input_type -> user.v1.GetPasskeyIDsRequest + 8, // 4: user.v1.AuthService.PasskeyLogin:input_type -> user.v1.PasskeyLoginRequest + 1, // 5: user.v1.AuthService.Login:output_type -> user.v1.LoginResponse + 3, // 6: user.v1.AuthService.SignUp:output_type -> user.v1.SignUpResponse + 5, // 7: user.v1.AuthService.Logout:output_type -> user.v1.LogoutResponse + 7, // 8: user.v1.AuthService.GetPasskeyIDs:output_type -> user.v1.GetPasskeyIDsResponse + 9, // 9: user.v1.AuthService.PasskeyLogin:output_type -> user.v1.PasskeyLoginResponse + 5, // [5:10] is the sub-list for method output_type + 0, // [0:5] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name @@ -379,7 +605,7 @@ func file_user_v1_auth_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_user_v1_auth_proto_rawDesc), len(file_user_v1_auth_proto_rawDesc)), NumEnums: 0, - NumMessages: 6, + NumMessages: 10, NumExtensions: 0, NumServices: 1, }, diff --git a/server/internal/services/user/v1/user.pb.go b/server/internal/services/user/v1/user.pb.go index 846746c..d9b09ca 100644 --- a/server/internal/services/user/v1/user.pb.go +++ b/server/internal/services/user/v1/user.pb.go @@ -457,6 +457,94 @@ func (x *UpdateProfilePictureResponse) GetUser() *User { return nil } +type CreatePasskeyRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + PublicKey []byte `protobuf:"bytes,2,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreatePasskeyRequest) Reset() { + *x = CreatePasskeyRequest{} + mi := &file_user_v1_user_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreatePasskeyRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreatePasskeyRequest) ProtoMessage() {} + +func (x *CreatePasskeyRequest) ProtoReflect() protoreflect.Message { + mi := &file_user_v1_user_proto_msgTypes[9] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreatePasskeyRequest.ProtoReflect.Descriptor instead. +func (*CreatePasskeyRequest) Descriptor() ([]byte, []int) { + return file_user_v1_user_proto_rawDescGZIP(), []int{9} +} + +func (x *CreatePasskeyRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *CreatePasskeyRequest) GetPublicKey() []byte { + if x != nil { + return x.PublicKey + } + return nil +} + +type CreatePasskeyResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreatePasskeyResponse) Reset() { + *x = CreatePasskeyResponse{} + mi := &file_user_v1_user_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreatePasskeyResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreatePasskeyResponse) ProtoMessage() {} + +func (x *CreatePasskeyResponse) ProtoReflect() protoreflect.Message { + mi := &file_user_v1_user_proto_msgTypes[10] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreatePasskeyResponse.ProtoReflect.Descriptor instead. +func (*CreatePasskeyResponse) Descriptor() ([]byte, []int) { + return file_user_v1_user_proto_rawDescGZIP(), []int{10} +} + var File_user_v1_user_proto protoreflect.FileDescriptor var file_user_v1_user_proto_rawDesc = string([]byte{ @@ -503,7 +591,13 @@ var file_user_v1_user_proto_rawDesc = string([]byte{ 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x69, 0x63, 0x74, 0x75, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, - 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x32, 0xcf, 0x02, 0x0a, 0x0b, 0x55, 0x73, 0x65, + 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x45, 0x0a, 0x14, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x6b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, + 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x22, + 0x17, 0x0a, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x6b, 0x65, 0x79, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xa1, 0x03, 0x0a, 0x0b, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x3e, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x12, 0x17, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x75, @@ -524,18 +618,23 @@ var file_user_v1_user_proto_rawDesc = string([]byte{ 0x69, 0x6c, 0x65, 0x50, 0x69, 0x63, 0x74, 0x75, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x69, 0x63, 0x74, 0x75, 0x72, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x9d, 0x01, 0x0a, 0x0b, 0x63, - 0x6f, 0x6d, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x42, 0x09, 0x55, 0x73, 0x65, 0x72, - 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x46, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x70, 0x6f, 0x74, 0x64, 0x65, 0x6d, 0x6f, 0x34, 0x2f, 0x74, 0x72, - 0x65, 0x76, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2f, 0x69, - 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, - 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2f, 0x76, 0x31, 0x3b, 0x75, 0x73, 0x65, 0x72, 0x76, 0x31, 0xa2, - 0x02, 0x03, 0x55, 0x58, 0x58, 0xaa, 0x02, 0x07, 0x55, 0x73, 0x65, 0x72, 0x2e, 0x56, 0x31, 0xca, - 0x02, 0x07, 0x55, 0x73, 0x65, 0x72, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x13, 0x55, 0x73, 0x65, 0x72, - 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, - 0x02, 0x08, 0x55, 0x73, 0x65, 0x72, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x50, 0x0a, 0x0d, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x6b, 0x65, 0x79, 0x12, 0x1d, 0x2e, 0x75, 0x73, + 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, + 0x6b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x75, 0x73, 0x65, + 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x6b, + 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x9d, 0x01, 0x0a, + 0x0b, 0x63, 0x6f, 0x6d, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x42, 0x09, 0x55, 0x73, + 0x65, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x46, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x70, 0x6f, 0x74, 0x64, 0x65, 0x6d, 0x6f, 0x34, 0x2f, + 0x74, 0x72, 0x65, 0x76, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x73, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2f, 0x76, 0x31, 0x3b, 0x75, 0x73, 0x65, 0x72, 0x76, + 0x31, 0xa2, 0x02, 0x03, 0x55, 0x58, 0x58, 0xaa, 0x02, 0x07, 0x55, 0x73, 0x65, 0x72, 0x2e, 0x56, + 0x31, 0xca, 0x02, 0x07, 0x55, 0x73, 0x65, 0x72, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x13, 0x55, 0x73, + 0x65, 0x72, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0xea, 0x02, 0x08, 0x55, 0x73, 0x65, 0x72, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, }) var ( @@ -550,7 +649,7 @@ func file_user_v1_user_proto_rawDescGZIP() []byte { return file_user_v1_user_proto_rawDescData } -var file_user_v1_user_proto_msgTypes = make([]protoimpl.MessageInfo, 9) +var file_user_v1_user_proto_msgTypes = make([]protoimpl.MessageInfo, 11) var file_user_v1_user_proto_goTypes = []any{ (*User)(nil), // 0: user.v1.User (*GetUserRequest)(nil), // 1: user.v1.GetUserRequest @@ -561,24 +660,28 @@ var file_user_v1_user_proto_goTypes = []any{ (*GetAPIKeyResponse)(nil), // 6: user.v1.GetAPIKeyResponse (*UpdateProfilePictureRequest)(nil), // 7: user.v1.UpdateProfilePictureRequest (*UpdateProfilePictureResponse)(nil), // 8: user.v1.UpdateProfilePictureResponse + (*CreatePasskeyRequest)(nil), // 9: user.v1.CreatePasskeyRequest + (*CreatePasskeyResponse)(nil), // 10: user.v1.CreatePasskeyResponse } var file_user_v1_user_proto_depIdxs = []int32{ - 0, // 0: user.v1.GetUserResponse.user:type_name -> user.v1.User - 0, // 1: user.v1.UpdatePasswordResponse.user:type_name -> user.v1.User - 0, // 2: user.v1.UpdateProfilePictureResponse.user:type_name -> user.v1.User - 1, // 3: user.v1.UserService.GetUser:input_type -> user.v1.GetUserRequest - 3, // 4: user.v1.UserService.UpdatePassword:input_type -> user.v1.UpdatePasswordRequest - 5, // 5: user.v1.UserService.GetAPIKey:input_type -> user.v1.GetAPIKeyRequest - 7, // 6: user.v1.UserService.UpdateProfilePicture:input_type -> user.v1.UpdateProfilePictureRequest - 2, // 7: user.v1.UserService.GetUser:output_type -> user.v1.GetUserResponse - 4, // 8: user.v1.UserService.UpdatePassword:output_type -> user.v1.UpdatePasswordResponse - 6, // 9: user.v1.UserService.GetAPIKey:output_type -> user.v1.GetAPIKeyResponse - 8, // 10: user.v1.UserService.UpdateProfilePicture:output_type -> user.v1.UpdateProfilePictureResponse - 7, // [7:11] is the sub-list for method output_type - 3, // [3:7] is the sub-list for method input_type - 3, // [3:3] is the sub-list for extension type_name - 3, // [3:3] is the sub-list for extension extendee - 0, // [0:3] is the sub-list for field type_name + 0, // 0: user.v1.GetUserResponse.user:type_name -> user.v1.User + 0, // 1: user.v1.UpdatePasswordResponse.user:type_name -> user.v1.User + 0, // 2: user.v1.UpdateProfilePictureResponse.user:type_name -> user.v1.User + 1, // 3: user.v1.UserService.GetUser:input_type -> user.v1.GetUserRequest + 3, // 4: user.v1.UserService.UpdatePassword:input_type -> user.v1.UpdatePasswordRequest + 5, // 5: user.v1.UserService.GetAPIKey:input_type -> user.v1.GetAPIKeyRequest + 7, // 6: user.v1.UserService.UpdateProfilePicture:input_type -> user.v1.UpdateProfilePictureRequest + 9, // 7: user.v1.UserService.CreatePasskey:input_type -> user.v1.CreatePasskeyRequest + 2, // 8: user.v1.UserService.GetUser:output_type -> user.v1.GetUserResponse + 4, // 9: user.v1.UserService.UpdatePassword:output_type -> user.v1.UpdatePasswordResponse + 6, // 10: user.v1.UserService.GetAPIKey:output_type -> user.v1.GetAPIKeyResponse + 8, // 11: user.v1.UserService.UpdateProfilePicture:output_type -> user.v1.UpdateProfilePictureResponse + 10, // 12: user.v1.UserService.CreatePasskey:output_type -> user.v1.CreatePasskeyResponse + 8, // [8:13] is the sub-list for method output_type + 3, // [3:8] is the sub-list for method input_type + 3, // [3:3] is the sub-list for extension type_name + 3, // [3:3] is the sub-list for extension extendee + 0, // [0:3] is the sub-list for field type_name } func init() { file_user_v1_user_proto_init() } @@ -593,7 +696,7 @@ func file_user_v1_user_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_user_v1_user_proto_rawDesc), len(file_user_v1_user_proto_rawDesc)), NumEnums: 0, - NumMessages: 9, + NumMessages: 11, NumExtensions: 0, NumServices: 1, }, diff --git a/server/internal/services/user/v1/userv1connect/auth.connect.go b/server/internal/services/user/v1/userv1connect/auth.connect.go index 6d42016..a63593c 100644 --- a/server/internal/services/user/v1/userv1connect/auth.connect.go +++ b/server/internal/services/user/v1/userv1connect/auth.connect.go @@ -39,6 +39,12 @@ const ( AuthServiceSignUpProcedure = "/user.v1.AuthService/SignUp" // AuthServiceLogoutProcedure is the fully-qualified name of the AuthService's Logout RPC. AuthServiceLogoutProcedure = "/user.v1.AuthService/Logout" + // AuthServiceGetPasskeyIDsProcedure is the fully-qualified name of the AuthService's GetPasskeyIDs + // RPC. + AuthServiceGetPasskeyIDsProcedure = "/user.v1.AuthService/GetPasskeyIDs" + // AuthServicePasskeyLoginProcedure is the fully-qualified name of the AuthService's PasskeyLogin + // RPC. + AuthServicePasskeyLoginProcedure = "/user.v1.AuthService/PasskeyLogin" ) // AuthServiceClient is a client for the user.v1.AuthService service. @@ -46,6 +52,8 @@ type AuthServiceClient interface { Login(context.Context, *connect.Request[v1.LoginRequest]) (*connect.Response[v1.LoginResponse], error) SignUp(context.Context, *connect.Request[v1.SignUpRequest]) (*connect.Response[v1.SignUpResponse], error) Logout(context.Context, *connect.Request[v1.LogoutRequest]) (*connect.Response[v1.LogoutResponse], error) + GetPasskeyIDs(context.Context, *connect.Request[v1.GetPasskeyIDsRequest]) (*connect.Response[v1.GetPasskeyIDsResponse], error) + PasskeyLogin(context.Context, *connect.Request[v1.PasskeyLoginRequest]) (*connect.Response[v1.PasskeyLoginResponse], error) } // NewAuthServiceClient constructs a client for the user.v1.AuthService service. By default, it uses @@ -77,14 +85,28 @@ func NewAuthServiceClient(httpClient connect.HTTPClient, baseURL string, opts .. connect.WithSchema(authServiceMethods.ByName("Logout")), connect.WithClientOptions(opts...), ), + getPasskeyIDs: connect.NewClient[v1.GetPasskeyIDsRequest, v1.GetPasskeyIDsResponse]( + httpClient, + baseURL+AuthServiceGetPasskeyIDsProcedure, + connect.WithSchema(authServiceMethods.ByName("GetPasskeyIDs")), + connect.WithClientOptions(opts...), + ), + passkeyLogin: connect.NewClient[v1.PasskeyLoginRequest, v1.PasskeyLoginResponse]( + httpClient, + baseURL+AuthServicePasskeyLoginProcedure, + connect.WithSchema(authServiceMethods.ByName("PasskeyLogin")), + connect.WithClientOptions(opts...), + ), } } // authServiceClient implements AuthServiceClient. type authServiceClient struct { - login *connect.Client[v1.LoginRequest, v1.LoginResponse] - signUp *connect.Client[v1.SignUpRequest, v1.SignUpResponse] - logout *connect.Client[v1.LogoutRequest, v1.LogoutResponse] + login *connect.Client[v1.LoginRequest, v1.LoginResponse] + signUp *connect.Client[v1.SignUpRequest, v1.SignUpResponse] + logout *connect.Client[v1.LogoutRequest, v1.LogoutResponse] + getPasskeyIDs *connect.Client[v1.GetPasskeyIDsRequest, v1.GetPasskeyIDsResponse] + passkeyLogin *connect.Client[v1.PasskeyLoginRequest, v1.PasskeyLoginResponse] } // Login calls user.v1.AuthService.Login. @@ -102,11 +124,23 @@ func (c *authServiceClient) Logout(ctx context.Context, req *connect.Request[v1. return c.logout.CallUnary(ctx, req) } +// GetPasskeyIDs calls user.v1.AuthService.GetPasskeyIDs. +func (c *authServiceClient) GetPasskeyIDs(ctx context.Context, req *connect.Request[v1.GetPasskeyIDsRequest]) (*connect.Response[v1.GetPasskeyIDsResponse], error) { + return c.getPasskeyIDs.CallUnary(ctx, req) +} + +// PasskeyLogin calls user.v1.AuthService.PasskeyLogin. +func (c *authServiceClient) PasskeyLogin(ctx context.Context, req *connect.Request[v1.PasskeyLoginRequest]) (*connect.Response[v1.PasskeyLoginResponse], error) { + return c.passkeyLogin.CallUnary(ctx, req) +} + // AuthServiceHandler is an implementation of the user.v1.AuthService service. type AuthServiceHandler interface { Login(context.Context, *connect.Request[v1.LoginRequest]) (*connect.Response[v1.LoginResponse], error) SignUp(context.Context, *connect.Request[v1.SignUpRequest]) (*connect.Response[v1.SignUpResponse], error) Logout(context.Context, *connect.Request[v1.LogoutRequest]) (*connect.Response[v1.LogoutResponse], error) + GetPasskeyIDs(context.Context, *connect.Request[v1.GetPasskeyIDsRequest]) (*connect.Response[v1.GetPasskeyIDsResponse], error) + PasskeyLogin(context.Context, *connect.Request[v1.PasskeyLoginRequest]) (*connect.Response[v1.PasskeyLoginResponse], error) } // NewAuthServiceHandler builds an HTTP handler from the service implementation. It returns the path @@ -134,6 +168,18 @@ func NewAuthServiceHandler(svc AuthServiceHandler, opts ...connect.HandlerOption connect.WithSchema(authServiceMethods.ByName("Logout")), connect.WithHandlerOptions(opts...), ) + authServiceGetPasskeyIDsHandler := connect.NewUnaryHandler( + AuthServiceGetPasskeyIDsProcedure, + svc.GetPasskeyIDs, + connect.WithSchema(authServiceMethods.ByName("GetPasskeyIDs")), + connect.WithHandlerOptions(opts...), + ) + authServicePasskeyLoginHandler := connect.NewUnaryHandler( + AuthServicePasskeyLoginProcedure, + svc.PasskeyLogin, + connect.WithSchema(authServiceMethods.ByName("PasskeyLogin")), + connect.WithHandlerOptions(opts...), + ) return "/user.v1.AuthService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { case AuthServiceLoginProcedure: @@ -142,6 +188,10 @@ func NewAuthServiceHandler(svc AuthServiceHandler, opts ...connect.HandlerOption authServiceSignUpHandler.ServeHTTP(w, r) case AuthServiceLogoutProcedure: authServiceLogoutHandler.ServeHTTP(w, r) + case AuthServiceGetPasskeyIDsProcedure: + authServiceGetPasskeyIDsHandler.ServeHTTP(w, r) + case AuthServicePasskeyLoginProcedure: + authServicePasskeyLoginHandler.ServeHTTP(w, r) default: http.NotFound(w, r) } @@ -162,3 +212,11 @@ func (UnimplementedAuthServiceHandler) SignUp(context.Context, *connect.Request[ func (UnimplementedAuthServiceHandler) Logout(context.Context, *connect.Request[v1.LogoutRequest]) (*connect.Response[v1.LogoutResponse], error) { return nil, connect.NewError(connect.CodeUnimplemented, errors.New("user.v1.AuthService.Logout is not implemented")) } + +func (UnimplementedAuthServiceHandler) GetPasskeyIDs(context.Context, *connect.Request[v1.GetPasskeyIDsRequest]) (*connect.Response[v1.GetPasskeyIDsResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("user.v1.AuthService.GetPasskeyIDs is not implemented")) +} + +func (UnimplementedAuthServiceHandler) PasskeyLogin(context.Context, *connect.Request[v1.PasskeyLoginRequest]) (*connect.Response[v1.PasskeyLoginResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("user.v1.AuthService.PasskeyLogin is not implemented")) +} diff --git a/server/internal/services/user/v1/userv1connect/user.connect.go b/server/internal/services/user/v1/userv1connect/user.connect.go index 7f25e60..ded5cfe 100644 --- a/server/internal/services/user/v1/userv1connect/user.connect.go +++ b/server/internal/services/user/v1/userv1connect/user.connect.go @@ -43,6 +43,9 @@ const ( // UserServiceUpdateProfilePictureProcedure is the fully-qualified name of the UserService's // UpdateProfilePicture RPC. UserServiceUpdateProfilePictureProcedure = "/user.v1.UserService/UpdateProfilePicture" + // UserServiceCreatePasskeyProcedure is the fully-qualified name of the UserService's CreatePasskey + // RPC. + UserServiceCreatePasskeyProcedure = "/user.v1.UserService/CreatePasskey" ) // UserServiceClient is a client for the user.v1.UserService service. @@ -51,6 +54,7 @@ type UserServiceClient interface { UpdatePassword(context.Context, *connect.Request[v1.UpdatePasswordRequest]) (*connect.Response[v1.UpdatePasswordResponse], error) GetAPIKey(context.Context, *connect.Request[v1.GetAPIKeyRequest]) (*connect.Response[v1.GetAPIKeyResponse], error) UpdateProfilePicture(context.Context, *connect.Request[v1.UpdateProfilePictureRequest]) (*connect.Response[v1.UpdateProfilePictureResponse], error) + CreatePasskey(context.Context, *connect.Request[v1.CreatePasskeyRequest]) (*connect.Response[v1.CreatePasskeyResponse], error) } // NewUserServiceClient constructs a client for the user.v1.UserService service. By default, it uses @@ -88,6 +92,12 @@ func NewUserServiceClient(httpClient connect.HTTPClient, baseURL string, opts .. connect.WithSchema(userServiceMethods.ByName("UpdateProfilePicture")), connect.WithClientOptions(opts...), ), + createPasskey: connect.NewClient[v1.CreatePasskeyRequest, v1.CreatePasskeyResponse]( + httpClient, + baseURL+UserServiceCreatePasskeyProcedure, + connect.WithSchema(userServiceMethods.ByName("CreatePasskey")), + connect.WithClientOptions(opts...), + ), } } @@ -97,6 +107,7 @@ type userServiceClient struct { updatePassword *connect.Client[v1.UpdatePasswordRequest, v1.UpdatePasswordResponse] getAPIKey *connect.Client[v1.GetAPIKeyRequest, v1.GetAPIKeyResponse] updateProfilePicture *connect.Client[v1.UpdateProfilePictureRequest, v1.UpdateProfilePictureResponse] + createPasskey *connect.Client[v1.CreatePasskeyRequest, v1.CreatePasskeyResponse] } // GetUser calls user.v1.UserService.GetUser. @@ -119,12 +130,18 @@ func (c *userServiceClient) UpdateProfilePicture(ctx context.Context, req *conne return c.updateProfilePicture.CallUnary(ctx, req) } +// CreatePasskey calls user.v1.UserService.CreatePasskey. +func (c *userServiceClient) CreatePasskey(ctx context.Context, req *connect.Request[v1.CreatePasskeyRequest]) (*connect.Response[v1.CreatePasskeyResponse], error) { + return c.createPasskey.CallUnary(ctx, req) +} + // UserServiceHandler is an implementation of the user.v1.UserService service. type UserServiceHandler interface { GetUser(context.Context, *connect.Request[v1.GetUserRequest]) (*connect.Response[v1.GetUserResponse], error) UpdatePassword(context.Context, *connect.Request[v1.UpdatePasswordRequest]) (*connect.Response[v1.UpdatePasswordResponse], error) GetAPIKey(context.Context, *connect.Request[v1.GetAPIKeyRequest]) (*connect.Response[v1.GetAPIKeyResponse], error) UpdateProfilePicture(context.Context, *connect.Request[v1.UpdateProfilePictureRequest]) (*connect.Response[v1.UpdateProfilePictureResponse], error) + CreatePasskey(context.Context, *connect.Request[v1.CreatePasskeyRequest]) (*connect.Response[v1.CreatePasskeyResponse], error) } // NewUserServiceHandler builds an HTTP handler from the service implementation. It returns the path @@ -158,6 +175,12 @@ func NewUserServiceHandler(svc UserServiceHandler, opts ...connect.HandlerOption connect.WithSchema(userServiceMethods.ByName("UpdateProfilePicture")), connect.WithHandlerOptions(opts...), ) + userServiceCreatePasskeyHandler := connect.NewUnaryHandler( + UserServiceCreatePasskeyProcedure, + svc.CreatePasskey, + connect.WithSchema(userServiceMethods.ByName("CreatePasskey")), + connect.WithHandlerOptions(opts...), + ) return "/user.v1.UserService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { case UserServiceGetUserProcedure: @@ -168,6 +191,8 @@ func NewUserServiceHandler(svc UserServiceHandler, opts ...connect.HandlerOption userServiceGetAPIKeyHandler.ServeHTTP(w, r) case UserServiceUpdateProfilePictureProcedure: userServiceUpdateProfilePictureHandler.ServeHTTP(w, r) + case UserServiceCreatePasskeyProcedure: + userServiceCreatePasskeyHandler.ServeHTTP(w, r) default: http.NotFound(w, r) } @@ -192,3 +217,7 @@ func (UnimplementedUserServiceHandler) GetAPIKey(context.Context, *connect.Reque func (UnimplementedUserServiceHandler) UpdateProfilePicture(context.Context, *connect.Request[v1.UpdateProfilePictureRequest]) (*connect.Response[v1.UpdateProfilePictureResponse], error) { return nil, connect.NewError(connect.CodeUnimplemented, errors.New("user.v1.UserService.UpdateProfilePicture is not implemented")) } + +func (UnimplementedUserServiceHandler) CreatePasskey(context.Context, *connect.Request[v1.CreatePasskeyRequest]) (*connect.Response[v1.CreatePasskeyResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("user.v1.UserService.CreatePasskey is not implemented")) +}