@opensaas/stack-auth 0.29.0 → 0.31.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.
@@ -1,4 +1,4 @@
1
1
 
2
- > @opensaas/stack-auth@0.29.0 build /home/runner/work/stack/stack/packages/auth
2
+ > @opensaas/stack-auth@0.31.0 build /home/runner/work/stack/stack/packages/auth
3
3
  > tsc
4
4
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # @opensaas/stack-auth
2
2
 
3
+ ## 0.31.0
4
+
5
+ ### Patch Changes
6
+
7
+ - [#772](https://github.com/OpenSaasAU/stack/pull/772) [`be5772b`](https://github.com/OpenSaasAU/stack/commit/be5772be231d5be6a77d80c4f7eff5adc15da2fa) Thanks [@borisno2](https://github.com/borisno2)! - Add a regression test locking the generated Session/Account user FK shape (no `@@index([userId])`, `onDelete: Cascade`) so future drift from better-auth parity is caught.
8
+
9
+ ## 0.30.0
10
+
11
+ ### Patch Changes
12
+
13
+ - [#741](https://github.com/OpenSaasAU/stack/pull/741) [`afa865f`](https://github.com/OpenSaasAU/stack/commit/afa865f62ed7968b494a87e0621cf71bacd36f39) Thanks [@borisno2](https://github.com/borisno2)! - Update documentation links to the restructured docs site URLs (Diátaxis layout)
14
+
15
+ - [#744](https://github.com/OpenSaasAU/stack/pull/744) [`5e135ef`](https://github.com/OpenSaasAU/stack/commit/5e135ef635dd7cd97ab106f46fbf808250aa079e) Thanks [@borisno2](https://github.com/borisno2)! - Fix stale `withMcpAuth` JSDoc example that imported a nonexistent generated module
16
+
3
17
  ## 0.29.0
4
18
 
5
19
  ## 0.28.0
package/README.md CHANGED
@@ -233,7 +233,7 @@ better-auth user, e.g. `AuthUser`). It does not assume that list is your app's
233
233
  own domain `User` — when the keys differ, your `User` is never extended or
234
234
  overwritten. **Linking your domain `User` to the Auth identity is your app's
235
235
  concern** (declare a `relationship({ ref: 'AuthUser' })` on your `User`). See the
236
- [Authentication guide](https://stack.opensaas.au/docs/guides/authentication) for
236
+ [Authentication guide](https://stack.opensaas.au/docs/how-to/authentication) for
237
237
  the full migrator walkthrough and a Schema-parity (clean-diff) check.
238
238
 
239
239
  ## UI Components
@@ -41,11 +41,10 @@ export declare function createBetterAuthMcpAdapter(auth: BetterAuthInstance): Mc
41
41
  * ```typescript
42
42
  * import { withMcpAuth } from '@opensaas/stack-auth/mcp'
43
43
  * import { auth } from '@/lib/auth'
44
- * import { createMcpServer } from '@/.opensaas/mcp/server'
45
44
  *
46
45
  * const handler = withMcpAuth(auth, async (req, session) => {
47
- * const server = createMcpServer(session)
48
- * return server.handleRequest(req)
46
+ * // session.userId is authenticated — handle the MCP request
47
+ * return new Response(JSON.stringify({ ok: true }))
49
48
  * })
50
49
  *
51
50
  * export { handler as GET, handler as POST, handler as DELETE }
@@ -1 +1 @@
1
- {"version":3,"file":"better-auth.d.ts","sourceRoot":"","sources":["../../src/mcp/better-auth.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAE9E;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAE/B,GAAG,EAAE,GAAG,CAAA;IAER,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;CACnB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,kBAAkB,GAAG,kBAAkB,CAIvF;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,kBAAkB,EACxB,OAAO,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,KAAK,OAAO,CAAC,QAAQ,CAAC,GAAG,QAAQ,GAC3E,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAoBrC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,UAAU,GAAG;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAIrF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,OAAO,CAGhF;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAG7D;AAED;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,kBAAkB,SAChD,OAAO,uBAS3B;AAED;;;;;GAKG;AACH,wBAAgB,mCAAmC,CAAC,KAAK,EAAE,kBAAkB,SACxD,OAAO,uBAS3B"}
1
+ {"version":3,"file":"better-auth.d.ts","sourceRoot":"","sources":["../../src/mcp/better-auth.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAE9E;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAE/B,GAAG,EAAE,GAAG,CAAA;IAER,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;CACnB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,kBAAkB,GAAG,kBAAkB,CAIvF;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,kBAAkB,EACxB,OAAO,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,KAAK,OAAO,CAAC,QAAQ,CAAC,GAAG,QAAQ,GAC3E,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAoBrC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,UAAU,GAAG;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAIrF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,OAAO,CAGhF;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAG7D;AAED;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,kBAAkB,SAChD,OAAO,uBAS3B;AAED;;;;;GAKG;AACH,wBAAgB,mCAAmC,CAAC,KAAK,EAAE,kBAAkB,SACxD,OAAO,uBAS3B"}
@@ -36,11 +36,10 @@ export function createBetterAuthMcpAdapter(auth) {
36
36
  * ```typescript
37
37
  * import { withMcpAuth } from '@opensaas/stack-auth/mcp'
38
38
  * import { auth } from '@/lib/auth'
39
- * import { createMcpServer } from '@/.opensaas/mcp/server'
40
39
  *
41
40
  * const handler = withMcpAuth(auth, async (req, session) => {
42
- * const server = createMcpServer(session)
43
- * return server.handleRequest(req)
41
+ * // session.userId is authenticated — handle the MCP request
42
+ * return new Response(JSON.stringify({ ok: true }))
44
43
  * })
45
44
  *
46
45
  * export { handler as GET, handler as POST, handler as DELETE }
@@ -1 +1 @@
1
- {"version":3,"file":"better-auth.js","sourceRoot":"","sources":["../../src/mcp/better-auth.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAeH;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,0BAA0B,CAAC,IAAwB;IACjE,OAAO,KAAK,EAAE,OAAgB,EAA8B,EAAE;QAC5D,OAAO,MAAM,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,OAAO,EAAE,CAAC,CAAA;IAClD,CAAC,CAAA;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,WAAW,CACzB,IAAwB,EACxB,OAA4E;IAE5E,OAAO,KAAK,EAAE,GAAY,EAAE,EAAE;QAC5B,uCAAuC;QACvC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC;YAC3C,OAAO,EAAE,GAAG,CAAC,OAAO;SACrB,CAAC,CAAA;QAEF,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,4DAA4D;YAC5D,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE;gBACxB,MAAM,EAAE,GAAG;gBACX,OAAO,EAAE;oBACP,kBAAkB,EAAE,2CAA2C;iBAChE;aACF,CAAC,CAAA;QACJ,CAAC;QAED,0CAA0C;QAC1C,OAAO,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;IAC9B,CAAC,CAAA;AACH,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,0BAA0B,CAAC,UAAsB;IAC/D,OAAO;QACL,MAAM,EAAE,UAAU,CAAC,MAAM;KAC1B,CAAA;AACH,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,SAAS,CAAC,OAAmB,EAAE,cAAwB;IACrE,IAAI,CAAC,OAAO,CAAC,MAAM;QAAE,OAAO,KAAK,CAAA;IACjC,OAAO,cAAc,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,MAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAA;AACzE,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAmB;IAClD,IAAI,CAAC,OAAO,CAAC,SAAS;QAAE,OAAO,KAAK,CAAA;IACpC,OAAO,IAAI,IAAI,EAAE,GAAG,OAAO,CAAC,SAAS,CAAA;AACvC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,2BAA2B,CAAC,KAAyB;IACnE,OAAO,KAAK,EAAE,GAAY,EAAE,EAAE;QAC5B,6CAA6C;QAC7C,MAAM,QAAQ,GAAG,kDAAkD,CAAA;QACnE,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,GAAG,CAAC,CAAA;QAE1C,OAAO,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE;YAC/B,OAAO,EAAE,GAAG,CAAC,OAAO;SACrB,CAAC,CAAA;IACJ,CAAC,CAAA;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mCAAmC,CAAC,KAAyB;IAC3E,OAAO,KAAK,EAAE,GAAY,EAAE,EAAE;QAC5B,6CAA6C;QAC7C,MAAM,QAAQ,GAAG,gDAAgD,CAAA;QACjE,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,GAAG,CAAC,CAAA;QAE1C,OAAO,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE;YAC/B,OAAO,EAAE,GAAG,CAAC,OAAO;SACrB,CAAC,CAAA;IACJ,CAAC,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"better-auth.js","sourceRoot":"","sources":["../../src/mcp/better-auth.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAeH;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,0BAA0B,CAAC,IAAwB;IACjE,OAAO,KAAK,EAAE,OAAgB,EAA8B,EAAE;QAC5D,OAAO,MAAM,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,OAAO,EAAE,CAAC,CAAA;IAClD,CAAC,CAAA;AACH,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,WAAW,CACzB,IAAwB,EACxB,OAA4E;IAE5E,OAAO,KAAK,EAAE,GAAY,EAAE,EAAE;QAC5B,uCAAuC;QACvC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC;YAC3C,OAAO,EAAE,GAAG,CAAC,OAAO;SACrB,CAAC,CAAA;QAEF,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,4DAA4D;YAC5D,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE;gBACxB,MAAM,EAAE,GAAG;gBACX,OAAO,EAAE;oBACP,kBAAkB,EAAE,2CAA2C;iBAChE;aACF,CAAC,CAAA;QACJ,CAAC;QAED,0CAA0C;QAC1C,OAAO,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;IAC9B,CAAC,CAAA;AACH,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,0BAA0B,CAAC,UAAsB;IAC/D,OAAO;QACL,MAAM,EAAE,UAAU,CAAC,MAAM;KAC1B,CAAA;AACH,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,SAAS,CAAC,OAAmB,EAAE,cAAwB;IACrE,IAAI,CAAC,OAAO,CAAC,MAAM;QAAE,OAAO,KAAK,CAAA;IACjC,OAAO,cAAc,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,MAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAA;AACzE,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAmB;IAClD,IAAI,CAAC,OAAO,CAAC,SAAS;QAAE,OAAO,KAAK,CAAA;IACpC,OAAO,IAAI,IAAI,EAAE,GAAG,OAAO,CAAC,SAAS,CAAA;AACvC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,2BAA2B,CAAC,KAAyB;IACnE,OAAO,KAAK,EAAE,GAAY,EAAE,EAAE;QAC5B,6CAA6C;QAC7C,MAAM,QAAQ,GAAG,kDAAkD,CAAA;QACnE,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,GAAG,CAAC,CAAA;QAE1C,OAAO,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE;YAC/B,OAAO,EAAE,GAAG,CAAC,OAAO;SACrB,CAAC,CAAA;IACJ,CAAC,CAAA;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mCAAmC,CAAC,KAAyB;IAC3E,OAAO,KAAK,EAAE,GAAY,EAAE,EAAE;QAC5B,6CAA6C;QAC7C,MAAM,QAAQ,GAAG,gDAAgD,CAAA;QACjE,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,GAAG,CAAC,CAAA;QAE1C,OAAO,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE;YAC/B,OAAO,EAAE,GAAG,CAAC,OAAO;SACrB,CAAC,CAAA;IACJ,CAAC,CAAA;AACH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opensaas/stack-auth",
3
- "version": "0.29.0",
3
+ "version": "0.31.0",
4
4
  "description": "Better-auth integration for OpenSaas Stack",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -65,7 +65,8 @@
65
65
  "react": "^19.2.4",
66
66
  "typescript": "npm:@typescript/typescript6@^6.0.2",
67
67
  "vitest": "^4.1.10",
68
- "@opensaas/stack-core": "0.29.0"
68
+ "@opensaas/stack-cli": "0.31.0",
69
+ "@opensaas/stack-core": "0.31.0"
69
70
  },
70
71
  "scripts": {
71
72
  "build": "tsc",
@@ -51,11 +51,10 @@ export function createBetterAuthMcpAdapter(auth: BetterAuthInstance): McpSession
51
51
  * ```typescript
52
52
  * import { withMcpAuth } from '@opensaas/stack-auth/mcp'
53
53
  * import { auth } from '@/lib/auth'
54
- * import { createMcpServer } from '@/.opensaas/mcp/server'
55
54
  *
56
55
  * const handler = withMcpAuth(auth, async (req, session) => {
57
- * const server = createMcpServer(session)
58
- * return server.handleRequest(req)
56
+ * // session.userId is authenticated — handle the MCP request
57
+ * return new Response(JSON.stringify({ ok: true }))
59
58
  * })
60
59
  *
61
60
  * export { handler as GET, handler as POST, handler as DELETE }
@@ -0,0 +1,81 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { config, list } from '@opensaas/stack-core'
3
+ import { text, relationship } from '@opensaas/stack-core/fields'
4
+ import type { OpenSaasConfig } from '@opensaas/stack-core'
5
+ import type { Plugin } from '@opensaas/stack-core/extend'
6
+ import { generatePrismaSchema } from '@opensaas/stack-cli/generator/prisma'
7
+ import { authPlugin } from '../src/config/plugin.js'
8
+
9
+ /**
10
+ * Resolve a config through plugin `init` (via `config()`) and each plugin's
11
+ * `beforeGenerate` hook — the same sequence the CLI generate pipeline runs —
12
+ * then hand the result to the Prisma generator. Unlike the config-level
13
+ * assertions in `derive-auth-lists.test.ts` and `plugin-schema-placement.test.ts`,
14
+ * this exercises the *assembled generated schema text* so the auth FK shape is
15
+ * locked end-to-end (issue #753).
16
+ */
17
+ async function generateSchema(userConfig: OpenSaasConfig): Promise<string> {
18
+ let current = await config(userConfig)
19
+ const plugins: Plugin[] = current.plugins ?? []
20
+ for (const plugin of plugins) {
21
+ if (plugin.beforeGenerate) {
22
+ current = await plugin.beforeGenerate(current)
23
+ }
24
+ }
25
+ return generatePrismaSchema(current)
26
+ }
27
+
28
+ /** Slice out a single `model X { ... }` block from generated schema text. */
29
+ function modelBlock(schema: string, modelName: string): string {
30
+ const start = schema.indexOf(`model ${modelName} {`)
31
+ if (start === -1) throw new Error(`model ${modelName} not found in generated schema`)
32
+ const end = schema.indexOf('\n}', start)
33
+ return schema.slice(start, end === -1 ? undefined : end + 2)
34
+ }
35
+
36
+ describe('generated auth schema — Session/Account user FK mirrors better-auth (issue #679/#753)', () => {
37
+ it('emits onDelete: Cascade and omits @@index on Session.user and Account.user', async () => {
38
+ const schema = await generateSchema({
39
+ db: { provider: 'sqlite' },
40
+ plugins: [authPlugin({ emailAndPassword: { enabled: true } })],
41
+ lists: {},
42
+ })
43
+
44
+ for (const model of ['Session', 'Account']) {
45
+ const block = modelBlock(schema, model)
46
+
47
+ // (a) The user relation carries the cascade referential action. Match on
48
+ // the whitespace-stable attribute substring rather than the padded line,
49
+ // since raw generator output and prisma-formatted output differ only in
50
+ // column alignment.
51
+ expect(block).toContain('@relation(onDelete: Cascade, fields: [userId], references: [id])')
52
+
53
+ // (b) No separate FK index is emitted — parity with better-auth, which
54
+ // ships no @@index([userId]). This is deliberate (ADR-0007), not a
55
+ // regression; catching a re-introduced index here flags drift.
56
+ expect(block).not.toContain('@@index([userId])')
57
+ expect(block).not.toContain('@@index([user])')
58
+ }
59
+ })
60
+
61
+ it('still emits the default @@index for a non-auth relationship FK', async () => {
62
+ // Contrast case: the generic FK-index path is untouched. Only the auth
63
+ // user relations opt out (via isIndexed: false); an ordinary app
64
+ // relationship keeps the default Keystone-parity @@index.
65
+ const schema = await generateSchema({
66
+ db: { provider: 'sqlite' },
67
+ plugins: [authPlugin({ emailAndPassword: { enabled: true } })],
68
+ lists: {
69
+ Widget: list({
70
+ fields: {
71
+ title: text({ validation: { isRequired: true } }),
72
+ owner: relationship({ ref: 'User' }),
73
+ },
74
+ }),
75
+ },
76
+ })
77
+
78
+ const widget = modelBlock(schema, 'Widget')
79
+ expect(widget).toContain('@@index([ownerId])')
80
+ })
81
+ })