@owlmeans/server-auth-identity 0.1.18-rc.12 → 0.1.18-rc.13

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 CHANGED
@@ -12,7 +12,7 @@ Mongo-backed local identity resources and provider account linking for OwlMeans
12
12
  ## Installation
13
13
 
14
14
  ```bash
15
- bun add @owlmeans/server-auth-identity
15
+ bun add @owlmeans/server-auth-identity@^0.1.18-rc.12
16
16
  ```
17
17
 
18
18
  ## Usage
@@ -102,7 +102,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
102
102
  your project's skill store (`.agents/skills/`):
103
103
 
104
104
  ```sh
105
- npx @owlmeans/agent-skills
105
+ npx @owlmeans/agent-skills@^0.1.18-rc.12
106
106
  ```
107
107
 
108
108
  The embedded files are version-matched to this package release. Do not edit them
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
3
  "package": "@owlmeans/server-auth-identity",
4
- "version": "0.1.18-rc.12",
5
- "generatedAt": "2026-09-01T17:39:40.759Z",
4
+ "version": "0.1.18-rc.13",
5
+ "generatedAt": "2026-09-04T22:43:25.434Z",
6
6
  "canonicalRepo": "https://github.com/owlmeans/common",
7
7
  "entries": [
8
8
  {
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: server-auth-identity
3
- description: How to use @owlmeans/server-auth-identity — Mongo-backed local identity resources (account, profile, credentials) and IdentityLinkingService for provider account linking. Auto-invoked when importing appendAuthIdentityResources, identity resource aliases, or IdentityLinkingService.
3
+ description: How to use @owlmeans/server-auth-identity — the Mongo-backed local identity store behind provider logins. Registers the organization-entity registry plus account, profile and credentials resources, the IdentityLinkingService that maps an external provider account onto one local identity, and the EntityResolverService that turns the slug on a token into a stable entity id. Auto-invoked when importing appendAuthIdentityResources, an identity resource alias, IdentityLinkingService or the entity resolver.
4
4
  user-invocable: false
5
5
  ---
6
6
  <!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
@@ -8,61 +8,108 @@ user-invocable: false
8
8
  # @owlmeans/server-auth-identity
9
9
 
10
10
  **Layer:** Server
11
- **Install:** `"@owlmeans/server-auth-identity": "^0.1.18-rc.12"` in `dependencies`
11
+ **Install:** `"@owlmeans/server-auth-identity": "^0.1.18-rc.13"` in `dependencies`
12
+
13
+ The local identity store a deployment owns when it does not delegate identity to an external IAM.
14
+ It answers two questions: *who is this person here* (account / profile / credentials) and *which
15
+ organization entity is this* (the org-entity registry and its resolver).
12
16
 
13
17
  ## Key Exports
14
18
 
15
19
  | Export | Description |
16
20
  |--------|-------------|
17
- | `appendAuthIdentityResources(context, dbAlias?)` | Register all three identity resources + linking service on a context |
18
- | `makeIdentityAccountResource(dbAlias?)` | Mongo resource for `IdentityAccount` (one per user) |
19
- | `makeIdentityProfileResource(dbAlias?)` | Mongo resource for `IdentityProfile` (ties user to entity + role) |
20
- | `makeIdentityCredentialsResource(dbAlias?)` | Mongo resource for `IdentityCredentials` (provider link per profile) |
21
- | `makeIdentityLinkingService()` | Service implementing `IdentityLinkingService` — links external providers to local profiles |
21
+ | `appendAuthIdentityResources(context, dbAlias?)` | Register the four resources, the linking service and the entity resolver in one call |
22
+ | `makeOrgEntityResource(dbAlias?)` | Mongo resource for `OrgEntity` — the organization-entity registry |
23
+ | `makeIdentityAccountResource(dbAlias?)` | Mongo resource for `IdentityAccount` (one per person) |
24
+ | `makeIdentityProfileResource(dbAlias?)` | Mongo resource for `IdentityProfile` (one person inside one organization entity, with a role) |
25
+ | `makeIdentityCredentialsResource(dbAlias?)` | Mongo resource for `IdentityCredentials` (one provider link per profile) |
26
+ | `makeIdentityLinkingService()` | The `IdentityLinkingService` implementation |
27
+ | `makeEntityResolverService(alias?)` | The `EntityResolverService` implementation, cached for 30 s per resolved value |
28
+ | `AUTH_IDENTITY_ORG_ENTITY` | Resource alias `'auth-identity:org-entity'` |
22
29
  | `AUTH_IDENTITY_ACCOUNT` | Resource alias `'auth-identity:account'` |
23
30
  | `AUTH_IDENTITY_PROFILE` | Resource alias `'auth-identity:profile'` |
24
31
  | `AUTH_IDENTITY_CREDENTIALS` | Resource alias `'auth-identity:credentials'` |
25
32
  | `AUTH_IDENTITY_LINKING` | Service alias `'auth-identity:linking'` |
33
+ | `AUTH_IDENTITY_DB_ALIAS` | `'auth-identity'` — the suggested db-config alias whose `resourcePrefix` scopes the prefix to identity collections only |
34
+ | `AUTH_IDENTITY_ORG_ENTITY_COLLECTION`, `AUTH_IDENTITY_ACCOUNT_COLLECTION`, `AUTH_IDENTITY_PROFILE_COLLECTION`, `AUTH_IDENTITY_CREDENTIALS_COLLECTION` | Colon-free Mongo collection base names (`org-entity`, `account`, `profile`, `credentials`) |
35
+ | `MAX_ENTITY_SLUG_ATTEMPTS` | How many word slugs to try before giving up minting a free one |
36
+ | `LOGIN_SERVICE_PREFIX`, `EXTERNAL_KEY_DELIMITER` | `'service'` and `':'` — the grammar of every derived key below |
37
+
38
+ The resource **aliases** keep their colon-delimited form and are lookup keys only; the Mongo
39
+ collection names are the colon-free `*_COLLECTION` values, optionally prefixed by the db config's
40
+ `resourcePrefix`.
26
41
 
27
42
  ## Types
28
43
 
29
44
  | Type | Description |
30
45
  |------|-------------|
31
- | `IdentityAccount` | `Profile & ResourceRecord` — `id`, `credential` (unique entity slug) |
32
- | `IdentityProfile` | `Profile & ResourceRecord` — `id`, `profileId`, `userId?`, `role`, `entityId`, `scopes`, `expiresAt?` |
33
- | `IdentityCredentials` | `AuthCredentials & ResourceRecord` — `profileId`, `type`, `userId` (external key), `credential` (login-service key) |
46
+ | `OrgEntity` | `id`, `slug`, `formerSlugs?`, `iamKey`, `names?`, `createdAt`, `updatedAt?` |
47
+ | `IdentityAccount` | `Profile` without `entitySlug`, plus `id`, `credential`, `entityId?` |
48
+ | `IdentityProfile` | `Profile` without `entitySlug`, plus `id`, `profileId`, `userId?`, `role`, `entityId?`, `expiresAt?` |
49
+ | `IdentityCredentials` | `AuthCredentials` + `profileId` |
34
50
  | `IdentityLinkingService` | `getLinkedProfile`, `linkProfile`, `linkCredentials`, `getOwnerProfiles`, `getOwnerCredentials` |
51
+ | `AccountMeta` | `{ username, force? }` — the second argument to `linkProfile` |
52
+ | `OrgEntityResource`, `IdentityAccountResource`, `IdentityProfileResource`, `IdentityCredentialsResource` | Typed `MongoResource` aliases |
53
+ | `IdentityConfig`, `IdentityContext` | Server config/context shapes |
54
+ | `GoogleUserInfo` | The Google userinfo claim set |
55
+ | `EmailIdentityArgs`, `EmailIdentityPayload`, `IdentityIamExtension` | Declaration-only seam for an IAM-backed email find-or-create |
56
+
57
+ The three `*Identity*` seam types are declarations and nothing more: no function in this
58
+ package accepts or returns them, and no consumer imports them. An email login is wired through
59
+ `IdentityLinkingService.getLinkedProfile` / `linkProfile`, which is what the OTP plugin does.
60
+
61
+ ## The organization entity
62
+
63
+ The customer organization is an **organization entity**, and this package is the registry that owns
64
+ it. Two values name one organization and they never substitute for each other:
65
+
66
+ - **`entityId`** — `OrgEntity.id`. Stable, never on the wire. Every account, profile and
67
+ organization-scoped record keys on it.
68
+ - **`entitySlug`** — `OrgEntity.slug`. Renameable, human-readable, and the only organization value
69
+ an auth payload carries. Every `AuthPayload` this service returns carries `entitySlug`, resolved
70
+ from the stored `entityId`; an id that no longer resolves yields `undefined` rather than leaking a
71
+ raw id where a consumer would read it as a slug.
72
+
73
+ A rename is one write to `OrgEntity`, because nothing else stores the slug. The retired name moves
74
+ into `formerSlugs` and keeps resolving, so tokens minted before the rename and third-party records
75
+ still quoting the old value keep working.
76
+
77
+ `iamKey` is frozen at creation and never recomputed. It is what systems that cannot be renamed —
78
+ an IAM realm, a namespace, a storage prefix — are told once; `mintName(id, key, mint)` is the
79
+ read-before-mint that freezes such a derived name into `OrgEntity.names`.
80
+
81
+ Registering the resolver is also the signal that this deployment **has** organizations: without it
82
+ `request.entity` stays undefined and every consumer falls back to treating the token's slug as the
83
+ only entity value there is.
35
84
 
36
85
  ## Usage
37
86
 
38
- ### Register in context
87
+ ### Register in a context
39
88
 
40
89
  ```typescript
41
90
  import { appendAuthIdentityResources } from '@owlmeans/server-auth-identity'
42
91
 
43
- // In your makeContext factory, after appendMongo/appendRedis/appendAuthService:
92
+ // in makeContext, after the Mongo/Redis services and appendAuthService:
44
93
  appendAuthIdentityResources(context)
45
94
  ```
46
95
 
47
- This registers three Mongo resources and the linking service in one call.
48
-
49
- ### Read a profile (gate / handler)
96
+ ### Read a profile (gate or handler)
50
97
 
51
98
  ```typescript
52
99
  import { AUTH_IDENTITY_PROFILE } from '@owlmeans/server-auth-identity'
53
100
  import type { IdentityProfileResource } from '@owlmeans/server-auth-identity'
101
+ import { requireEntityKey } from '@owlmeans/auth-common'
54
102
 
55
- const profileRes = ctx.resource<IdentityProfileResource>(AUTH_IDENTITY_PROFILE)
56
-
57
- // One record by any field, or by several at once — read-only, and typed by the resource:
58
- const profile = await profileRes.load({ profileId })
59
- const scoped = await profileRes.load({ entityId, profileId })
103
+ const profiles = ctx.resource<IdentityProfileResource>(AUTH_IDENTITY_PROFILE)
104
+ const entityId = requireEntityKey(req)
60
105
 
61
- // Every match, newest first:
62
- const { items, total } = await profileRes.list({ entityId }, { sort: [{ field: 'createdAt', order: 'desc' }] })
106
+ const profile = await profiles.load({ entityId, profileId })
107
+ const { items } = await profiles.list({ entityId }, { sort: [{ field: 'createdAt', order: 'desc' }] })
63
108
  ```
64
109
 
65
- ### Link an external provider to a local profile
110
+ Query profiles by `entityId`, never by the slug — `requireEntityKey` hands you the resolved id.
111
+
112
+ ### Link a provider account to a local identity
66
113
 
67
114
  ```typescript
68
115
  import { AUTH_IDENTITY_LINKING } from '@owlmeans/server-auth-identity'
@@ -70,47 +117,75 @@ import type { IdentityLinkingService } from '@owlmeans/server-auth-identity'
70
117
 
71
118
  const linking = ctx.service<IdentityLinkingService>(AUTH_IDENTITY_LINKING)
72
119
 
73
- // First login — creates account + profile + credentials:
74
- const payload = await linking.linkProfile(providerDetails, { username: 'display name' })
75
-
76
- // Subsequent logins — finds existing profile by external key:
77
- const payload = await linking.getLinkedProfile(providerDetails)
78
- ```
79
-
80
- Both return `AuthPayload` with `{ type, role, userId, profileId, entityId, scopes }`.
81
-
82
- ### ProviderProfileDetails shape (input)
83
-
84
- ```typescript
85
- interface ProviderProfileDetails {
86
- type: string // e.g. 'google'
87
- service: string // e.g. 'google' (provider service alias)
88
- userId: string // provider subject (sub claim)
89
- profileId?: string // optional existing profile to link to
120
+ const details = {
121
+ type: 'google-oauth', // the AuthenticationType
122
+ service: 'google', // the provider service alias
123
+ clientId: 'google', // the provider client this login came through
124
+ userId: providerSub, // the provider's subject claim
90
125
  }
91
- ```
92
-
93
- ## Key Derivation Conventions
94
-
95
- - **Profile userId**: the account's mongo id — a **declared ObjectId reference**
96
- (`resource.reference('userId', AUTH_IDENTITY_ACCOUNT)`): stored as `ObjectId`, exchanged
97
- as a string, auto-indexed and auto-migrated at boot. The only reference in the trio.
98
- - **Account credential**: unique Base58 slug (16 chars) — serves as the local `entityId`
99
- - **Profile profileId**: `"{type}:{accountId}"` — stable across provider re-links; a composite key, NOT a reference
100
- - **Credentials userId** (external key): `"{type}:{service}:{providerSub}"` — uniquely identifies the external account. Same name as `profile.userId`, entirely different meaning — never convert or compare across the two
101
- - **Credentials credential** (login-service key): `"service:{type}:{service}"` — groups credentials by provider
102
126
 
103
- `AuthPayload.userId` is emitted as `profile.userId ?? profile.profileId`, so a value flowing
104
- back into a `userId` query may be a composite key — criteria tolerate it (matches nothing)
105
- by the reference conversion's design.
127
+ // Returning login: find the credential, then its profile.
128
+ let payload = await linking.getLinkedProfile(details)
129
+ // First login by this method: link it to the person's identity, registering one if new.
130
+ payload ??= await linking.linkProfile(details, { username: 'person@example.org' })
131
+ ```
106
132
 
107
- ## Resource Indexes
133
+ Both return an `AuthPayload` — `{ type, role, userId, profileId, entitySlug, scopes }`.
134
+
135
+ `linkCredentials(details)` adds a credential row to an already-linked profile named by
136
+ `details.profileId`. `getOwnerProfiles(entityId)` lists an organization's profiles;
137
+ `getOwnerCredentials(userId, entityId?, type?)` returns one profile's credential as an
138
+ `AuthCredentials`.
139
+
140
+ ## One person, one platform identity
141
+
142
+ `linkProfile` **links** a person the platform already knows and registers only a genuinely new one.
143
+ It matches on the account's `name`, which every shipped provider establishes as a verified email
144
+ before it gets here (Google asserts the address, OTP proves possession of it, the supervisor key is
145
+ a full-trust developer credential). A second sign-in method therefore adds a second **credential**
146
+ to one profile — never a second account, profile or organization entity.
147
+
148
+ Two rows can legitimately carry one address, and only one of them is a platform login: an
149
+ organization's invited **end user** is a person-shaped row for the generated application to
150
+ authenticate, deliberately separate from the platform credential. They are told apart by the
151
+ profile's `credential`: this service is the only writer of `"service:{type}:{service}"`, so a
152
+ profile carrying one is a platform login and a profile carrying none is somebody's end user.
153
+
154
+ `linkProfile(details, { username, force: true })` skips the lookup and registers a fresh identity
155
+ regardless. Use it only where a genuinely separate identity is intended.
156
+
157
+ ## Key derivation conventions
158
+
159
+ - **Account `credential`** — a unique 16-character Base58 slug, the account's own stable key.
160
+ Generation retries up to five times on a duplicate-key collision; that is the designed behaviour
161
+ for the slug space, not an error path.
162
+ - **Account / profile `entityId`** — the `OrgEntity` record id. Every first registration creates the
163
+ entity record **first**, because it owns the id everything else is keyed by and the frozen
164
+ `iamKey`.
165
+ - **Profile `userId`** — the account's Mongo id, and a **declared ObjectId reference**
166
+ (`resource.reference('userId', AUTH_IDENTITY_ACCOUNT)`): stored as `ObjectId`, exchanged as a
167
+ string, auto-indexed and auto-migrated at boot. The only reference in the set.
168
+ - **Profile `profileId`** — `"{type}:{accountId}"`. A composite key, stable across provider
169
+ re-links, and NOT a reference.
170
+ - **Credentials `userId`** (external key) — `"{type}:{service}:{providerSub}"`. Same field name as
171
+ `profile.userId`, entirely different meaning; never compare or convert across the two.
172
+ - **Credentials / profile `credential`** (login-service key) — `"service:{type}:{service}"`.
173
+
174
+ `AuthPayload.userId` is emitted as `profile.userId ?? profile.profileId`, so a value coming back
175
+ into a `userId` query may be a composite key. The reference conversion tolerates it by matching
176
+ nothing.
177
+
178
+ ## Resource indexes
108
179
 
109
180
  | Resource | Index | Fields | Notes |
110
181
  |----------|-------|--------|-------|
182
+ | OrgEntity | `slug` | `{ slug: 1 }` | unique — the only thing between two organizations and one public name |
183
+ | OrgEntity | `iamKey` | `{ iamKey: 1 }` | unique — a duplicate would hand one organization another's realm |
184
+ | OrgEntity | `formerSlugs` | `{ formerSlugs: 1 }` | not unique; a retired slug must stay findable |
111
185
  | Account | `credential` | `{ credential: 1 }` | unique |
112
186
  | Account | `entityId` | `{ entityId: 1 }` | |
113
187
  | Account | `secret` | `{ secret: 1 }` | unique, sparse |
188
+ | Account | `name` | `{ name: 1 }` | not unique — one address is legitimately both a platform account and an end-user row |
114
189
  | Profile | `userId` | `{ userId: 1 }` | |
115
190
  | Profile | `entityId` | `{ entityId: 1 }` | |
116
191
  | Profile | `role` | `{ role: 1, entityId: 1 }` | |
@@ -118,32 +193,42 @@ by the reference conversion's design.
118
193
  | Credentials | `provider` | `{ type: 1, userId: 1, credential: 1 }` | unique |
119
194
  | Credentials | `profileId` | `{ profileId: 1 }` | |
120
195
 
121
- ## Important Gotchas
122
-
123
- - **`Resource.take()` is destructive** — it deletes the record it returns. For read-only identity lookups in gates or handlers use `load(where)`, which answers a multi-field query in one call (`load({ type, userId, credential })`), or `list(where, opts)` when you need every match. Never use `take()` in authorization checks.
124
- - **`IdentityLinkingService` is compatible with `AccountLinkingService`** from `@owlmeans/server-oidc-rp` but defined independently to avoid circular dependency. Both return `AuthPayload`.
125
- - First-login profiles are created with `ALL_SCOPES` and `AuthRole.User` — the gate layer is initially permissive per entity. Restrict scopes as needed for fine-grained authorization.
126
- - Account `credential` slug generation retries up to 5 times on duplicate key collision — this is the designed behavior for the Base58 slug space.
127
-
128
- ## Product-Viable Usage Notes
129
-
130
- - Register `appendAuthIdentityResources(context)` immediately after `appendAuthService(context)` so guards can authenticate before product gates authorize.
131
- - Product gates should load `AUTH_IDENTITY_PROFILE` read-only, verify `entityId`, reject expired/blocked profiles, then compare gate params against profile scopes.
132
- - Google login goes through OIDC provider config, but the durable authorization source is the local `IdentityProfile` record.
133
- - Account `credential` is the public local entity slug. Keep it separate from Mongo account `id` and provider subject identifiers.
134
-
135
- ## Relationship to Other Auth Packages
136
-
137
- - **`@owlmeans/server-auth`** — manages the auth guard (token verification). This package manages the identity store underneath.
138
- - **`@owlmeans/server-oidc-rp`** — the OIDC relying party that can call `IdentityLinkingService` during OAuth callback to map external identity to local profile. The linking interface is compatible but independent.
139
- - **`@owlmeans/oidc`** — provides `ProviderProfileDetails` and `OidcProviderDescriptor` types consumed by the linking service and the OIDC client.
196
+ ## Gotchas
197
+
198
+ - **`Resource.take()` is destructive** — it deletes the record it returns. Identity lookups in gates
199
+ and handlers use `load(where)`, which answers a multi-field query in one call
200
+ (`load({ type, userId, credential })`), or `list(where, opts)` for every match. Never `take()` in
201
+ an authorization check.
202
+ - **`IdentityLinkingService` is compatible with `AccountLinkingService`** from
203
+ `@owlmeans/server-oidc-rp` but declared independently, to keep the dependency acyclic. Both return
204
+ `AuthPayload`.
205
+ - First-login profiles are created with `ALL_SCOPES` and `AuthRole.User` — the gate layer starts
206
+ permissive within one organization entity. Narrow the scopes where finer authorization is needed.
207
+ - `mintSlug` throws `SyntaxError('entity:slug-exhausted')` after `MAX_ENTITY_SLUG_ATTEMPTS` word
208
+ bases (three numeric suffixes each). `rename` throws `entity:slug-malformed:<slug>` against
209
+ `ENTITY_SLUG_PATTERN` and `entity:slug-taken:<slug>` for a name any entity has ever answered to.
210
+ - The resolver caches every name a hit was found under — id, slug, former slug, `iamKey` — for 30
211
+ seconds. A rename is therefore visible to other replicas within that window, which is survivable
212
+ precisely because the old slug keeps resolving.
213
+
214
+ ## Relationship to other auth packages
215
+
216
+ - **`@owlmeans/server-auth`** — verifies the bearer token and canonicalizes the slug on the
217
+ credential through this package's resolver. This package is the store underneath.
218
+ - **`@owlmeans/auth-common`** — declares `EntityResolverService` (implemented here),
219
+ `ENTITY_RESOLVER` and `OrgEntityRef`, and carries the `entityKeyOf` / `requireEntityKey` /
220
+ `attachEntity` helpers that read what this package resolves.
221
+ - **`@owlmeans/server-oidc-rp`** — the relying party that calls the linking service during an OAuth
222
+ callback.
223
+ - **`@owlmeans/server-auth-otp`** — the email-OTP plugin resolves its user through the linking
224
+ service.
225
+ - **`@owlmeans/oidc`** — supplies `ProviderProfileDetails` and `OidcProviderDescriptor`.
140
226
 
141
227
  ## Depends On
142
228
 
143
229
  - `@owlmeans/auth` — `AuthPayload`, `AuthRole`, `ALL_SCOPES`, `Profile`, `AuthCredentials`
144
- - `@owlmeans/basic-ids` — `createIdOfLength`, `IdStyle`
145
- - `@owlmeans/context` — `appendContextual`
146
- - `@owlmeans/mongo-resource` — `makeMongoResource`
147
- - `@owlmeans/oidc` — `ProviderProfileDetails`
148
- - `@owlmeans/resource` — `ResourceRecord`
149
- - `@owlmeans/server-context` — `ServerConfig`, `ServerContext`
230
+ - `@owlmeans/auth-common` — `EntityResolverService`, `ENTITY_RESOLVER`, `ENTITY_SLUG_PATTERN`
231
+ - `@owlmeans/basic-ids` — `createIdOfLength`, `IdStyle`, `generateWordSlug`, `nextSlugCandidate`
232
+ - `@owlmeans/mongo-resource` — `makeMongoResource`, declared references and indexes
233
+ - `@owlmeans/context`, `@owlmeans/resource`, `@owlmeans/oidc`, `@owlmeans/server-context`
234
+ - `mongodb` (peer) — the driver behind the Mongo resources this package registers
@@ -1 +1 @@
1
- {"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAKrD,eAAO,MAAM,2BAA2B,GACtC,SAAS,YAAY,CAAC,GAAG,CAAC,EAC1B,UAAU,MAAM,KACf,IAUF,CAAA"}
1
+ {"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAKrD,eAAO,MAAM,2BAA2B,YAC7B,YAAY,CAAC,GAAG,CAAC,YAChB,MAAM,KACf,IAUF,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../src/resolver.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,qBAAqB,EAAgB,MAAM,uBAAuB,CAAA;AA6BhF,eAAO,MAAM,yBAAyB,GACpC,QAAO,MAAwB,KAC9B,qBAkIF,CAAA"}
1
+ {"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../src/resolver.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,qBAAqB,EAAgB,MAAM,uBAAuB,CAAA;AA6BhF,eAAO,MAAM,yBAAyB,WAC7B,MAAM,KACZ,qBAkIF,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"resolver.js","sourceRoot":"","sources":["../src/resolver.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"resolver.js","sourceRoot":"","sources":["../src/resolver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAIpD,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAA;AAC5E,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAEzE,OAAO,EAAE,wBAAwB,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAA;AAIhF;;;;;;;GAOG;AACH,MAAM,SAAS,GAAG,MAAM,CAAA;AAExB,wFAAwF;AACxF,MAAM,KAAK,GAAG,CAAC,MAAwB,EAAuB,EAAE,CAC9D,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IACtB,EAAE,EAAE,MAAM,CAAC,EAAG,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM;CAC1F,CAAA;AAOH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CACvC,KAAK,GAAW,eAAe,EACR,EAAE;IACzB,4FAA4F;IAC5F,mDAAmD;IACnD,MAAM,KAAK,GAAG,IAAI,GAAG,EAAsB,CAAA;IAC3C,MAAM,GAAG,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAA;IAE5B,MAAM,QAAQ,GAAG,CAAC,MAAwB,EAAE,GAAG,IAAc,EAAoB,EAAE;QACjF,MAAM,EAAE,GAAG,GAAG,EAAE,CAAA;QAChB,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,IAAI,GAAG,KAAK,EAAE;gBAAE,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAA;QAChD,CAAC;QACD,IAAI,MAAM,IAAI,IAAI,EAAE,CAAC;YACnB,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,EAAG,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAA;YACrC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAA;YACtC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAA;YACxC,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,WAAW,IAAI,EAAE;gBAAE,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAA;QAClF,CAAC;QAED,OAAO,MAAM,CAAA;IACf,CAAC,CAAA;IAED,MAAM,MAAM,GAAG,CAAC,MAAiB,EAAE,EAAE;QACnC,KAAK,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;YACzF,IAAI,GAAG,IAAI,IAAI;gBAAE,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;QACpC,CAAC;IACH,CAAC,CAAA;IAED,MAAM,QAAQ,GAAG,GAAsB,EAAE,CACtC,OAAO,CAAC,GAAe,CAAC,QAAQ,CAAoB,wBAAwB,CAAC,CAAA;IAEhF,MAAM,IAAI,GAAG,KAAK,EAAE,KAAa,EAA6B,EAAE;QAC9D,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QAC/B,IAAI,MAAM,IAAI,IAAI,IAAI,GAAG,EAAE,GAAG,MAAM,CAAC,EAAE,GAAG,SAAS,EAAE,CAAC;YACpD,OAAO,MAAM,CAAC,MAAM,CAAA;QACtB,CAAC;QAED,MAAM,GAAG,GAAG,QAAQ,EAAE,CAAA;QACtB,4FAA4F;QAC5F,0FAA0F;QAC1F,yFAAyF;QACzF,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QAClC,IAAI,IAAI,IAAI,IAAI;YAAE,OAAO,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;QAE9C,MAAM,UAAU,GAA0B;YACxC,EAAE,IAAI,EAAE,KAAK,EAAE;YACf,EAAE,WAAW,EAAE,EAAE,SAAS,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE;YACvC,EAAE,MAAM,EAAE,KAAK,EAAE;SAClB,CAAA;QACD,KAAK,MAAM,KAAK,IAAI,UAAU,EAAE,CAAC;YAC/B,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;YACnC,IAAI,KAAK,IAAI,IAAI;gBAAE,OAAO,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;QAClD,CAAC;QAED,OAAO,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IAC9B,CAAC,CAAA;IAED,MAAM,OAAO,GAA0B,gBAAgB,CAAwB,KAAK,EAAE;QACpF,OAAO,EAAE,KAAK,EAAC,KAAK,EAAC,EAAE,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,CAAC;QAEhD,IAAI,EAAE,KAAK,EAAC,EAAE,EAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,QAAQ,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QAEhE,QAAQ,EAAE,KAAK,IAAI,EAAE;YACnB,MAAM,GAAG,GAAG,QAAQ,EAAE,CAAA;YACtB,4FAA4F;YAC5F,yFAAyF;YACzF,oDAAoD;YACpD,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,wBAAwB,EAAE,EAAE,OAAO,EAAE,CAAC;gBACrE,MAAM,IAAI,GAAG,gBAAgB,EAAE,CAAA;gBAC/B,KAAK,IAAI,MAAM,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC;oBAC3C,MAAM,SAAS,GAAG,iBAAiB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;oBACjD,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC;wBAC3B,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,SAAS,EAAE,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC;qBACxE,CAAC,CAAA;oBACF,IAAI,KAAK,IAAI,IAAI;wBAAE,OAAO,SAAS,CAAA;gBACrC,CAAC;YACH,CAAC;YAED,MAAM,IAAI,WAAW,CAAC,uBAAuB,CAAC,CAAA;QAChD,CAAC;QAED,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE;YACzB,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBACpC,MAAM,IAAI,WAAW,CAAC,yBAAyB,IAAI,EAAE,CAAC,CAAA;YACxD,CAAC;YAED,MAAM,GAAG,GAAG,QAAQ,EAAE,CAAA;YACtB,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;YAChC,IAAI,MAAM,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;gBACzB,OAAO,KAAK,CAAC,MAAM,CAAE,CAAA;YACvB,CAAC;YAED,6FAA6F;YAC7F,8DAA8D;YAC9D,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC;gBAC3B,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,SAAS,EAAE,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;aACxD,CAAC,CAAA;YACF,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;gBACrC,MAAM,IAAI,WAAW,CAAC,qBAAqB,IAAI,EAAE,CAAC,CAAA;YACpD,CAAC;YAED,MAAM,CAAC,MAAM,CAAC,CAAA;YACd,MAAM,WAAW,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,IAAI,EAAE,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;iBAC3E,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,KAAK,IAAI,CAAC,CAAA;YACpC,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,EAAE,GAAG,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,EAAE,CAAC,CAAA;YAEzF,OAAO,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAE,CAAA;QAClC,CAAC;QAED,QAAQ,EAAE,KAAK,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;YAChC,MAAM,GAAG,GAAG,QAAQ,EAAE,CAAA;YACtB,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;YAChC,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,CAAA;YACpC,uFAAuF;YACvF,sFAAsF;YACtF,IAAI,QAAQ,IAAI,IAAI,IAAI,QAAQ,KAAK,EAAE,EAAE,CAAC;gBACxC,OAAO,QAAQ,CAAA;YACjB,CAAC;YAED,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAE,CAAC,CAAA;YACnC,MAAM,CAAC,MAAM,CAAC,CAAA;YACd,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC;gBAC/B,GAAG,MAAM,EAAE,KAAK,EAAE,EAAE,GAAG,MAAM,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE;aAC5E,CAAC,CAAA;YACF,QAAQ,CAAC,OAAO,CAAC,CAAA;YAEjB,OAAO,OAAO,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,IAAI,MAAM,CAAA;QACvC,CAAC;KACF,CAAC,CAAA;IAEF,OAAO,OAAO,CAAA;AAChB,CAAC,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../src/resource.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAmB,uBAAuB,EAAmB,uBAAuB,EAAuB,2BAA2B,EAAa,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAOpM;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,GAAI,UAAU,MAAM,KAAG,iBAQxD,CAAA;AAED,eAAO,MAAM,2BAA2B,GAAI,UAAU,MAAM,KAAG,uBAQ9D,CAAA;AAED,eAAO,MAAM,2BAA2B,GAAI,UAAU,MAAM,KAAG,uBAe9D,CAAA;AAED,eAAO,MAAM,+BAA+B,GAAI,UAAU,MAAM,KAAG,2BAOlE,CAAA"}
1
+ {"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../src/resource.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAmB,uBAAuB,EAAmB,uBAAuB,EAAuB,2BAA2B,EAAa,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAOpM;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,aAAc,MAAM,KAAG,iBAQxD,CAAA;AAED,eAAO,MAAM,2BAA2B,aAAc,MAAM,KAAG,uBAkB9D,CAAA;AAED,eAAO,MAAM,2BAA2B,aAAc,MAAM,KAAG,uBAe9D,CAAA;AAED,eAAO,MAAM,+BAA+B,aAAc,MAAM,KAAG,2BAOlE,CAAA"}
package/build/resource.js CHANGED
@@ -21,6 +21,16 @@ export const makeIdentityAccountResource = (dbAlias) => {
21
21
  resource.index('credential', { credential: 1 }, { unique: true });
22
22
  resource.index('entityId', { entityId: 1 });
23
23
  resource.index('secret', { secret: 1 }, { unique: true, sparse: true });
24
+ /**
25
+ * The account's login name — an email, for every provider shipped today.
26
+ *
27
+ * Indexed because `linkProfile` reads it on every registration: a person signing in by a second
28
+ * method must land on the identity they already have, not a new one. NOT unique — the same
29
+ * address legitimately appears twice, once for the platform account and once for an
30
+ * organization's END USER record (`inviteUser`), and those are different people-shaped rows for
31
+ * different audiences. The two are told apart by the profile, never by the account.
32
+ */
33
+ resource.index('name', { name: 1 });
24
34
  return resource;
25
35
  };
26
36
  export const makeIdentityProfileResource = (dbAlias) => {
@@ -1 +1 @@
1
- {"version":3,"file":"resource.js","sourceRoot":"","sources":["../src/resource.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"resource.js","sourceRoot":"","sources":["../src/resource.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAA8F,sBAAsB,EAAE,MAAM,YAAY,CAAA;AAmBpJ,eAAO,MAAM,0BAA0B,QAAO,sBAqM7C,CAAA"}
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAA8F,sBAAsB,EAAE,MAAM,YAAY,CAAA;AAmBpJ,eAAO,MAAM,0BAA0B,QAAO,sBA8Q7C,CAAA"}
package/build/service.js CHANGED
@@ -23,6 +23,40 @@ export const makeIdentityLinkingService = () => {
23
23
  const entity = await ctx.service(ENTITY_RESOLVER).byId(entityId);
24
24
  return entity?.slug;
25
25
  };
26
+ /**
27
+ * The platform identity this person already has, if any.
28
+ *
29
+ * One human, one email, one profile — whichever way they sign in. Every provider that reaches
30
+ * `linkProfile` establishes the email first (Google asserts a verified address, the OTP flow
31
+ * proves possession of it, the supervisor key is a full-trust developer credential), so the
32
+ * name is the identity and a second method is a second CREDENTIAL on it, never a second person.
33
+ * Without this the platform minted a whole new organization per method: the same address signed
34
+ * in by Google and by key ended up in two entities, and each could see only the projects the
35
+ * other had not created.
36
+ *
37
+ * Two rows can carry one address, and only one of them is a platform login. `inviteUser`
38
+ * (`@owlmeans/iam-integrated`) writes an END USER record for the same person — the identity the
39
+ * GENERATED application authenticates, deliberately kept apart from the platform credential.
40
+ * Those rows carry no login service, which is what tells the two apart here: this service is
41
+ * the only writer of `credential = "service:{type}:{service}"`, so a profile that has one is a
42
+ * platform login and a profile that has none is somebody's end user.
43
+ */
44
+ const findPlatformIdentity = async (username) => {
45
+ const name = username.trim();
46
+ if (name === '')
47
+ return null;
48
+ const ctx = service.ctx;
49
+ const accountResource = ctx.resource(AUTH_IDENTITY_ACCOUNT);
50
+ const profileResource = ctx.resource(AUTH_IDENTITY_PROFILE);
51
+ const { items: accounts } = await accountResource.list({ name });
52
+ for (const account of accounts) {
53
+ const { items: profiles } = await profileResource.list({ userId: account.id });
54
+ const platform = profiles.find(profile => profile.credential?.startsWith(`${LOGIN_SERVICE_PREFIX}${EXTERNAL_KEY_DELIMITER}`) === true);
55
+ if (platform != null)
56
+ return platform;
57
+ }
58
+ return null;
59
+ };
26
60
  const service = appendContextual(AUTH_IDENTITY_LINKING, {
27
61
  getLinkedProfile: async (details) => {
28
62
  const ctx = service.ctx;
@@ -54,6 +88,27 @@ export const makeIdentityLinkingService = () => {
54
88
  const credsResource = ctx.resource(AUTH_IDENTITY_CREDENTIALS);
55
89
  const entityResource = ctx.resource(AUTH_IDENTITY_ORG_ENTITY);
56
90
  const resolver = ctx.service(ENTITY_RESOLVER);
91
+ // A person the platform already knows is LINKED, not registered again. Everything below
92
+ // this point creates an organization, an account and a profile, and doing that for a second
93
+ // login method is what put one email in two entities.
94
+ const known = meta.force === true ? null : await findPlatformIdentity(meta.username);
95
+ if (known != null) {
96
+ await credsResource.create({
97
+ challenge: '',
98
+ type: details.type,
99
+ userId: externalKey(details),
100
+ profileId: known.profileId,
101
+ credential: loginService(details),
102
+ });
103
+ return {
104
+ type: details.type,
105
+ role: known.role,
106
+ userId: known.userId ?? known.profileId,
107
+ profileId: known.profileId,
108
+ entitySlug: await slugOf(known.entityId),
109
+ scopes: known.scopes,
110
+ };
111
+ }
57
112
  // Every account gets its own organization. The entity record is created FIRST because it
58
113
  // owns the two values that outlive everything else here: the id the account and its profiles
59
114
  // are keyed by, and the frozen `iamKey` that realms and object names are minted from.
@@ -144,6 +199,18 @@ export const makeIdentityLinkingService = () => {
144
199
  }
145
200
  return result;
146
201
  },
202
+ unlinkCredentials: async (details) => {
203
+ const ctx = service.ctx;
204
+ const credsResource = ctx.resource(AUTH_IDENTITY_CREDENTIALS);
205
+ const cred = await credsResource.load({
206
+ type: details.type,
207
+ userId: externalKey(details),
208
+ credential: loginService(details),
209
+ });
210
+ if (cred?.id == null)
211
+ return;
212
+ await credsResource.delete(cred.id);
213
+ },
147
214
  getOwnerProfiles: async (entityId) => {
148
215
  const ctx = service.ctx;
149
216
  const profileResource = ctx.resource(AUTH_IDENTITY_PROFILE);
@@ -1 +1 @@
1
- {"version":3,"file":"service.js","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"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"}
package/build/types.d.ts CHANGED
@@ -70,6 +70,17 @@ export interface IdentityLinkingService extends InitializedService {
70
70
  getLinkedProfile: (details: ProviderProfileDetails) => Promise<AuthPayload | null>;
71
71
  linkProfile: (details: ProviderProfileDetails, meta: AccountMeta) => Promise<AuthPayload>;
72
72
  linkCredentials: (details: ProviderProfileDetails) => Promise<AuthPayload>;
73
+ /**
74
+ * Forget which profile an external login maps to, so the next `linkProfile` establishes it anew.
75
+ *
76
+ * The stored mapping is keyed `{type, userId, credential}` under a unique index, so a caller
77
+ * that has decided the existing mapping is wrong cannot simply write over it — it has to be
78
+ * retired first. That decision belongs to whoever owns the login path (a decorator serving a
79
+ * different population than the platform's own customers, say); the key format belongs here.
80
+ *
81
+ * Idempotent: a login that maps to nothing is already in the state this promises.
82
+ */
83
+ unlinkCredentials: (details: ProviderProfileDetails) => Promise<void>;
73
84
  getOwnerProfiles: (entityId: string) => Promise<Profile[]>;
74
85
  getOwnerCredentials: (userId: string, entityId?: string, type?: string) => Promise<AuthCredentials | undefined>;
75
86
  }
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owlmeans/server-auth-identity",
3
- "version": "0.1.18-rc.12",
3
+ "version": "0.1.18-rc.13",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -22,14 +22,14 @@
22
22
  }
23
23
  },
24
24
  "dependencies": {
25
- "@owlmeans/auth": "^0.1.18-rc.8",
26
- "@owlmeans/auth-common": "^0.1.18-rc.12",
27
- "@owlmeans/basic-ids": "^0.1.18-rc.8",
28
- "@owlmeans/context": "^0.1.18-rc.7",
29
- "@owlmeans/mongo-resource": "^0.1.18-rc.11",
30
- "@owlmeans/oidc": "^0.1.18-rc.12",
31
- "@owlmeans/resource": "^0.1.18-rc.8",
32
- "@owlmeans/server-context": "^0.1.18-rc.11"
25
+ "@owlmeans/auth": "^0.1.18-rc.9",
26
+ "@owlmeans/auth-common": "^0.1.18-rc.13",
27
+ "@owlmeans/basic-ids": "^0.1.18-rc.9",
28
+ "@owlmeans/context": "^0.1.18-rc.8",
29
+ "@owlmeans/mongo-resource": "^0.1.18-rc.12",
30
+ "@owlmeans/oidc": "^0.1.18-rc.13",
31
+ "@owlmeans/resource": "^0.1.18-rc.9",
32
+ "@owlmeans/server-context": "^0.1.18-rc.12"
33
33
  },
34
34
  "peerDependencies": {
35
35
  "mongodb": "*"
package/src/resource.ts CHANGED
@@ -32,6 +32,16 @@ export const makeIdentityAccountResource = (dbAlias?: string): IdentityAccountRe
32
32
  resource.index('credential', { credential: 1 }, { unique: true })
33
33
  resource.index('entityId', { entityId: 1 })
34
34
  resource.index('secret', { secret: 1 }, { unique: true, sparse: true })
35
+ /**
36
+ * The account's login name — an email, for every provider shipped today.
37
+ *
38
+ * Indexed because `linkProfile` reads it on every registration: a person signing in by a second
39
+ * method must land on the identity they already have, not a new one. NOT unique — the same
40
+ * address legitimately appears twice, once for the platform account and once for an
41
+ * organization's END USER record (`inviteUser`), and those are different people-shaped rows for
42
+ * different audiences. The two are told apart by the profile, never by the account.
43
+ */
44
+ resource.index('name', { name: 1 })
35
45
  return resource
36
46
  }
37
47
 
package/src/service.ts CHANGED
@@ -6,7 +6,7 @@ import type { ProviderProfileDetails } from '@owlmeans/oidc'
6
6
  import { createIdOfLength, IdStyle } from '@owlmeans/basic-ids'
7
7
  import type { Criteria } from '@owlmeans/resource'
8
8
  import type { AccountMeta, IdentityAccountResource, IdentityProfileResource, IdentityCredentialsResource, IdentityLinkingService } from './types.js'
9
- import type { IdentityCredentials, IdentityProfile } from './types.js'
9
+ import type { IdentityAccount, IdentityCredentials, IdentityProfile } from './types.js'
10
10
  import type { OrgEntity, OrgEntityResource } from './types.js'
11
11
  import type { EntityResolverService } from '@owlmeans/auth-common'
12
12
  import { ENTITY_RESOLVER } from '@owlmeans/auth-common'
@@ -40,6 +40,44 @@ export const makeIdentityLinkingService = (): IdentityLinkingService => {
40
40
  return entity?.slug
41
41
  }
42
42
 
43
+ /**
44
+ * The platform identity this person already has, if any.
45
+ *
46
+ * One human, one email, one profile — whichever way they sign in. Every provider that reaches
47
+ * `linkProfile` establishes the email first (Google asserts a verified address, the OTP flow
48
+ * proves possession of it, the supervisor key is a full-trust developer credential), so the
49
+ * name is the identity and a second method is a second CREDENTIAL on it, never a second person.
50
+ * Without this the platform minted a whole new organization per method: the same address signed
51
+ * in by Google and by key ended up in two entities, and each could see only the projects the
52
+ * other had not created.
53
+ *
54
+ * Two rows can carry one address, and only one of them is a platform login. `inviteUser`
55
+ * (`@owlmeans/iam-integrated`) writes an END USER record for the same person — the identity the
56
+ * GENERATED application authenticates, deliberately kept apart from the platform credential.
57
+ * Those rows carry no login service, which is what tells the two apart here: this service is
58
+ * the only writer of `credential = "service:{type}:{service}"`, so a profile that has one is a
59
+ * platform login and a profile that has none is somebody's end user.
60
+ */
61
+ const findPlatformIdentity = async (username: string): Promise<IdentityProfile | null> => {
62
+ const name = username.trim()
63
+ if (name === '') return null
64
+
65
+ const ctx = service.ctx as Context
66
+ const accountResource = ctx.resource<IdentityAccountResource>(AUTH_IDENTITY_ACCOUNT)
67
+ const profileResource = ctx.resource<IdentityProfileResource>(AUTH_IDENTITY_PROFILE)
68
+
69
+ const { items: accounts } = await accountResource.list({ name } as Criteria<IdentityAccount>)
70
+ for (const account of accounts) {
71
+ const { items: profiles } = await profileResource.list({ userId: account.id } as Criteria<IdentityProfile>)
72
+ const platform = profiles.find(profile =>
73
+ profile.credential?.startsWith(`${LOGIN_SERVICE_PREFIX}${EXTERNAL_KEY_DELIMITER}`) === true
74
+ )
75
+ if (platform != null) return platform
76
+ }
77
+
78
+ return null
79
+ }
80
+
43
81
  const service: IdentityLinkingService = appendContextual<IdentityLinkingService>(AUTH_IDENTITY_LINKING, {
44
82
  getLinkedProfile: async (details: ProviderProfileDetails): Promise<AuthPayload | null> => {
45
83
  const ctx = service.ctx as Context
@@ -74,6 +112,29 @@ export const makeIdentityLinkingService = (): IdentityLinkingService => {
74
112
  const entityResource = ctx.resource<OrgEntityResource>(AUTH_IDENTITY_ORG_ENTITY)
75
113
  const resolver = ctx.service<EntityResolverService>(ENTITY_RESOLVER)
76
114
 
115
+ // A person the platform already knows is LINKED, not registered again. Everything below
116
+ // this point creates an organization, an account and a profile, and doing that for a second
117
+ // login method is what put one email in two entities.
118
+ const known = meta.force === true ? null : await findPlatformIdentity(meta.username)
119
+ if (known != null) {
120
+ await credsResource.create({
121
+ challenge: '',
122
+ type: details.type,
123
+ userId: externalKey(details),
124
+ profileId: known.profileId,
125
+ credential: loginService(details),
126
+ })
127
+
128
+ return {
129
+ type: details.type,
130
+ role: known.role,
131
+ userId: known.userId ?? known.profileId,
132
+ profileId: known.profileId,
133
+ entitySlug: await slugOf(known.entityId),
134
+ scopes: known.scopes,
135
+ }
136
+ }
137
+
77
138
  // Every account gets its own organization. The entity record is created FIRST because it
78
139
  // owns the two values that outlive everything else here: the id the account and its profiles
79
140
  // are keyed by, and the frozen `iamKey` that realms and object names are minted from.
@@ -170,6 +231,18 @@ export const makeIdentityLinkingService = (): IdentityLinkingService => {
170
231
  return result
171
232
  },
172
233
 
234
+ unlinkCredentials: async (details: ProviderProfileDetails): Promise<void> => {
235
+ const ctx = service.ctx as Context
236
+ const credsResource = ctx.resource<IdentityCredentialsResource>(AUTH_IDENTITY_CREDENTIALS)
237
+ const cred = await credsResource.load({
238
+ type: details.type,
239
+ userId: externalKey(details),
240
+ credential: loginService(details),
241
+ })
242
+ if (cred?.id == null) return
243
+ await credsResource.delete(cred.id)
244
+ },
245
+
173
246
  getOwnerProfiles: async (entityId: string): Promise<Profile[]> => {
174
247
  const ctx = service.ctx as Context
175
248
  const profileResource = ctx.resource<IdentityProfileResource>(AUTH_IDENTITY_PROFILE)
package/src/types.ts CHANGED
@@ -77,6 +77,17 @@ export interface IdentityLinkingService extends InitializedService {
77
77
  getLinkedProfile: (details: ProviderProfileDetails) => Promise<AuthPayload | null>
78
78
  linkProfile: (details: ProviderProfileDetails, meta: AccountMeta) => Promise<AuthPayload>
79
79
  linkCredentials: (details: ProviderProfileDetails) => Promise<AuthPayload>
80
+ /**
81
+ * Forget which profile an external login maps to, so the next `linkProfile` establishes it anew.
82
+ *
83
+ * The stored mapping is keyed `{type, userId, credential}` under a unique index, so a caller
84
+ * that has decided the existing mapping is wrong cannot simply write over it — it has to be
85
+ * retired first. That decision belongs to whoever owns the login path (a decorator serving a
86
+ * different population than the platform's own customers, say); the key format belongs here.
87
+ *
88
+ * Idempotent: a login that maps to nothing is already in the state this promises.
89
+ */
90
+ unlinkCredentials: (details: ProviderProfileDetails) => Promise<void>
80
91
  getOwnerProfiles: (entityId: string) => Promise<Profile[]>
81
92
  getOwnerCredentials: (userId: string, entityId?: string, type?: string) => Promise<AuthCredentials | undefined>
82
93
  }
@@ -0,0 +1,202 @@
1
+ import { describe, expect, test } from 'bun:test'
2
+ import { AuthRole } from '@owlmeans/auth'
3
+ import { AuthenticationType } from '@owlmeans/auth'
4
+ import { makeIdentityLinkingService } from '../src/service.js'
5
+ import {
6
+ AUTH_IDENTITY_ACCOUNT, AUTH_IDENTITY_CREDENTIALS, AUTH_IDENTITY_ORG_ENTITY,
7
+ AUTH_IDENTITY_PROFILE,
8
+ } from '../src/consts.js'
9
+ import { ENTITY_RESOLVER } from '@owlmeans/auth-common'
10
+
11
+ /**
12
+ * One human, one email, one platform profile — whichever way they sign in.
13
+ *
14
+ * `linkProfile` used to create an organization, an account and a profile on every registration,
15
+ * and registration is per METHOD. The same address signing in by Google and then by key ended up
16
+ * in two entities that could not see each other's projects: `requireEntity` matched neither, and
17
+ * ownership (`project.createdBy`) named a profile the other identity did not have.
18
+ *
19
+ * The rows are stubbed rather than mocked — the service's whole behaviour here is which record it
20
+ * reads before it writes, so a stub that records writes is the subject, not a stand-in for it.
21
+ */
22
+ const stubResource = <T extends { id?: string }>(alias: string, seed: T[] = []) => {
23
+ const items: T[] = [...seed]
24
+ let next = seed.length + 1
25
+
26
+ const matches = (record: any, where: any): boolean =>
27
+ where == null || Object.entries(where).every(([key, value]) => record[key] === value)
28
+
29
+ return {
30
+ alias,
31
+ items,
32
+ list: async (where?: unknown) => ({ items: items.filter(item => matches(item, where)) }),
33
+ load: async (where?: unknown) => items.find(item => matches(item, where)) ?? null,
34
+ get: async (where?: unknown) => {
35
+ const found = items.find(item => matches(item, where))
36
+ if (found == null) throw new Error(`${alias}: not found`)
37
+ return found
38
+ },
39
+ create: async (record: T) => {
40
+ const stored = { ...record, id: record.id ?? `${alias}-${next++}` } as T
41
+ items.push(stored)
42
+ return stored
43
+ },
44
+ delete: async (id: string) => {
45
+ const at = items.findIndex(item => item.id === id)
46
+ if (at < 0) return null
47
+ return items.splice(at, 1)[0]
48
+ },
49
+ registerContext: () => undefined,
50
+ }
51
+ }
52
+
53
+ const makeCtx = (seed: {
54
+ accounts?: any[], profiles?: any[], credentials?: any[]
55
+ } = {}) => {
56
+ const resources: Record<string, any> = {
57
+ [AUTH_IDENTITY_ACCOUNT]: stubResource('account', seed.accounts),
58
+ [AUTH_IDENTITY_PROFILE]: stubResource('profile', seed.profiles),
59
+ [AUTH_IDENTITY_CREDENTIALS]: stubResource('credential', seed.credentials),
60
+ [AUTH_IDENTITY_ORG_ENTITY]: stubResource('entity'),
61
+ }
62
+ let minted = 0
63
+
64
+ return {
65
+ resources,
66
+ resource: (alias: string) => resources[alias],
67
+ service: (alias: string) => {
68
+ if (alias !== ENTITY_RESOLVER) throw new Error(`unexpected service ${alias}`)
69
+ return {
70
+ mintSlug: async () => `slug-${++minted}`,
71
+ byId: async (id: string) => ({ id, slug: `slug-of-${id}` }),
72
+ }
73
+ },
74
+ }
75
+ }
76
+
77
+ const details = (type: string, sub: string) => ({
78
+ type, service: type, clientId: type, userId: sub, username: 'person@example.org',
79
+ })
80
+
81
+ const linkingFor = (ctx: unknown) => {
82
+ const service = makeIdentityLinkingService()
83
+ service.registerContext(ctx as never)
84
+ return service
85
+ }
86
+
87
+ describe('linkProfile', () => {
88
+ test('a first sign-in registers an organization, an account and a profile', async () => {
89
+ const ctx = makeCtx()
90
+ const payload = await linkingFor(ctx).linkProfile(
91
+ details('google-oauth', 'google-sub') as never, { username: 'person@example.org' }
92
+ )
93
+
94
+ expect(ctx.resources[AUTH_IDENTITY_ORG_ENTITY].items).toHaveLength(1)
95
+ expect(ctx.resources[AUTH_IDENTITY_ACCOUNT].items).toHaveLength(1)
96
+ expect(ctx.resources[AUTH_IDENTITY_PROFILE].items).toHaveLength(1)
97
+ expect(payload.profileId).toStartWith('google-oauth:')
98
+ })
99
+
100
+ test('a SECOND method on the same email adds a credential, not an identity', async () => {
101
+ // The whole defect: this used to mint a second organization, and the person's projects
102
+ // became invisible to their other login.
103
+ const ctx = makeCtx()
104
+ const linking = linkingFor(ctx)
105
+
106
+ const first = await linking.linkProfile(
107
+ details('google-oauth', 'google-sub') as never, { username: 'person@example.org' }
108
+ )
109
+ const second = await linking.linkProfile(
110
+ details(AuthenticationType.Supervisor, 'person@example.org') as never,
111
+ { username: 'person@example.org' }
112
+ )
113
+
114
+ expect(ctx.resources[AUTH_IDENTITY_ORG_ENTITY].items).toHaveLength(1)
115
+ expect(ctx.resources[AUTH_IDENTITY_ACCOUNT].items).toHaveLength(1)
116
+ expect(ctx.resources[AUTH_IDENTITY_PROFILE].items).toHaveLength(1)
117
+ // Two credentials, one per method — which is the point.
118
+ expect(ctx.resources[AUTH_IDENTITY_CREDENTIALS].items).toHaveLength(2)
119
+ expect(ctx.resources[AUTH_IDENTITY_CREDENTIALS].items.map((c: any) => c.type).sort())
120
+ .toEqual(['google-oauth', AuthenticationType.Supervisor].sort())
121
+
122
+ // Same identity, same organization — so the same projects.
123
+ expect(second.profileId).toBe(first.profileId)
124
+ expect(second.entitySlug).toBe(first.entitySlug)
125
+ })
126
+
127
+ test('a different email is a different person', async () => {
128
+ const ctx = makeCtx()
129
+ const linking = linkingFor(ctx)
130
+
131
+ await linking.linkProfile(details('google-oauth', 'a') as never, { username: 'a@example.org' })
132
+ await linking.linkProfile(details('google-oauth', 'b') as never, { username: 'b@example.org' })
133
+
134
+ expect(ctx.resources[AUTH_IDENTITY_ORG_ENTITY].items).toHaveLength(2)
135
+ expect(ctx.resources[AUTH_IDENTITY_PROFILE].items).toHaveLength(2)
136
+ })
137
+
138
+ test("an organization's END USER row is never mistaken for a platform login", async () => {
139
+ // `inviteUser` writes a row for the same person carrying the same address — the identity the
140
+ // GENERATED application authenticates, deliberately separate from the platform credential. It
141
+ // has no login service on it, and that is what tells the two apart.
142
+ const ctx = makeCtx({
143
+ accounts: [{ id: 'acc-end-user', credential: 'x', name: 'person@example.org', entityId: 'ent-1' }],
144
+ profiles: [{
145
+ id: 'prof-end-user', profileId: 'email-otp:acc-end-user', userId: 'acc-end-user',
146
+ role: AuthRole.User, name: 'person@example.org', email: 'person@example.org',
147
+ entityId: 'ent-1', scopes: ['*'],
148
+ }],
149
+ })
150
+
151
+ const payload = await linkingFor(ctx).linkProfile(
152
+ details('google-oauth', 'google-sub') as never, { username: 'person@example.org' }
153
+ )
154
+
155
+ // A new platform identity, not a hijack of the end-user row.
156
+ expect(payload.profileId).toStartWith('google-oauth:')
157
+ expect(ctx.resources[AUTH_IDENTITY_PROFILE].items).toHaveLength(2)
158
+ })
159
+
160
+ test('force still registers a fresh identity', async () => {
161
+ const ctx = makeCtx()
162
+ const linking = linkingFor(ctx)
163
+
164
+ await linking.linkProfile(details('google-oauth', 'a') as never, { username: 'person@example.org' })
165
+ await linking.linkProfile(
166
+ details('google-oauth', 'b') as never, { username: 'person@example.org', force: true }
167
+ )
168
+
169
+ expect(ctx.resources[AUTH_IDENTITY_PROFILE].items).toHaveLength(2)
170
+ })
171
+ })
172
+
173
+ /**
174
+ * Forgetting which profile an external login maps to.
175
+ *
176
+ * The mapping is unique on `{type, userId, credential}`, so a caller that has decided the stored
177
+ * one is wrong cannot write over it — it has to be retired first. That is what lets a login path
178
+ * serving a different population than this service's own customers (a target project's end users,
179
+ * say) re-establish an identity of its own for an address that was merged onto a platform one.
180
+ */
181
+ describe('unlinkCredentials', () => {
182
+ test('the stored mapping is removed, and only that one', async () => {
183
+ const ctx = makeCtx()
184
+ const linking = linkingFor(ctx)
185
+ await linking.linkProfile(details('email-otp', 'sub-1'), { username: 'person@example.org' })
186
+ await linking.linkProfile(details('google', 'sub-2'), { username: 'person@example.org' })
187
+ expect(ctx.resources[AUTH_IDENTITY_CREDENTIALS].items.length).toBe(2)
188
+
189
+ await linking.unlinkCredentials(details('email-otp', 'sub-1'))
190
+
191
+ expect(ctx.resources[AUTH_IDENTITY_CREDENTIALS].items.map((c: any) => c.type))
192
+ .toEqual(['google'])
193
+ // And the login is unlinked as far as every reader is concerned.
194
+ expect(await linking.getLinkedProfile(details('email-otp', 'sub-1'))).toBeNull()
195
+ })
196
+
197
+ test('a login that maps to nothing is already in the state this promises', async () => {
198
+ const linking = linkingFor(makeCtx())
199
+
200
+ expect(await linking.unlinkCredentials(details('email-otp', 'nobody'))).toBeUndefined()
201
+ })
202
+ })