@structured-id/ui-core 1.3.0 → 3.0.0
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.
- package/README.md +22 -10
- package/package.json +25 -43
- package/src/components/SidStatusBadge.vue +1 -5
- package/src/composables/useAccountTransport.test.ts +2 -1
- package/src/composables/useGrpc.test.ts +17 -14
- package/src/composables/useGrpc.ts +4 -2
- package/src/index.ts +2 -120
- package/src/profile/components/ConsentManager.vue +5 -1
- package/src/profile/components/ProfileEditor.vue +1 -1
- package/src/profile/components/SecuritySettings.vue +5 -1
- package/src/profile/components/SessionList.vue +2 -1
- package/src/profile/components/SidRegistrationForm.test.ts +173 -1
- package/src/profile/components/SidRegistrationForm.vue +88 -32
- package/src/profile/components/index.ts +1 -4
- package/src/profile/composables/index.ts +1 -0
- package/src/profile/composables/operationPlan.test.ts +155 -0
- package/src/profile/composables/operationPlan.ts +218 -0
- package/src/profile/composables/useAccountApi.ts +4 -4
- package/src/profile/composables/useAuth.test.ts +312 -31
- package/src/profile/composables/useAuth.ts +176 -30
- package/src/profile/composables/useAuthApi.opaque.test.ts +13 -6
- package/src/profile/composables/useAuthApi.ts +40 -18
- package/src/profile/composables/useCurrentPasswordRequirement.test.ts +196 -0
- package/src/profile/composables/useCurrentPasswordRequirement.ts +95 -0
- package/src/profile/composables/useEnrollmentApi.test.ts +7 -3
- package/src/profile/composables/useEnrollmentApi.ts +3 -5
- package/src/profile/composables/useIdentityApi.ts +4 -4
- package/src/profile/composables/useSecurityLevel.test.ts +1 -1
- package/src/profile/composables/useSecurityLevel.ts +1 -1
- package/src/quasar/SidPasswordCeremony.test.ts +253 -0
- package/src/quasar/SidPasswordCeremony.vue +456 -0
- package/src/quasar/index.ts +2 -0
- package/src/utils/rpcRefusal.test.ts +4 -4
- package/src/utils/rpcRefusal.ts +8 -4
- package/dist/browser-vp/index.cjs +0 -349
- package/dist/browser-vp/index.d.cts +0 -276
- package/dist/browser-vp/index.d.ts +0 -276
- package/dist/browser-vp/index.js +0 -349
- package/dist/chunk-C3Q766CY.cjs +0 -125
- package/dist/chunk-J5YXRXB5.js +0 -125
- package/dist/index.cjs +0 -23581
- package/dist/index.d.cts +0 -11539
- package/dist/index.d.ts +0 -11539
- package/dist/index.js +0 -23581
- package/dist/storage-4CB2JTLW.cjs +0 -22
- package/dist/storage-THRWREZP.js +0 -22
- package/src/browser-vp/crypto.ts +0 -253
- package/src/browser-vp/index.test.ts +0 -10
- package/src/browser-vp/index.ts +0 -116
- package/src/browser-vp/prf.ts +0 -179
- package/src/browser-vp/storage.test.ts +0 -9
- package/src/browser-vp/storage.ts +0 -163
- package/src/browser-vp/types.ts +0 -95
- package/src/browser-vp/vp.test.ts +0 -43
- package/src/browser-vp/vp.ts +0 -111
- package/src/generated/google/api/http.ts +0 -687
- package/src/generated/google/api/httpbody.ts +0 -159
- package/src/generated/google/protobuf/any.ts +0 -326
- package/src/generated/google/protobuf/descriptor.ts +0 -4865
- package/src/generated/google/protobuf/duration.ts +0 -230
- package/src/generated/google/protobuf/empty.ts +0 -94
- package/src/generated/google/protobuf/struct.ts +0 -484
- package/src/generated/google/protobuf/timestamp.ts +0 -287
- package/src/generated/google/rpc/error_details.ts +0 -1386
- package/src/generated/google/rpc/status.ts +0 -127
- package/src/generated/sid/v1/account/account.client.ts +0 -527
- package/src/generated/sid/v1/account/account.ts +0 -3261
- package/src/generated/sid/v1/admin/admin.client.ts +0 -139
- package/src/generated/sid/v1/admin/admin.ts +0 -781
- package/src/generated/sid/v1/admin/admin_roles.client.ts +0 -181
- package/src/generated/sid/v1/admin/admin_roles.ts +0 -1606
- package/src/generated/sid/v1/admin/analytics.client.ts +0 -106
- package/src/generated/sid/v1/admin/analytics.ts +0 -1017
- package/src/generated/sid/v1/admin/branding.client.ts +0 -166
- package/src/generated/sid/v1/admin/branding.ts +0 -1906
- package/src/generated/sid/v1/admin/enrollment.client.ts +0 -177
- package/src/generated/sid/v1/admin/enrollment.ts +0 -1766
- package/src/generated/sid/v1/admin/events.client.ts +0 -195
- package/src/generated/sid/v1/admin/events.ts +0 -1900
- package/src/generated/sid/v1/admin/flow.client.ts +0 -183
- package/src/generated/sid/v1/admin/flow.ts +0 -1552
- package/src/generated/sid/v1/admin/keys.client.ts +0 -107
- package/src/generated/sid/v1/admin/keys.ts +0 -931
- package/src/generated/sid/v1/admin/login_experience.client.ts +0 -160
- package/src/generated/sid/v1/admin/login_experience.ts +0 -1061
- package/src/generated/sid/v1/admin/migration.client.ts +0 -127
- package/src/generated/sid/v1/admin/migration.ts +0 -1948
- package/src/generated/sid/v1/admin/notifications.client.ts +0 -134
- package/src/generated/sid/v1/admin/notifications.ts +0 -1273
- package/src/generated/sid/v1/admin/operations.client.ts +0 -241
- package/src/generated/sid/v1/admin/operations.ts +0 -2633
- package/src/generated/sid/v1/admin/organizations.client.ts +0 -57
- package/src/generated/sid/v1/admin/organizations.ts +0 -332
- package/src/generated/sid/v1/admin/provisioning.client.ts +0 -232
- package/src/generated/sid/v1/admin/provisioning.ts +0 -1383
- package/src/generated/sid/v1/admin/scopes.client.ts +0 -171
- package/src/generated/sid/v1/admin/scopes.ts +0 -1324
- package/src/generated/sid/v1/admin/security.client.ts +0 -410
- package/src/generated/sid/v1/admin/security.ts +0 -4077
- package/src/generated/sid/v1/admin/sessions.client.ts +0 -114
- package/src/generated/sid/v1/admin/sessions.ts +0 -923
- package/src/generated/sid/v1/admin/settings.client.ts +0 -351
- package/src/generated/sid/v1/admin/settings.ts +0 -2458
- package/src/generated/sid/v1/admin/user_profile.client.ts +0 -57
- package/src/generated/sid/v1/admin/user_profile.ts +0 -730
- package/src/generated/sid/v1/admin/webhooks.client.ts +0 -269
- package/src/generated/sid/v1/admin/webhooks.ts +0 -2804
- package/src/generated/sid/v1/attestation/attestation.client.ts +0 -115
- package/src/generated/sid/v1/attestation/attestation.ts +0 -813
- package/src/generated/sid/v1/authn/auth.client.ts +0 -943
- package/src/generated/sid/v1/authn/auth.ts +0 -9719
- package/src/generated/sid/v1/authn/flows.client.ts +0 -413
- package/src/generated/sid/v1/authn/flows.ts +0 -3205
- package/src/generated/sid/v1/authn/issuer.client.ts +0 -489
- package/src/generated/sid/v1/authn/issuer.ts +0 -1600
- package/src/generated/sid/v1/authn/password_history.client.ts +0 -57
- package/src/generated/sid/v1/authn/password_history.ts +0 -578
- package/src/generated/sid/v1/authn/system_integration.client.ts +0 -73
- package/src/generated/sid/v1/authn/system_integration.ts +0 -218
- package/src/generated/sid/v1/authz/authz.client.ts +0 -459
- package/src/generated/sid/v1/authz/authz.ts +0 -6524
- package/src/generated/sid/v1/authz/forward_auth.client.ts +0 -97
- package/src/generated/sid/v1/authz/forward_auth.ts +0 -79
- package/src/generated/sid/v1/authz/governance.client.ts +0 -102
- package/src/generated/sid/v1/authz/governance.ts +0 -875
- package/src/generated/sid/v1/common/errors.ts +0 -710
- package/src/generated/sid/v1/common/pagination.ts +0 -152
- package/src/generated/sid/v1/common/transport.ts +0 -227
- package/src/generated/sid/v1/common/types.ts +0 -151
- package/src/generated/sid/v1/events/event_stream.client.ts +0 -67
- package/src/generated/sid/v1/events/event_stream.ts +0 -375
- package/src/generated/sid/v1/federation/upstream.client.ts +0 -142
- package/src/generated/sid/v1/federation/upstream.ts +0 -1736
- package/src/generated/sid/v1/identity/identity.client.ts +0 -467
- package/src/generated/sid/v1/identity/identity.ts +0 -5676
- package/src/generated/sid/v1/ids/ids.ts +0 -395
- package/src/generated/sid/v1/machine/machine_user.client.ts +0 -219
- package/src/generated/sid/v1/machine/machine_user.ts +0 -2364
- package/src/generated/sid/v1/machine/pat.client.ts +0 -130
- package/src/generated/sid/v1/machine/pat.ts +0 -1083
- package/src/generated/sid/v1/projects/projects.client.ts +0 -404
- package/src/generated/sid/v1/projects/projects.ts +0 -4446
- package/src/generated/sid/v1/scim/scim.client.ts +0 -520
- package/src/generated/sid/v1/scim/scim.ts +0 -3182
- package/src/generated/sid/v1/test/test.client.ts +0 -232
- package/src/generated/sid/v1/test/test.ts +0 -1136
- package/src/transport/adaptive.test.ts +0 -728
- package/src/transport/adaptive.ts +0 -308
- package/src/transport/framing.test.ts +0 -227
- package/src/transport/framing.ts +0 -98
- package/src/transport/webtransport.test.ts +0 -728
- package/src/transport/webtransport.ts +0 -323
package/README.md
CHANGED
|
@@ -1,23 +1,34 @@
|
|
|
1
1
|
# @structured-id/ui-core
|
|
2
2
|
|
|
3
|
-
Shared UI package for StructuredID applications
|
|
3
|
+
Shared UI package for StructuredID applications: Vue 3 composables, Pinia stores and reusable components over the generated clients of [`@structured-id/proto`](https://www.npmjs.com/package/@structured-id/proto).
|
|
4
4
|
|
|
5
5
|
## What's Inside
|
|
6
6
|
|
|
7
|
-
- **
|
|
8
|
-
- **Composables** -- `createAuth` (password ceremonies over an injected ZKPP client), `useAuthApi`, `useAccountApi`, `useIdentityApi`, `useSecurityLevel`
|
|
7
|
+
- **Composables** -- `createAuth` (password ceremonies over an injected ZKPP client), `useAuthApi`, `useAccountApi`, `useIdentityApi`, `useSecurityLevel`, transport setup (`initGrpc`, `initAccountApi`)
|
|
9
8
|
- **Components** -- `SidLoginForm`, `SidRegistrationForm`, `SidMfaChallenge`, `SidPrincipalInput`, profile management pages
|
|
10
|
-
- **Utilities** -- `normalizePrincipal`, `detectLoginInputType`, validators, formatters
|
|
9
|
+
- **Utilities** -- `normalizePrincipal`, `detectLoginInputType`, `rpcRefusal`, validators, formatters
|
|
10
|
+
|
|
11
|
+
The generated messages and clients, the `google.rpc` error model and the gRPC-web / WebTransport transport live in `@structured-id/proto`; import them from there (`@structured-id/proto/sid/v1/identity/identity.client`, `@structured-id/proto/transport`).
|
|
12
|
+
|
|
13
|
+
### Migration from the previous exports
|
|
14
|
+
|
|
15
|
+
The root no longer re-exports generated clients, messages or enums, and the
|
|
16
|
+
`./proto` subpath is removed. Import those symbols from their schema modules in
|
|
17
|
+
`@structured-id/proto`. The `./browser-vp` prototype subpath is also removed;
|
|
18
|
+
this package does not provide a replacement browser-presentation implementation.
|
|
19
|
+
The Vue composables and components keep their UI package owners.
|
|
11
20
|
|
|
12
21
|
## Usage
|
|
13
22
|
|
|
14
23
|
```ts
|
|
15
24
|
import { getTransport, initGrpc } from "@structured-id/ui-core";
|
|
16
25
|
import { SidLoginForm, createAuth } from "@structured-id/ui-core/profile";
|
|
17
|
-
import {
|
|
26
|
+
import { IdentityServiceClient } from "@structured-id/proto/sid/v1/identity/identity.client";
|
|
27
|
+
import { loadZkppClient } from "@structured-id/opaque";
|
|
18
28
|
|
|
19
29
|
// Initialize gRPC transport
|
|
20
30
|
initGrpc({ baseUrl: "http://localhost:9080" });
|
|
31
|
+
const identity = new IdentityServiceClient(getTransport());
|
|
21
32
|
|
|
22
33
|
// Password ceremonies (register, login, change, reset) on the app's client
|
|
23
34
|
const { login } = createAuth(() => loadZkppClient());
|
|
@@ -25,25 +36,26 @@ const { login } = createAuth(() => loadZkppClient());
|
|
|
25
36
|
|
|
26
37
|
## Development
|
|
27
38
|
|
|
39
|
+
The package ships its TypeScript and Vue sources; the consuming application's
|
|
40
|
+
bundler (Vite under Quasar) compiles them, so there is no build step.
|
|
41
|
+
|
|
28
42
|
```bash
|
|
29
43
|
yarn install
|
|
30
|
-
yarn build # Build library
|
|
31
44
|
yarn test # Run tests (vitest)
|
|
32
45
|
yarn typecheck # vue-tsc --noEmit
|
|
46
|
+
node ci/check-package.mjs # Pack and build an external Vue/Vite consumer
|
|
33
47
|
```
|
|
34
48
|
|
|
35
49
|
## Architecture
|
|
36
50
|
|
|
37
51
|
```
|
|
38
52
|
src/
|
|
39
|
-
composables/ # Vue 3 composition API
|
|
53
|
+
composables/ # Vue 3 composition API, transport setup
|
|
40
54
|
stores/ # Pinia stores (auth)
|
|
41
55
|
profile/ # Profile components and composables
|
|
42
56
|
components/ # SidLoginForm, SidRegistrationForm, SidMfaChallenge
|
|
43
57
|
composables/ # createAuth, useAuthApi, useAccountApi, useIdentityApi
|
|
44
|
-
utils/ # normalizePrincipal, validators, formatters
|
|
45
|
-
generated/ # protobuf-ts generated clients (do not edit)
|
|
46
|
-
proto/ # Proto definitions submodule
|
|
58
|
+
utils/ # normalizePrincipal, rpcRefusal, validators, formatters
|
|
47
59
|
```
|
|
48
60
|
|
|
49
61
|
## License
|
package/package.json
CHANGED
|
@@ -1,30 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@structured-id/ui-core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public",
|
|
7
7
|
"registry": "https://registry.npmjs.org"
|
|
8
8
|
},
|
|
9
|
-
"description": "Shared UI package for StructuredID
|
|
9
|
+
"description": "Shared UI package for StructuredID: Vue 3 composables, Pinia stores and components over the @structured-id/proto clients.",
|
|
10
10
|
"repository": {
|
|
11
11
|
"type": "git",
|
|
12
12
|
"url": "git+https://github.com/structured-id/ui-core.git"
|
|
13
13
|
},
|
|
14
14
|
"homepage": "https://github.com/structured-id/ui-core",
|
|
15
15
|
"bugs": "https://github.com/structured-id/ui-core/issues",
|
|
16
|
-
"
|
|
17
|
-
"module": "dist/index.js",
|
|
18
|
-
"types": "dist/index.d.ts",
|
|
16
|
+
"types": "./src/index.ts",
|
|
19
17
|
"exports": {
|
|
20
18
|
".": {
|
|
21
|
-
"
|
|
22
|
-
"import": "./src/index.ts",
|
|
23
|
-
"require": "./dist/index.cjs"
|
|
24
|
-
},
|
|
25
|
-
"./proto": {
|
|
26
|
-
"types": "./src/generated/index.d.ts",
|
|
27
|
-
"import": "./src/generated/index.js"
|
|
19
|
+
"import": "./src/index.ts"
|
|
28
20
|
},
|
|
29
21
|
"./components": {
|
|
30
22
|
"import": "./src/components/index.ts"
|
|
@@ -32,59 +24,49 @@
|
|
|
32
24
|
"./quasar": {
|
|
33
25
|
"import": "./src/quasar/index.ts"
|
|
34
26
|
},
|
|
35
|
-
"./browser-vp": {
|
|
36
|
-
"types": "./dist/browser-vp/index.d.ts",
|
|
37
|
-
"import": "./dist/browser-vp/index.js",
|
|
38
|
-
"require": "./dist/browser-vp/index.cjs"
|
|
39
|
-
},
|
|
40
27
|
"./profile": {
|
|
41
28
|
"import": "./src/profile/index.ts"
|
|
42
29
|
}
|
|
43
30
|
},
|
|
44
31
|
"files": [
|
|
45
|
-
"dist",
|
|
46
32
|
"src"
|
|
47
33
|
],
|
|
48
34
|
"scripts": {
|
|
49
|
-
"generate": "buf generate --template buf.gen.yaml",
|
|
50
|
-
"build": "tsup",
|
|
51
|
-
"dev": "tsup --watch",
|
|
52
35
|
"typecheck": "vue-tsc --noEmit",
|
|
53
36
|
"lint": "eslint src/",
|
|
54
37
|
"format": "prettier --write \"src/**/*.{ts,vue}\"",
|
|
55
38
|
"test": "vitest run",
|
|
56
|
-
"test:watch": "vitest"
|
|
57
|
-
"prepublishOnly": "yarn generate && yarn build"
|
|
39
|
+
"test:watch": "vitest"
|
|
58
40
|
},
|
|
59
41
|
"dependencies": {
|
|
60
42
|
"@protobuf-ts/grpcweb-transport": "^2.11.1",
|
|
61
43
|
"@protobuf-ts/runtime": "^2.11.1",
|
|
62
44
|
"@protobuf-ts/runtime-rpc": "^2.11.1",
|
|
63
|
-
"
|
|
45
|
+
"@structured-id/proto": "^0.2.0",
|
|
46
|
+
"libphonenumber-js": "^1.13.15"
|
|
64
47
|
},
|
|
65
48
|
"devDependencies": {
|
|
66
|
-
"@
|
|
67
|
-
"@
|
|
68
|
-
"@
|
|
69
|
-
"@vue/
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
"
|
|
79
|
-
"vue": "^
|
|
80
|
-
"vue-
|
|
81
|
-
"vue-tsc": "^2.0.0"
|
|
49
|
+
"@vitejs/plugin-vue": "^6.0.9",
|
|
50
|
+
"@vue/compiler-dom": "^3.5.43",
|
|
51
|
+
"@vue/devtools-api": "^8.2.1",
|
|
52
|
+
"@vue/test-utils": "^2.5.1",
|
|
53
|
+
"eslint": "^10.12.0",
|
|
54
|
+
"jsdom": "^30.1.2",
|
|
55
|
+
"pinia": "^4.0.3",
|
|
56
|
+
"prettier": "^3.9.9",
|
|
57
|
+
"typescript": "^6.0.3",
|
|
58
|
+
"typescript-eslint": "^8.71.1",
|
|
59
|
+
"vite": "^8.3.4",
|
|
60
|
+
"vitest": "^5.0.3",
|
|
61
|
+
"vue": "^3.5.43",
|
|
62
|
+
"vue-router": "^5.4.0",
|
|
63
|
+
"vue-tsc": "^3.3.12"
|
|
82
64
|
},
|
|
83
65
|
"peerDependencies": {
|
|
84
|
-
"pinia": "^
|
|
66
|
+
"pinia": "^4.0.3",
|
|
85
67
|
"quasar": "^2.17.0",
|
|
86
|
-
"vue": "^3.
|
|
87
|
-
"vue-router": "^4.
|
|
68
|
+
"vue": "^3.5.43",
|
|
69
|
+
"vue-router": "^5.4.0"
|
|
88
70
|
},
|
|
89
71
|
"peerDependenciesMeta": {
|
|
90
72
|
"quasar": {
|
|
@@ -12,7 +12,8 @@ const created: Array<{
|
|
|
12
12
|
}> = [];
|
|
13
13
|
|
|
14
14
|
vi.mock("@protobuf-ts/grpcweb-transport", () => ({
|
|
15
|
-
|
|
15
|
+
// Called with `new`: a constructor mock must be a `function`, not an arrow.
|
|
16
|
+
GrpcWebFetchTransport: vi.fn().mockImplementation(function (options) {
|
|
16
17
|
created.push(options);
|
|
17
18
|
return { type: "grpc-web-transport" };
|
|
18
19
|
}),
|
|
@@ -5,32 +5,35 @@ import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
|
5
5
|
// ---------------------------------------------------------------------------
|
|
6
6
|
|
|
7
7
|
const mockAdaptiveInit = vi
|
|
8
|
-
.fn<
|
|
8
|
+
.fn<() => Promise<void>>()
|
|
9
9
|
.mockResolvedValue(undefined);
|
|
10
10
|
const mockAdaptiveClose = vi.fn();
|
|
11
11
|
const mockAdaptiveActiveTransport = vi
|
|
12
|
-
.fn<
|
|
12
|
+
.fn<() => string>()
|
|
13
13
|
.mockReturnValue("grpc-web");
|
|
14
14
|
|
|
15
|
-
vi.mock("
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
15
|
+
vi.mock("@structured-id/proto/transport", () => ({
|
|
16
|
+
// Constructor mocks are `function`s: they are called with `new`.
|
|
17
|
+
AdaptiveRpcTransport: vi.fn().mockImplementation(function () {
|
|
18
|
+
return {
|
|
19
|
+
init: mockAdaptiveInit,
|
|
20
|
+
close: mockAdaptiveClose,
|
|
21
|
+
get activeTransport() {
|
|
22
|
+
return mockAdaptiveActiveTransport();
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
}),
|
|
23
26
|
}));
|
|
24
27
|
|
|
25
28
|
vi.mock("@protobuf-ts/grpcweb-transport", () => ({
|
|
26
|
-
GrpcWebFetchTransport: vi
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
GrpcWebFetchTransport: vi.fn().mockImplementation(function () {
|
|
30
|
+
return { type: "grpc-web-transport" };
|
|
31
|
+
}),
|
|
29
32
|
}));
|
|
30
33
|
|
|
31
34
|
// Import AFTER mocks
|
|
32
35
|
import { GrpcWebFetchTransport } from "@protobuf-ts/grpcweb-transport";
|
|
33
|
-
import { AdaptiveRpcTransport } from "
|
|
36
|
+
import { AdaptiveRpcTransport } from "@structured-id/proto/transport";
|
|
34
37
|
import {
|
|
35
38
|
initGrpc,
|
|
36
39
|
getTransport,
|
|
@@ -2,8 +2,10 @@ import { GrpcWebFetchTransport } from "@protobuf-ts/grpcweb-transport";
|
|
|
2
2
|
import type { RpcTransport } from "@protobuf-ts/runtime-rpc";
|
|
3
3
|
import { ref, type Ref } from "vue";
|
|
4
4
|
import type { GrpcConfig } from "../types/auth";
|
|
5
|
-
import {
|
|
6
|
-
|
|
5
|
+
import {
|
|
6
|
+
AdaptiveRpcTransport,
|
|
7
|
+
type TransportType,
|
|
8
|
+
} from "@structured-id/proto/transport";
|
|
7
9
|
|
|
8
10
|
/** Shared RPC transport instance (gRPC-web or AdaptiveRpcTransport). */
|
|
9
11
|
let _transport: RpcTransport | null = null;
|
package/src/index.ts
CHANGED
|
@@ -69,123 +69,5 @@ export type { AuthState, SessionInfo, GrpcConfig } from "./types";
|
|
|
69
69
|
// i18n key constants
|
|
70
70
|
export { AUTH_KEYS, COMMON_KEYS } from "./i18n";
|
|
71
71
|
|
|
72
|
-
// Generated
|
|
73
|
-
|
|
74
|
-
export { IdentityServiceClient } from "./generated/sid/v1/identity/identity.client";
|
|
75
|
-
export { AccountServiceClient } from "./generated/sid/v1/account/account.client";
|
|
76
|
-
export { UpstreamServiceClient } from "./generated/sid/v1/federation/upstream.client";
|
|
77
|
-
export { EnrollmentServiceClient } from "./generated/sid/v1/admin/enrollment.client";
|
|
78
|
-
export type { InstanceStatus } from "./generated/sid/v1/admin/enrollment";
|
|
79
|
-
|
|
80
|
-
// Re-export commonly used proto types
|
|
81
|
-
export type {
|
|
82
|
-
OpaqueLoginStartRequest,
|
|
83
|
-
OpaqueLoginStartResponse,
|
|
84
|
-
OpaqueLoginFinishRequest,
|
|
85
|
-
OpaqueLoginFinishResponse,
|
|
86
|
-
OpaqueRegistrationStartRequest,
|
|
87
|
-
OpaqueRegistrationStartResponse,
|
|
88
|
-
OpaqueRegistrationFinishRequest,
|
|
89
|
-
OpaqueRegistrationFinishResponse,
|
|
90
|
-
MfaVerifyResponse,
|
|
91
|
-
TotpEnrollmentChallenge,
|
|
92
|
-
FinishTotpEnrollmentResponse,
|
|
93
|
-
ResendSmsMfaResponse,
|
|
94
|
-
GenerateRecoveryCodesResponse,
|
|
95
|
-
VerifyOtpResponse,
|
|
96
|
-
ResendOtpResponse,
|
|
97
|
-
VerifyPhoneResponse,
|
|
98
|
-
ConfirmIdpLinkResponse,
|
|
99
|
-
UserOrganization,
|
|
100
|
-
ListUserOrganizationsResponse,
|
|
101
|
-
SelectOrganizationResponse,
|
|
102
|
-
GetTermsResponse,
|
|
103
|
-
AcceptTermsResponse,
|
|
104
|
-
CibaConsentResponse,
|
|
105
|
-
VerifyCaptchaResponse,
|
|
106
|
-
DynamicPrompt,
|
|
107
|
-
DynamicPromptField,
|
|
108
|
-
SubmitDynamicPromptResponse,
|
|
109
|
-
StepUpChallenge,
|
|
110
|
-
CompleteStepUpResponse,
|
|
111
|
-
DeleteMfaCredentialResponse,
|
|
112
|
-
ConfirmAccountDeletionResponse,
|
|
113
|
-
SubmitDeviceUserCodeResponse,
|
|
114
|
-
OAuth2AuthorizeResponse,
|
|
115
|
-
WebAuthnAuthenticationStartResponse,
|
|
116
|
-
WebAuthnAuthenticationFinishResponse,
|
|
117
|
-
WebAuthnRegistrationStartResponse,
|
|
118
|
-
WebAuthnRegistrationFinishResponse,
|
|
119
|
-
RequestMagicLinkResponse,
|
|
120
|
-
VerifyMagicLinkResponse,
|
|
121
|
-
RequestPasswordResetResponse,
|
|
122
|
-
VerifyPasswordResetResponse,
|
|
123
|
-
ExecutePasswordResetResponse,
|
|
124
|
-
CompletePasswordResetResponse,
|
|
125
|
-
OpaqueZkppRegistrationStartResponse,
|
|
126
|
-
OpaqueZkppRegistrationFinishResponse,
|
|
127
|
-
PasswordChangeChallengeResponse,
|
|
128
|
-
PasswordChangeExecuteResponse,
|
|
129
|
-
PasswordChangeFinishResponse,
|
|
130
|
-
} from "./generated/sid/v1/authn/auth";
|
|
131
|
-
|
|
132
|
-
// Password operations: the history evaluator and the proof the finish carries
|
|
133
|
-
export { PasswordHistoryEvaluatorServiceClient } from "./generated/sid/v1/authn/password_history.client";
|
|
134
|
-
export type {
|
|
135
|
-
PasswordHistoryContext,
|
|
136
|
-
PasswordHistoryDomain,
|
|
137
|
-
PasswordHistoryEvaluation,
|
|
138
|
-
PasswordHistoryEvaluationProof,
|
|
139
|
-
PasswordRegistrationProof,
|
|
140
|
-
} from "./generated/sid/v1/authn/password_history";
|
|
141
|
-
export type { PasswordOperationId } from "./generated/sid/v1/ids/ids";
|
|
142
|
-
|
|
143
|
-
export type {
|
|
144
|
-
Profile,
|
|
145
|
-
Session,
|
|
146
|
-
Credential,
|
|
147
|
-
Principal,
|
|
148
|
-
} from "./generated/sid/v1/identity/identity";
|
|
149
|
-
|
|
150
|
-
export { StepUpMethod } from "./generated/sid/v1/authn/auth";
|
|
151
|
-
|
|
152
|
-
export {
|
|
153
|
-
ProfileStatus,
|
|
154
|
-
PrincipalType,
|
|
155
|
-
CredentialType,
|
|
156
|
-
AuthenticatorAttachment,
|
|
157
|
-
} from "./generated/sid/v1/identity/identity";
|
|
158
|
-
|
|
159
|
-
export type {
|
|
160
|
-
ConsentInfo,
|
|
161
|
-
ClaimConsent,
|
|
162
|
-
ActivityEvent,
|
|
163
|
-
ListConsentsResponse,
|
|
164
|
-
GetConsentDetailResponse,
|
|
165
|
-
ListActivityEventsResponse,
|
|
166
|
-
LinkedAccount,
|
|
167
|
-
ListLinkedAccountsResponse,
|
|
168
|
-
LinkAccountResponse,
|
|
169
|
-
ReauthenticateLinkResponse,
|
|
170
|
-
AvailableApp,
|
|
171
|
-
ListAvailableAppsResponse,
|
|
172
|
-
LaunchAppResponse,
|
|
173
|
-
PasswordlessStatus,
|
|
174
|
-
DisablePasswordlessResponse,
|
|
175
|
-
} from "./generated/sid/v1/account/account";
|
|
176
|
-
|
|
177
|
-
export {
|
|
178
|
-
ConsentStatus,
|
|
179
|
-
ClaimType,
|
|
180
|
-
ClaimConsentStatus,
|
|
181
|
-
ActivityEventStatus,
|
|
182
|
-
LinkedAccountTrustCategory,
|
|
183
|
-
LinkedAccountStatus,
|
|
184
|
-
} from "./generated/sid/v1/account/account";
|
|
185
|
-
|
|
186
|
-
export type { EnabledProviderInfo } from "./generated/sid/v1/federation/upstream";
|
|
187
|
-
|
|
188
|
-
export { Timestamp } from "./generated/google/protobuf/timestamp";
|
|
189
|
-
|
|
190
|
-
// Browser VP — import from '@structured-id/ui-core/browser-vp'
|
|
191
|
-
// Separate entry point: WebAuthn PRF, IndexedDB, VP creation (browser-only APIs)
|
|
72
|
+
// Generated clients and messages come from '@structured-id/proto'
|
|
73
|
+
// (e.g. '@structured-id/proto/sid/v1/identity/identity.client').
|
|
@@ -66,7 +66,11 @@
|
|
|
66
66
|
</template>
|
|
67
67
|
|
|
68
68
|
<script setup lang="ts">
|
|
69
|
-
import {
|
|
69
|
+
import {
|
|
70
|
+
type ConsentInfo,
|
|
71
|
+
ConsentStatus,
|
|
72
|
+
} from "@structured-id/proto/sid/v1/account/account";
|
|
73
|
+
import { Timestamp } from "@structured-id/proto/google/protobuf/timestamp";
|
|
70
74
|
|
|
71
75
|
defineProps<{
|
|
72
76
|
consents: ConsentInfo[];
|
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
|
|
87
87
|
<script setup lang="ts">
|
|
88
88
|
import { reactive, computed, watch } from "vue";
|
|
89
|
-
import type { Profile } from "
|
|
89
|
+
import type { Profile } from "@structured-id/proto/sid/v1/identity/identity";
|
|
90
90
|
|
|
91
91
|
const props = withDefaults(
|
|
92
92
|
defineProps<{
|
|
@@ -96,7 +96,11 @@
|
|
|
96
96
|
</template>
|
|
97
97
|
|
|
98
98
|
<script setup lang="ts">
|
|
99
|
-
import {
|
|
99
|
+
import {
|
|
100
|
+
type Credential,
|
|
101
|
+
CredentialType,
|
|
102
|
+
} from "@structured-id/proto/sid/v1/identity/identity";
|
|
103
|
+
import { Timestamp } from "@structured-id/proto/google/protobuf/timestamp";
|
|
100
104
|
import type { FactorCategory } from "../composables/useSecurityLevel";
|
|
101
105
|
|
|
102
106
|
defineProps<{
|
|
@@ -49,7 +49,8 @@
|
|
|
49
49
|
</template>
|
|
50
50
|
|
|
51
51
|
<script setup lang="ts">
|
|
52
|
-
import {
|
|
52
|
+
import type { Session } from "@structured-id/proto/sid/v1/identity/identity";
|
|
53
|
+
import { Timestamp } from "@structured-id/proto/google/protobuf/timestamp";
|
|
53
54
|
|
|
54
55
|
defineProps<{
|
|
55
56
|
sessions: Session[];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect, vi } from "vitest";
|
|
2
|
-
import { mount } from "@vue/test-utils";
|
|
2
|
+
import { flushPromises, mount } from "@vue/test-utils";
|
|
3
3
|
import SidRegistrationForm from "./SidRegistrationForm.vue";
|
|
4
4
|
|
|
5
5
|
const quasarStubs = {
|
|
@@ -22,6 +22,13 @@ const quasarStubs = {
|
|
|
22
22
|
'<input :value="modelValue" @input="$emit(\'update:modelValue\', $event.target.value)" />',
|
|
23
23
|
},
|
|
24
24
|
"q-btn": { template: '<button type="submit"><slot /></button>' },
|
|
25
|
+
"sid-password-ceremony": {
|
|
26
|
+
name: "SidPasswordCeremony",
|
|
27
|
+
props: ["outcome", "refusedText", "length", "fields"],
|
|
28
|
+
emits: ["settled"],
|
|
29
|
+
template:
|
|
30
|
+
'<div class="ceremony" :data-outcome="outcome" :data-refused="refusedText" :data-length="length" :data-fields="fields" />',
|
|
31
|
+
},
|
|
25
32
|
"sid-principal-input": {
|
|
26
33
|
props: ["modelValue", "disable", "label"],
|
|
27
34
|
emits: ["update:modelValue", "update:principal-type"],
|
|
@@ -89,6 +96,23 @@ describe("SidRegistrationForm", () => {
|
|
|
89
96
|
expect(w.findAll("input")).toHaveLength(3);
|
|
90
97
|
});
|
|
91
98
|
|
|
99
|
+
// The ceremony folds as many rows as the form had password fields.
|
|
100
|
+
it("gives the ceremony the form's password fields", async () => {
|
|
101
|
+
for (const [showConfirmPassword, fields] of [
|
|
102
|
+
[true, "2"],
|
|
103
|
+
[false, "1"],
|
|
104
|
+
] as const) {
|
|
105
|
+
const w = mountForm({ claimRequired: false, showConfirmPassword });
|
|
106
|
+
const inputs = w.findAll("input");
|
|
107
|
+
await inputs[0].setValue("owner@example.com");
|
|
108
|
+
for (const input of inputs.slice(1))
|
|
109
|
+
await input.setValue("Secret-Pass-123");
|
|
110
|
+
await w.find("form").trigger("submit");
|
|
111
|
+
await flushPromises();
|
|
112
|
+
expect(w.find(".ceremony").attributes("data-fields")).toBe(fields);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
|
|
92
116
|
// The pasted token reaches registerFn trimmed, and no invite code with it.
|
|
93
117
|
it("submits the trimmed claim token", async () => {
|
|
94
118
|
registerFn.mockClear();
|
|
@@ -125,6 +149,154 @@ describe("SidRegistrationForm", () => {
|
|
|
125
149
|
);
|
|
126
150
|
});
|
|
127
151
|
|
|
152
|
+
// While the registration runs the ceremony stands in for the fields; the
|
|
153
|
+
// account is announced only once its closing animation has played, and the
|
|
154
|
+
// form then settles on its success text, without the fields.
|
|
155
|
+
it("shows the ceremony and announces success after it settles", async () => {
|
|
156
|
+
let finish: () => void = () => {};
|
|
157
|
+
registerFn.mockImplementationOnce(
|
|
158
|
+
() => new Promise<void>((resolve) => (finish = resolve)),
|
|
159
|
+
);
|
|
160
|
+
const w = mountForm();
|
|
161
|
+
const [principal, password, confirm] = w.findAll("input");
|
|
162
|
+
await principal.setValue("user@example.com");
|
|
163
|
+
await password.setValue("Secret-Pass-123");
|
|
164
|
+
await confirm.setValue("Secret-Pass-123");
|
|
165
|
+
await w.find("form").trigger("submit");
|
|
166
|
+
|
|
167
|
+
const ceremony = w.find(".ceremony");
|
|
168
|
+
expect(ceremony.attributes("data-outcome")).toBe("running");
|
|
169
|
+
expect(ceremony.attributes("data-length")).toBe("15");
|
|
170
|
+
expect(w.find("form").isVisible()).toBe(false);
|
|
171
|
+
|
|
172
|
+
finish();
|
|
173
|
+
await flushPromises();
|
|
174
|
+
expect(w.find(".ceremony").attributes("data-outcome")).toBe("accepted");
|
|
175
|
+
expect(w.emitted("success")).toBeUndefined();
|
|
176
|
+
w.findComponent({ name: "SidPasswordCeremony" }).vm.$emit(
|
|
177
|
+
"settled",
|
|
178
|
+
"accepted",
|
|
179
|
+
);
|
|
180
|
+
expect(w.emitted("success")).toHaveLength(1);
|
|
181
|
+
await flushPromises();
|
|
182
|
+
expect(w.find(".ceremony").exists()).toBe(false);
|
|
183
|
+
expect(w.text()).toContain("Account created. Signing you in");
|
|
184
|
+
expect(w.find("form").isVisible()).toBe(false);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// A refusal names its reason in the ceremony, then gives the fields back
|
|
188
|
+
// with the reason above them.
|
|
189
|
+
it("gives the fields back with the reason after a refusal", async () => {
|
|
190
|
+
registerFn.mockRejectedValueOnce(new Error("This identifier is taken"));
|
|
191
|
+
const w = mountForm();
|
|
192
|
+
const [principal, password, confirm] = w.findAll("input");
|
|
193
|
+
await principal.setValue("user@example.com");
|
|
194
|
+
await password.setValue("Secret-Pass-123");
|
|
195
|
+
await confirm.setValue("Secret-Pass-123");
|
|
196
|
+
await w.find("form").trigger("submit");
|
|
197
|
+
await flushPromises();
|
|
198
|
+
|
|
199
|
+
const ceremony = w.find(".ceremony");
|
|
200
|
+
expect(ceremony.attributes("data-outcome")).toBe("refused");
|
|
201
|
+
expect(ceremony.attributes("data-refused")).toBe(
|
|
202
|
+
"This identifier is taken",
|
|
203
|
+
);
|
|
204
|
+
w.findComponent({ name: "SidPasswordCeremony" }).vm.$emit(
|
|
205
|
+
"settled",
|
|
206
|
+
"refused",
|
|
207
|
+
);
|
|
208
|
+
await flushPromises();
|
|
209
|
+
expect(w.find(".ceremony").exists()).toBe(false);
|
|
210
|
+
expect(w.find("form").isVisible()).toBe(true);
|
|
211
|
+
expect(w.text()).toContain("This identifier is taken");
|
|
212
|
+
expect(w.emitted("error")).toHaveLength(1);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
// An application that draws its own progress keeps the `progress` slot:
|
|
216
|
+
// it replaces the ceremony and receives the progress while the operation
|
|
217
|
+
// runs. The final report comes right before the operation resolves, so
|
|
218
|
+
// the slot keeps it for a painted frame; with no closing animation to wait
|
|
219
|
+
// for, the form then announces the result.
|
|
220
|
+
it("lets a progress slot stand in for the ceremony", async () => {
|
|
221
|
+
const frames: FrameRequestCallback[] = [];
|
|
222
|
+
vi.stubGlobal("requestAnimationFrame", (cb: FrameRequestCallback) =>
|
|
223
|
+
frames.push(cb),
|
|
224
|
+
);
|
|
225
|
+
const paint = async () => {
|
|
226
|
+
for (const cb of frames.splice(0)) cb(0);
|
|
227
|
+
await flushPromises();
|
|
228
|
+
};
|
|
229
|
+
let report: (p: { fraction: number; label: string }) => void = () => {};
|
|
230
|
+
let finish: () => void = () => {};
|
|
231
|
+
registerFn.mockImplementationOnce(
|
|
232
|
+
(_id, _pw, _extras, onProgress) =>
|
|
233
|
+
new Promise<void>((resolve) => {
|
|
234
|
+
report = onProgress;
|
|
235
|
+
finish = () => {
|
|
236
|
+
onProgress({ fraction: 1, label: "Done" });
|
|
237
|
+
resolve();
|
|
238
|
+
};
|
|
239
|
+
}),
|
|
240
|
+
);
|
|
241
|
+
const w = mount(SidRegistrationForm, {
|
|
242
|
+
props: { registerFn },
|
|
243
|
+
global: { stubs: quasarStubs },
|
|
244
|
+
slots: {
|
|
245
|
+
progress: `<template #progress="{ progress }">
|
|
246
|
+
<div class="own-gauge">{{ progress?.label }}</div>
|
|
247
|
+
</template>`,
|
|
248
|
+
},
|
|
249
|
+
});
|
|
250
|
+
const [principal, password, confirm] = w.findAll("input");
|
|
251
|
+
await principal.setValue("user@example.com");
|
|
252
|
+
await password.setValue("Secret-Pass-123");
|
|
253
|
+
await confirm.setValue("Secret-Pass-123");
|
|
254
|
+
await w.find("form").trigger("submit");
|
|
255
|
+
|
|
256
|
+
report({ fraction: 0.4, label: "Building the proof" });
|
|
257
|
+
await flushPromises();
|
|
258
|
+
expect(w.find(".ceremony").exists()).toBe(false);
|
|
259
|
+
expect(w.find(".own-gauge").text()).toBe("Building the proof");
|
|
260
|
+
|
|
261
|
+
finish();
|
|
262
|
+
await flushPromises();
|
|
263
|
+
expect(w.find(".own-gauge").text()).toBe("Done");
|
|
264
|
+
expect(w.emitted("success")).toBeUndefined();
|
|
265
|
+
|
|
266
|
+
await paint();
|
|
267
|
+
await paint();
|
|
268
|
+
expect(w.emitted("success")).toHaveLength(1);
|
|
269
|
+
expect(w.find(".own-gauge").exists()).toBe(false);
|
|
270
|
+
expect(w.text()).toContain("Account created. Signing you in");
|
|
271
|
+
vi.unstubAllGlobals();
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
// A background tab may never run animation frames: the result is still
|
|
275
|
+
// announced, once, after a short timer, so a parent's sign-in or
|
|
276
|
+
// navigation is never held back by a paint that does not come.
|
|
277
|
+
it("announces the result without animation frames", async () => {
|
|
278
|
+
vi.useFakeTimers();
|
|
279
|
+
vi.stubGlobal("requestAnimationFrame", () => 0);
|
|
280
|
+
registerFn.mockResolvedValueOnce(undefined);
|
|
281
|
+
const w = mount(SidRegistrationForm, {
|
|
282
|
+
props: { registerFn },
|
|
283
|
+
global: { stubs: quasarStubs },
|
|
284
|
+
slots: { progress: `<div class="own-gauge" />` },
|
|
285
|
+
});
|
|
286
|
+
const [principal, password, confirm] = w.findAll("input");
|
|
287
|
+
await principal.setValue("user@example.com");
|
|
288
|
+
await password.setValue("Secret-Pass-123");
|
|
289
|
+
await confirm.setValue("Secret-Pass-123");
|
|
290
|
+
await w.find("form").trigger("submit");
|
|
291
|
+
await flushPromises();
|
|
292
|
+
expect(w.emitted("success")).toBeUndefined();
|
|
293
|
+
|
|
294
|
+
await vi.advanceTimersByTimeAsync(1000);
|
|
295
|
+
expect(w.emitted("success")).toHaveLength(1);
|
|
296
|
+
vi.unstubAllGlobals();
|
|
297
|
+
vi.useRealTimers();
|
|
298
|
+
});
|
|
299
|
+
|
|
128
300
|
it("renders footer slot", () => {
|
|
129
301
|
const w = mount(SidRegistrationForm, {
|
|
130
302
|
props: { registerFn },
|