@owlmeans/server-auth-identity 0.1.15 → 0.1.16

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
@@ -96,10 +96,9 @@ const { items } = await profiles.list({ entityId, profileId } as any)
96
96
  <!-- owlmeans:agent-guidance:start -->
97
97
  ## Agent guidance
98
98
 
99
- This package ships embedded Claude Code skills and GitHub Copilot instructions under
100
- `agent-meta/`. After installing your `@owlmeans/*` packages, run the OwlMeans
101
- agent-skills installer to place them into your project's native locations
102
- (`.claude/skills/` and `.github/instructions/`):
99
+ This package ships embedded agent skills under `agent-meta/`. After installing your
100
+ `@owlmeans/*` packages, run the OwlMeans agent-skills installer to place them into
101
+ your project's skill store (`.agents/skills/`):
103
102
 
104
103
  ```sh
105
104
  npx @owlmeans/agent-skills
@@ -1,8 +1,8 @@
1
1
  {
2
- "schemaVersion": 1,
2
+ "schemaVersion": 2,
3
3
  "package": "@owlmeans/server-auth-identity",
4
- "version": "0.1.15",
5
- "generatedAt": "2026-08-07T17:06:32.892Z",
4
+ "version": "0.1.16",
5
+ "generatedAt": "2026-08-14T10:14:48.847Z",
6
6
  "canonicalRepo": "https://github.com/owlmeans/common",
7
7
  "entries": [
8
8
  {
@@ -10,14 +10,7 @@
10
10
  "name": "server-auth-identity",
11
11
  "category": "package-specific",
12
12
  "file": "skills/server-auth-identity/SKILL.md",
13
- "canonicalPath": ".claude/skills/server-auth-identity/SKILL.md"
14
- },
15
- {
16
- "kind": "instruction",
17
- "name": "server-auth-identity",
18
- "category": "package-specific",
19
- "file": "instructions/server-auth-identity.instructions.md",
20
- "canonicalPath": ".github/instructions/server-auth-identity.instructions.md"
13
+ "canonicalPath": ".agents/skills/server-auth-identity/SKILL.md"
21
14
  }
22
15
  ]
23
16
  }
@@ -8,7 +8,7 @@ user-invocable: false
8
8
  # @owlmeans/server-auth-identity
9
9
 
10
10
  **Layer:** Server
11
- **Install:** `"@owlmeans/server-auth-identity": "^0.1.15"` in `dependencies`
11
+ **Install:** `"@owlmeans/server-auth-identity": "^0.1.16"` in `dependencies`
12
12
 
13
13
  ## Key Exports
14
14
 
@@ -92,11 +92,18 @@ interface ProviderProfileDetails {
92
92
 
93
93
  ## Key Derivation Conventions
94
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.
95
98
  - **Account credential**: unique Base58 slug (16 chars) — serves as the local `entityId`
96
- - **Profile profileId**: `"{type}:{accountId}"` — stable across provider re-links
97
- - **Credentials userId** (external key): `"{type}:{service}:{providerSub}"` — uniquely identifies the external account
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
98
101
  - **Credentials credential** (login-service key): `"service:{type}:{service}"` — groups credentials by provider
99
102
 
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.
106
+
100
107
  ## Resource Indexes
101
108
 
102
109
  | Resource | Index | Fields | Notes |
@@ -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,EAAE,MAAM,YAAY,CAAA;AAMtK,eAAO,MAAM,2BAA2B,GAAI,UAAU,MAAM,KAAG,uBAQ9D,CAAA;AAED,eAAO,MAAM,2BAA2B,GAAI,UAAU,MAAM,KAAG,uBAS9D,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,EAAE,MAAM,YAAY,CAAA;AAMtK,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"}
package/build/resource.js CHANGED
@@ -9,6 +9,12 @@ export const makeIdentityAccountResource = (dbAlias) => {
9
9
  };
10
10
  export const makeIdentityProfileResource = (dbAlias) => {
11
11
  const resource = makeMongoResource(AUTH_IDENTITY_PROFILE, dbAlias, undefined, undefined, AUTH_IDENTITY_PROFILE_COLLECTION);
12
+ /**
13
+ * The account's mongo id — the ONLY ObjectId reference in the identity trio.
14
+ * `profileId` is a composite key (`"{type}:{accountId}"`), `entityId` is the Base58
15
+ * slug, and `credentials.userId` is an external provider key — none of them converts.
16
+ */
17
+ resource.reference('userId', AUTH_IDENTITY_ACCOUNT);
12
18
  resource.index('userId', { userId: 1 });
13
19
  resource.index('entityId', { entityId: 1 });
14
20
  resource.index('role', { role: 1, entityId: 1 });
@@ -1 +1 @@
1
- {"version":3,"file":"resource.js","sourceRoot":"","sources":["../src/resource.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAE5D,OAAO,EACL,qBAAqB,EAAE,qBAAqB,EAAE,yBAAyB,EACvE,gCAAgC,EAAE,gCAAgC,EAAE,oCAAoC,EACzG,MAAM,aAAa,CAAA;AAEpB,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,OAAgB,EAA2B,EAAE;IACvF,MAAM,QAAQ,GAAG,iBAAiB,CAChC,qBAAqB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,gCAAgC,CACvF,CAAA;IACD,QAAQ,CAAC,KAAK,CAAC,YAAY,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAA;IACjE,QAAQ,CAAC,KAAK,CAAC,UAAU,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAA;IAC3C,QAAQ,CAAC,KAAK,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAA;IACvE,OAAO,QAAQ,CAAA;AACjB,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,OAAgB,EAA2B,EAAE;IACvF,MAAM,QAAQ,GAAG,iBAAiB,CAChC,qBAAqB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,gCAAgC,CACvF,CAAA;IACD,QAAQ,CAAC,KAAK,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAA;IACvC,QAAQ,CAAC,KAAK,CAAC,UAAU,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAA;IAC3C,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAA;IAChD,QAAQ,CAAC,KAAK,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAA;IAC1E,OAAO,QAAQ,CAAA;AACjB,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,+BAA+B,GAAG,CAAC,OAAgB,EAA+B,EAAE;IAC/F,MAAM,QAAQ,GAAG,iBAAiB,CAChC,yBAAyB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,oCAAoC,CAC/F,CAAA;IACD,QAAQ,CAAC,KAAK,CAAC,UAAU,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAA;IACnF,QAAQ,CAAC,KAAK,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAA;IAC7C,OAAO,QAAQ,CAAA;AACjB,CAAC,CAAA"}
1
+ {"version":3,"file":"resource.js","sourceRoot":"","sources":["../src/resource.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAE5D,OAAO,EACL,qBAAqB,EAAE,qBAAqB,EAAE,yBAAyB,EACvE,gCAAgC,EAAE,gCAAgC,EAAE,oCAAoC,EACzG,MAAM,aAAa,CAAA;AAEpB,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,OAAgB,EAA2B,EAAE;IACvF,MAAM,QAAQ,GAAG,iBAAiB,CAChC,qBAAqB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,gCAAgC,CACvF,CAAA;IACD,QAAQ,CAAC,KAAK,CAAC,YAAY,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAA;IACjE,QAAQ,CAAC,KAAK,CAAC,UAAU,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAA;IAC3C,QAAQ,CAAC,KAAK,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAA;IACvE,OAAO,QAAQ,CAAA;AACjB,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,OAAgB,EAA2B,EAAE;IACvF,MAAM,QAAQ,GAAG,iBAAiB,CAChC,qBAAqB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,gCAAgC,CACvF,CAAA;IACD;;;;OAIG;IACH,QAAQ,CAAC,SAAS,CAAC,QAAQ,EAAE,qBAAqB,CAAC,CAAA;IACnD,QAAQ,CAAC,KAAK,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAA;IACvC,QAAQ,CAAC,KAAK,CAAC,UAAU,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAA;IAC3C,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAA;IAChD,QAAQ,CAAC,KAAK,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAA;IAC1E,OAAO,QAAQ,CAAA;AACjB,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,+BAA+B,GAAG,CAAC,OAAgB,EAA+B,EAAE;IAC/F,MAAM,QAAQ,GAAG,iBAAiB,CAChC,yBAAyB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,oCAAoC,CAC/F,CAAA;IACD,QAAQ,CAAC,KAAK,CAAC,UAAU,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAA;IACnF,QAAQ,CAAC,KAAK,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAA;IAC7C,OAAO,QAAQ,CAAA;AACjB,CAAC,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owlmeans/server-auth-identity",
3
- "version": "0.1.15",
3
+ "version": "0.1.16",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -22,13 +22,13 @@
22
22
  }
23
23
  },
24
24
  "dependencies": {
25
- "@owlmeans/auth": "^0.1.15",
26
- "@owlmeans/basic-ids": "^0.1.15",
27
- "@owlmeans/context": "^0.1.15",
28
- "@owlmeans/mongo-resource": "^0.1.15",
29
- "@owlmeans/oidc": "^0.1.15",
30
- "@owlmeans/resource": "^0.1.15",
31
- "@owlmeans/server-context": "^0.1.15"
25
+ "@owlmeans/auth": "^0.1.16",
26
+ "@owlmeans/basic-ids": "^0.1.16",
27
+ "@owlmeans/context": "^0.1.16",
28
+ "@owlmeans/mongo-resource": "^0.1.16",
29
+ "@owlmeans/oidc": "^0.1.16",
30
+ "@owlmeans/resource": "^0.1.16",
31
+ "@owlmeans/server-context": "^0.1.16"
32
32
  },
33
33
  "peerDependencies": {
34
34
  "mongodb": "*"
package/src/resource.ts CHANGED
@@ -19,6 +19,12 @@ export const makeIdentityProfileResource = (dbAlias?: string): IdentityProfileRe
19
19
  const resource = makeMongoResource<IdentityProfile, IdentityProfileResource>(
20
20
  AUTH_IDENTITY_PROFILE, dbAlias, undefined, undefined, AUTH_IDENTITY_PROFILE_COLLECTION
21
21
  )
22
+ /**
23
+ * The account's mongo id — the ONLY ObjectId reference in the identity trio.
24
+ * `profileId` is a composite key (`"{type}:{accountId}"`), `entityId` is the Base58
25
+ * slug, and `credentials.userId` is an external provider key — none of them converts.
26
+ */
27
+ resource.reference('userId', AUTH_IDENTITY_ACCOUNT)
22
28
  resource.index('userId', { userId: 1 })
23
29
  resource.index('entityId', { entityId: 1 })
24
30
  resource.index('role', { role: 1, entityId: 1 })
@@ -1,118 +0,0 @@
1
- ---
2
- description: "How to use @owlmeans/server-auth-identity — Mongo-backed local identity resources (account, profile, credentials) and IdentityLinkingService for provider account linking. Use when importing appendAuthIdentityResources, makeIdentityLinkingService, or identity resource aliases."
3
- applyTo: "**/context.ts, **/services/**/*.ts, **/*.ts, **/*.tsx"
4
- ---
5
- <!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
6
-
7
- # @owlmeans/server-auth-identity
8
-
9
- **Layer:** Server
10
- **Install:** `"@owlmeans/server-auth-identity": "^0.1.15"` in `dependencies`
11
-
12
- ## Key Exports
13
-
14
- | Export | Description |
15
- |--------|-------------|
16
- | `appendAuthIdentityResources(context, dbAlias?)` | Register all three identity resources + linking service on a context |
17
- | `makeIdentityAccountResource(dbAlias?)` | Mongo resource for `IdentityAccount` (one per user) |
18
- | `makeIdentityProfileResource(dbAlias?)` | Mongo resource for `IdentityProfile` (ties user to entity + role) |
19
- | `makeIdentityCredentialsResource(dbAlias?)` | Mongo resource for `IdentityCredentials` (provider link per profile) |
20
- | `makeIdentityLinkingService()` | Service implementing `IdentityLinkingService` — links external providers to local profiles |
21
- | `AUTH_IDENTITY_ACCOUNT` | Resource alias `'auth-identity:account'` |
22
- | `AUTH_IDENTITY_PROFILE` | Resource alias `'auth-identity:profile'` |
23
- | `AUTH_IDENTITY_CREDENTIALS` | Resource alias `'auth-identity:credentials'` |
24
- | `AUTH_IDENTITY_LINKING` | Service alias `'auth-identity:linking'` |
25
-
26
- ## Types
27
-
28
- | Type | Description |
29
- |------|-------------|
30
- | `IdentityAccount` | `Profile & ResourceRecord` — `id`, `credential` (unique entity slug) |
31
- | `IdentityProfile` | `Profile & ResourceRecord` — `id`, `profileId`, `userId?`, `role`, `entityId`, `scopes`, `expiresAt?` |
32
- | `IdentityCredentials` | `AuthCredentials & ResourceRecord` — `profileId`, `type`, `userId` (external key), `credential` (login-service key) |
33
- | `IdentityLinkingService` | `getLinkedProfile`, `linkProfile`, `linkCredentials`, `getOwnerProfiles`, `getOwnerCredentials` |
34
- | `IdentityAccountResource` | `MongoResource<IdentityAccount>` |
35
- | `IdentityProfileResource` | `MongoResource<IdentityProfile>` |
36
- | `IdentityCredentialsResource` | `MongoResource<IdentityCredentials>` |
37
-
38
- ## Usage
39
-
40
- ### Register in context
41
-
42
- ```typescript
43
- import { appendAuthIdentityResources } from '@owlmeans/server-auth-identity'
44
-
45
- // After appendMongo, appendRedis, appendAuthService:
46
- appendAuthIdentityResources(context)
47
- ```
48
-
49
- ### Read a profile (gate / handler)
50
-
51
- ```typescript
52
- import { AUTH_IDENTITY_PROFILE } from '@owlmeans/server-auth-identity'
53
- import type { IdentityProfileResource } from '@owlmeans/server-auth-identity'
54
-
55
- const profileRes = ctx.resource<IdentityProfileResource>(AUTH_IDENTITY_PROFILE)
56
-
57
- // Single-field indexed lookup (read-only):
58
- const profile = await profileRes.load(profileId, 'profileId')
59
-
60
- // Multi-field criteria lookup (read-only):
61
- const { items } = await profileRes.list({ entityId, profileId } as any)
62
- const profile = items[0] ?? null
63
- ```
64
-
65
- ### Link an external provider to a local profile
66
-
67
- ```typescript
68
- import { AUTH_IDENTITY_LINKING } from '@owlmeans/server-auth-identity'
69
- import type { IdentityLinkingService } from '@owlmeans/server-auth-identity'
70
-
71
- const linking = ctx.service<IdentityLinkingService>(AUTH_IDENTITY_LINKING)
72
- const payload = await linking.linkProfile(providerDetails, { username })
73
- // Returns AuthPayload with type, role, userId, profileId, entityId, scopes
74
- ```
75
-
76
- ## Key Derivation Conventions
77
-
78
- - **Account credential**: unique Base58 slug (16 chars) — serves as the local `entityId`
79
- - **Profile profileId**: `"{type}:{accountId}"` — stable across provider re-links
80
- - **Credentials userId** (external key): `"{type}:{service}:{providerSub}"` — uniquely identifies the external account
81
- - **Credentials credential** (login-service key): `"service:{type}:{service}"` — groups credentials by provider
82
-
83
- ## Resource Indexes
84
-
85
- | Resource | Index | Fields | Notes |
86
- |----------|-------|--------|-------|
87
- | Account | `credential` | `{ credential: 1 }` | unique |
88
- | Account | `entityId` | `{ entityId: 1 }` | |
89
- | Account | `secret` | `{ secret: 1 }` | unique, sparse |
90
- | Profile | `userId` | `{ userId: 1 }` | |
91
- | Profile | `entityId` | `{ entityId: 1 }` | |
92
- | Profile | `role` | `{ role: 1, entityId: 1 }` | |
93
- | Profile | `profile` | `{ profileId: 1, entityId: 1 }` | unique |
94
- | Credentials | `provider` | `{ type: 1, userId: 1, credential: 1 }` | unique |
95
- | Credentials | `profileId` | `{ profileId: 1 }` | |
96
-
97
- ## Important Gotchas
98
-
99
- - **`Resource.pick()` is destructive** — it deletes the record. For read-only identity lookups, use `load(id, field)` for single-field indexed queries or `list(criteria)` for multi-field queries. Never use `pick()` in a gate or authorization check.
100
- - **`IdentityLinkingService` is compatible with `AccountLinkingService`** from `@owlmeans/server-oidc-rp` but defined independently to avoid circular dependency. Both return `AuthPayload`.
101
- - First-login profiles are created with `ALL_SCOPES` and `AuthRole.User` — the gate layer is initially permissive per entity.
102
-
103
- ## Product-Viable Usage Notes
104
-
105
- - Register `appendAuthIdentityResources(context)` immediately after `appendAuthService(context)` so guards can authenticate before product gates authorize.
106
- - Product gates should load `AUTH_IDENTITY_PROFILE` read-only, verify `entityId`, reject expired/blocked profiles, then compare gate params against profile scopes.
107
- - Google login goes through OIDC provider config, but the durable authorization source is the local `IdentityProfile` record.
108
- - Account `credential` is the public local entity slug. Keep it separate from Mongo account `id` and provider subject identifiers.
109
-
110
- ## Depends On
111
-
112
- - `@owlmeans/auth` — `AuthPayload`, `AuthRole`, `ALL_SCOPES`
113
- - `@owlmeans/basic-ids` — `createIdOfLength` for slug generation
114
- - `@owlmeans/context` — `appendContextual`
115
- - `@owlmeans/mongo-resource` — `makeMongoResource`
116
- - `@owlmeans/oidc` — `ProviderProfileDetails`
117
- - `@owlmeans/resource` — `ResourceRecord`
118
- - `@owlmeans/server-context` — `ServerConfig`, `ServerContext`