@vue-skuilder/common-ui 0.2.19 → 0.2.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"UserLogin.vue.d.ts","sourceRoot":"","sources":["../../../src/components/auth/UserLogin.vue"],"names":[],"mappings":"AA2DA;AAsMA,UAAU,KAAK;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAkHD,iBAAS,cAAc;WAuPT,OAAO,IAA6B;;mCAVV,GAAG;;;;EAe1C;AAqBD,KAAK,oBAAoB,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AAC9D,QAAA,MAAM,eAAe;;;;;;;;;gBArYN,MAAM;6EA8YnB,CAAC;wBACkB,uBAAuB,CAAC,OAAO,eAAe,EAAE,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAAnG,wBAAoG;AAapG,KAAK,uBAAuB,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IACxC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KAEV,CAAA;CACD,CAAC"}
1
+ {"version":3,"file":"UserLogin.vue.d.ts","sourceRoot":"","sources":["../../../src/components/auth/UserLogin.vue"],"names":[],"mappings":"AA2DA;AAyNA,UAAU,KAAK;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAmID,iBAAS,cAAc;WAuPT,OAAO,IAA6B;;mCAVV,GAAG;;;;EAe1C;AAqBD,KAAK,oBAAoB,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AAC9D,QAAA,MAAM,eAAe;;;;;;;;;gBAtZN,MAAM;6EA+ZnB,CAAC;wBACkB,uBAAuB,CAAC,OAAO,eAAe,EAAE,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAAnG,wBAAoG;AAapG,KAAK,uBAAuB,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IACxC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KAEV,CAAA;CACD,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"UserRegistration.vue.d.ts","sourceRoot":"","sources":["../../../src/components/auth/UserRegistration.vue"],"names":[],"mappings":"AA8EA;AAAA,OAiSO,EAAmB,QAAQ,EAAE,MAAM,KAAK,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEnD,OAAO,EAAE,MAAM,EAAO,MAAM,sBAAsB,CAAC;;;cAU3B,QAAQ,CAAC,CAAC,OAAO,EAAE;YAAE,QAAQ,EAAE,MAAM,CAAA;SAAE,KAAK,IAAI,CAAC;;;;;;;;;;;;;;;;;;UAuBrD,eAAe,GAAG,IAAI;WACO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yBAmRrB,CAAC;;;;;;;;;;;;wBAqCf,CAAA;yBACd,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yBAtC2B,CAAC;;;;;;;;;;;;wBAqCf,CAAA;yBACd,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yBAtC2B,CAAC;;;;;;;;;;;;wBAqCf,CAAA;yBACd,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yBAtC2B,CAAC;;;;;;;;;;;;wBAqCf,CAAA;yBACd,CAAC;;;;;;;;;;;;;;;;;yBAhTgB,OAAO;;;;;qBASX,MAAM;2BAGA,MAAM;;;;;;;;cA7CT,QAAQ,CAAC,CAAC,OAAO,EAAE;YAAE,QAAQ,EAAE,MAAM,CAAA;SAAE,KAAK,IAAI,CAAC;;;;;;;AALzE,wBAqMG"}
1
+ {"version":3,"file":"UserRegistration.vue.d.ts","sourceRoot":"","sources":["../../../src/components/auth/UserRegistration.vue"],"names":[],"mappings":"AA8EA;AAAA,OAgSO,EAAmB,QAAQ,EAAE,MAAM,KAAK,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEnD,OAAO,EAAE,MAAM,EAAO,MAAM,sBAAsB,CAAC;;;cAU3B,QAAQ,CAAC,CAAC,OAAO,EAAE;YAAE,QAAQ,EAAE,MAAM,CAAA;SAAE,KAAK,IAAI,CAAC;;;;;;;;;;;;;;;;;;UAuBrD,eAAe,GAAG,IAAI;WACO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yBAqR5C,CAAC;;;;;;;;;;;;wBAqCJ,CAAC;yBAAuB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yBArCtB,CAAC;;;;;;;;;;;;wBAqCJ,CAAC;yBAAuB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yBArCtB,CAAC;;;;;;;;;;;;wBAqCJ,CAAC;yBAAuB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yBArCtB,CAAC;;;;;;;;;;;;wBAqCJ,CAAC;yBAAuB,CAAC;;;;;;;;;;;;;;;;;yBAjTV,OAAO;;;;;qBASX,MAAM;2BAGA,MAAM;;;;;;;;cA7CT,QAAQ,CAAC,CAAC,OAAO,EAAE;YAAE,QAAQ,EAAE,MAAM,CAAA;SAAE,KAAK,IAAI,CAAC;;;;;;;AALzE,wBAoMG"}
@@ -5,5 +5,5 @@ export { default as UserLoginAndRegistrationContainer } from './UserLoginAndRegi
5
5
  export { default as VerifyEmail } from './VerifyEmail.vue';
6
6
  export { default as RequestPasswordReset } from './RequestPasswordReset.vue';
7
7
  export { default as ResetPassword } from './ResetPassword.vue';
8
- export { sendVerificationEmail, verifyEmail, getUserStatus, requestPasswordReset, resetPassword, type AuthResponse, type VerifyEmailResponse, type UserStatusResponse, } from '../../services/authAPI';
8
+ export { sendVerificationEmail, verifyEmail, getUserStatus, resolveLoginIdentifier, requestPasswordReset, resetPassword, type AuthResponse, type VerifyEmailResponse, type UserStatusResponse, type ResolveLoginResponse, } from '../../services/authAPI';
9
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/auth/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AACrE,OAAO,EAAE,OAAO,IAAI,iCAAiC,EAAE,MAAM,yCAAyC,CAAC;AAGvG,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,OAAO,IAAI,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAC7E,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAG/D,OAAO,EACL,qBAAqB,EACrB,WAAW,EACX,aAAa,EACb,oBAAoB,EACpB,aAAa,EACb,KAAK,YAAY,EACjB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,GACxB,MAAM,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/auth/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AACrE,OAAO,EAAE,OAAO,IAAI,iCAAiC,EAAE,MAAM,yCAAyC,CAAC;AAGvG,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,OAAO,IAAI,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAC7E,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAG/D,OAAO,EACL,qBAAqB,EACrB,WAAW,EACX,aAAa,EACb,sBAAsB,EACtB,oBAAoB,EACpB,aAAa,EACb,KAAK,YAAY,EACjB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,GAC1B,MAAM,wBAAwB,CAAC"}
@@ -30,6 +30,9 @@ export interface UserStatusResponse extends AuthResponse {
30
30
  email?: string | null;
31
31
  entitlements?: UserEntitlements;
32
32
  }
33
+ export interface ResolveLoginResponse extends AuthResponse {
34
+ username?: string;
35
+ }
33
36
  /**
34
37
  * Triggers verification email send for a newly created account.
35
38
  *
@@ -44,6 +47,13 @@ export declare function sendVerificationEmail(username: string, email: string, o
44
47
  * Verify user email using token from magic link.
45
48
  */
46
49
  export declare function verifyEmail(token: string): Promise<VerifyEmailResponse>;
50
+ /**
51
+ * Resolve a login identifier (username OR email) to a couch username, gated on
52
+ * the correct password. Returns the username only on success; every failure is
53
+ * a uniform "invalid credentials" (no account-enumeration leak). Used by the
54
+ * login form before calling user.login() when the identifier is an email.
55
+ */
56
+ export declare function resolveLoginIdentifier(identifier: string, password: string): Promise<ResolveLoginResponse>;
47
57
  /**
48
58
  * Get current user's account status (verification status, email).
49
59
  * Requires valid AuthSession cookie.
@@ -1 +1 @@
1
- {"version":3,"file":"authAPI.d.ts","sourceRoot":"","sources":["../../src/services/authAPI.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,mBAAmB,CAAC,EAAE;YACpB,OAAO,CAAC,EAAE,MAAM,CAAC;YACjB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;SACxB,CAAC;KACH;CACF;AAgCD,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,mBAAoB,SAAQ,YAAY;IACvD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AAE3D,MAAM,WAAW,kBAAmB,SAAQ,YAAY;IACtD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,sBAAsB,GAAG,UAAU,GAAG,WAAW,CAAC;IAC3D,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,YAAY,CAAC,EAAE,gBAAgB,CAAC;CACjC;AAED;;;;;;;;GAQG;AACH,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,YAAY,CAAC,CA+BvB;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAuB7E;AAED;;;GAGG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAsBjE;AAED;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,YAAY,CAAC,CA4BvB;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,YAAY,CAAC,CAuBvB"}
1
+ {"version":3,"file":"authAPI.d.ts","sourceRoot":"","sources":["../../src/services/authAPI.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,mBAAmB,CAAC,EAAE;YACpB,OAAO,CAAC,EAAE,MAAM,CAAC;YACjB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;SACxB,CAAC;KACH;CACF;AAgCD,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,mBAAoB,SAAQ,YAAY;IACvD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AAE3D,MAAM,WAAW,kBAAmB,SAAQ,YAAY;IACtD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,sBAAsB,GAAG,UAAU,GAAG,WAAW,CAAC;IAC3D,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,YAAY,CAAC,EAAE,gBAAgB,CAAC;CACjC;AAED,MAAM,WAAW,oBAAqB,SAAQ,YAAY;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;GAQG;AACH,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,YAAY,CAAC,CA+BvB;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC,CA+B7E;AAED;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAC1C,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,oBAAoB,CAAC,CA6B/B;AAED;;;GAGG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAsBjE;AAED;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,YAAY,CAAC,CA4BvB;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,YAAY,CAAC,CAuBvB"}
@@ -1 +1 @@
1
- {"version":3,"file":"useAuthStore.d.ts","sourceRoot":"","sources":["../../src/stores/useAuthStore.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgB,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAGjE,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,eAAe,GAAG,SAAS,CAAC;IACnC,oBAAoB,EAAE;QACpB,IAAI,EAAE,OAAO,CAAC;QACd,QAAQ,EAAE,OAAO,CAAC;QAClB,aAAa,EAAE,OAAO,CAAC;QACvB,eAAe,EAAE,OAAO,CAAC;KAC1B,CAAC;IACF,cAAc,EAAE,OAAO,CAAC;CACzB;AAED,wBAAsB,cAAc,IAAI,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC,CAiB3E;AAED,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAgFw/jB,CAAC;;;;;;;;;;;;oBAAm0D,CAAC;qBAAuB,CAAC;;;;;;;kBA3Gp2nB,OAAO;sBACH,OAAO;2BACF,OAAO;6BACL,OAAO;;wBAEV,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAsGw/jB,CAAC;;;;;;;;;;;;oBAAm0D,CAAC;qBAAuB,CAAC;;;;;;;kBA3Gp2nB,OAAO;sBACH,OAAO;2BACF,OAAO;6BACL,OAAO;;wBAEV,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAsGw/jB,CAAC;;;;;;;;;;;;oBAAm0D,CAAC;qBAAuB,CAAC;;;;;;;kBA3Gp2nB,OAAO;sBACH,OAAO;2BACF,OAAO;6BACL,OAAO;;wBAEV,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAsGw/jB,CAAC;;;;;;;;;;;;oBAAm0D,CAAC;qBAAuB,CAAC;;;;;;;;;yBA1Cn1nB,OAAO;uBAIT,OAAO;;;;;EAqC/B,CAAC"}
1
+ {"version":3,"file":"useAuthStore.d.ts","sourceRoot":"","sources":["../../src/stores/useAuthStore.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgB,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAGjE,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,eAAe,GAAG,SAAS,CAAC;IACnC,oBAAoB,EAAE;QACpB,IAAI,EAAE,OAAO,CAAC;QACd,QAAQ,EAAE,OAAO,CAAC;QAClB,aAAa,EAAE,OAAO,CAAC;QACvB,eAAe,EAAE,OAAO,CAAC;KAC1B,CAAC;IACF,cAAc,EAAE,OAAO,CAAC;CACzB;AAED,wBAAsB,cAAc,IAAI,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC,CAiB3E;AAED,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAgF2vkB,CAAC;;;;;;;;;;;;oBAAm0D,CAAC;qBAAuB,CAAC;;;;;;;kBA3GvmoB,OAAO;sBACH,OAAO;2BACF,OAAO;6BACL,OAAO;;wBAEV,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAsG2vkB,CAAC;;;;;;;;;;;;oBAAm0D,CAAC;qBAAuB,CAAC;;;;;;;kBA3GvmoB,OAAO;sBACH,OAAO;2BACF,OAAO;6BACL,OAAO;;wBAEV,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAsG2vkB,CAAC;;;;;;;;;;;;oBAAm0D,CAAC;qBAAuB,CAAC;;;;;;;kBA3GvmoB,OAAO;sBACH,OAAO;2BACF,OAAO;6BACL,OAAO;;wBAEV,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAsG2vkB,CAAC;;;;;;;;;;;;oBAAm0D,CAAC;qBAAuB,CAAC;;;;;;;;;yBA1CtloB,OAAO;uBAIT,OAAO;;;;;EAqC/B,CAAC"}
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.2.19",
6
+ "version": "0.2.21",
7
7
  "sideEffects": false,
8
8
  "main": "./dist/common-ui.umd.js",
9
9
  "module": "./dist/common-ui.es.js",
@@ -34,8 +34,8 @@
34
34
  "@highlightjs/vue-plugin": "^2.1.2",
35
35
  "@mdi/font": "^7.3.67",
36
36
  "@vojtechlanka/vue-tags-input": "^3",
37
- "@vue-skuilder/common": "0.2.19",
38
- "@vue-skuilder/db": "0.2.19",
37
+ "@vue-skuilder/common": "0.2.21",
38
+ "@vue-skuilder/db": "0.2.21",
39
39
  "highlight.js": "^11.0.1",
40
40
  "marked": "^15.0.6",
41
41
  "moment": "^2.29.4",
@@ -71,5 +71,5 @@
71
71
  "vite-plugin-dts": "^4.5.3",
72
72
  "vitest": "^4.1.0"
73
73
  },
74
- "stableVersion": "0.2.19"
74
+ "stableVersion": "0.2.21"
75
75
  }
@@ -6,10 +6,10 @@
6
6
  <v-form onsubmit="return false;" @submit.prevent="login">
7
7
  <v-text-field
8
8
  id=""
9
- v-model="username"
9
+ v-model="identifier"
10
10
  autofocus
11
11
  name="username"
12
- label="Username"
12
+ label="Username or email"
13
13
  prepend-icon="mdi-account-circle"
14
14
  ></v-text-field>
15
15
  <v-text-field
@@ -67,6 +67,7 @@ import { Status } from '@vue-skuilder/common';
67
67
  import { User } from '@vue-skuilder/db';
68
68
  import { getCurrentUser, useAuthStore } from '../../stores/useAuthStore';
69
69
  import { useConfigStore } from '../../stores/useConfigStore';
70
+ import { resolveLoginIdentifier } from '../../services/authAPI';
70
71
 
71
72
  // Define props
72
73
  interface Props {
@@ -89,7 +90,8 @@ const route = useRoute();
89
90
  const authStore = useAuthStore();
90
91
  const configStore = useConfigStore();
91
92
 
92
- const username = ref('');
93
+ // Holds a username OR an email address (see login()).
94
+ const identifier = ref('');
93
95
  const password = ref('');
94
96
  const passwordVisible = ref(false);
95
97
  const awaitingResponse = ref(false);
@@ -120,7 +122,7 @@ const initBadLogin = () => {
120
122
  const login = async () => {
121
123
  awaitingResponse.value = true;
122
124
  log('Starting login attempt');
123
- log(`Login attempt for username: ${username.value}`);
125
+ log(`Login attempt for identifier: ${identifier.value}`);
124
126
 
125
127
  try {
126
128
  log('Attempting to get User instance');
@@ -128,7 +130,23 @@ const login = async () => {
128
130
  user.value = await getCurrentUser();
129
131
  log('Got User instance, attempting login');
130
132
 
131
- await user.value.login(username.value, password.value);
133
+ // Couch _session authenticates by username only. When the identifier is an
134
+ // email, resolve it to the account username first — server-side and gated
135
+ // on the correct password, so it can't be used to enumerate accounts. A
136
+ // plain username is passed straight through.
137
+ let loginName = identifier.value;
138
+ if (identifier.value.includes('@')) {
139
+ const resolved = await resolveLoginIdentifier(identifier.value, password.value);
140
+ if (!resolved.ok || !resolved.username) {
141
+ log('Email identifier did not resolve (bad credentials)');
142
+ initBadLogin();
143
+ awaitingResponse.value = false;
144
+ return;
145
+ }
146
+ loginName = resolved.username;
147
+ }
148
+
149
+ await user.value.login(loginName, password.value);
132
150
  log('Login successful');
133
151
 
134
152
  // load user config
@@ -209,12 +209,11 @@ Author: ${this.author}
209
209
  this.authStore.loginAndRegistration.init = false;
210
210
  this.authStore.loginAndRegistration.init = true;
211
211
 
212
- // Save email to user config if provided
212
+ // Send verification email if an address was provided. The address
213
+ // is persisted to the _users doc by send-verification (the single
214
+ // source of truth); no separate CONFIG.email copy is kept.
213
215
  if (this.email) {
214
216
  try {
215
- const currentUser = await getCurrentUser();
216
- await currentUser.setConfig({ email: this.email });
217
-
218
217
  // Trigger verification email send with current origin
219
218
  const origin = typeof window !== 'undefined' ? window.location.origin : undefined;
220
219
  const verificationResult = await sendVerificationEmail(this.username, this.email, origin);
@@ -14,9 +14,11 @@ export {
14
14
  sendVerificationEmail,
15
15
  verifyEmail,
16
16
  getUserStatus,
17
+ resolveLoginIdentifier,
17
18
  requestPasswordReset,
18
19
  resetPassword,
19
20
  type AuthResponse,
20
21
  type VerifyEmailResponse,
21
22
  type UserStatusResponse,
23
+ type ResolveLoginResponse,
22
24
  } from '../../services/authAPI';
@@ -68,6 +68,10 @@ export interface UserStatusResponse extends AuthResponse {
68
68
  entitlements?: UserEntitlements;
69
69
  }
70
70
 
71
+ export interface ResolveLoginResponse extends AuthResponse {
72
+ username?: string;
73
+ }
74
+
71
75
  /**
72
76
  * Triggers verification email send for a newly created account.
73
77
  *
@@ -127,9 +131,57 @@ export async function verifyEmail(token: string): Promise<VerifyEmailResponse> {
127
131
  });
128
132
 
129
133
  if (!response.ok) {
134
+ // Prefer the server's explanatory message (expired token, email already
135
+ // verified on another account, …) over a bare HTTP status.
136
+ let serverError: string | undefined;
137
+ try {
138
+ serverError = (await response.json())?.error;
139
+ } catch {
140
+ // non-JSON body — fall through to the generic message
141
+ }
130
142
  return {
131
143
  ok: false,
132
- error: `HTTP ${response.status}: ${response.statusText}`,
144
+ error: serverError || `HTTP ${response.status}: ${response.statusText}`,
145
+ };
146
+ }
147
+
148
+ return await response.json();
149
+ } catch (error) {
150
+ return {
151
+ ok: false,
152
+ error: error instanceof Error ? error.message : 'Network error',
153
+ };
154
+ }
155
+ }
156
+
157
+ /**
158
+ * Resolve a login identifier (username OR email) to a couch username, gated on
159
+ * the correct password. Returns the username only on success; every failure is
160
+ * a uniform "invalid credentials" (no account-enumeration leak). Used by the
161
+ * login form before calling user.login() when the identifier is an email.
162
+ */
163
+ export async function resolveLoginIdentifier(
164
+ identifier: string,
165
+ password: string
166
+ ): Promise<ResolveLoginResponse> {
167
+ try {
168
+ const response = await fetch(`${getApiBase()}/auth/resolve-login`, {
169
+ method: 'POST',
170
+ headers: { 'Content-Type': 'application/json' },
171
+ credentials: 'include',
172
+ body: JSON.stringify({ identifier, password }),
173
+ });
174
+
175
+ if (!response.ok) {
176
+ let serverError: string | undefined;
177
+ try {
178
+ serverError = (await response.json())?.error;
179
+ } catch {
180
+ // non-JSON body — fall through to the generic message
181
+ }
182
+ return {
183
+ ok: false,
184
+ error: serverError || `HTTP ${response.status}: ${response.statusText}`,
133
185
  };
134
186
  }
135
187