@owlmeans/client-auth 0.1.1 → 0.1.3

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.
@@ -0,0 +1,138 @@
1
+ import { describe, test, expect } from 'bun:test'
2
+ import { makeTestContext } from './context.js'
3
+ import { makeFixtureKeyPair } from '@owlmeans/test-auth'
4
+ import { EnvelopeKind, makeEnvelopeModel } from '@owlmeans/basic-envelope'
5
+ import { AuthorizationError, AuthroizationType, AuthRole } from '@owlmeans/auth'
6
+ import type { Auth } from '@owlmeans/auth'
7
+ import { DEFAULT_ALIAS, AUTH_RESOURCE, USER_ID } from '../src/consts.js'
8
+ import type { AuthService } from '@owlmeans/auth-common'
9
+ import type { Resource } from '@owlmeans/resource'
10
+ import type { ClientAuthRecord } from '../src/types.js'
11
+
12
+ /**
13
+ * Produce a valid bearer token (as server-auth would issue)
14
+ */
15
+ const makeBearer = async (auth: Partial<Auth>) => {
16
+ const appKP = makeFixtureKeyPair('client-auth-app-key')
17
+ const fullAuth: Auth = {
18
+ token: 'nonce-123',
19
+ userId: 'user-1',
20
+ scopes: ['*'],
21
+ role: AuthRole.User,
22
+ type: AuthroizationType.Ed25519BasicToken,
23
+ source: 'test-service',
24
+ isUser: true,
25
+ createdAt: new Date(),
26
+ ...auth,
27
+ }
28
+
29
+ const signed = await makeEnvelopeModel<Auth>(AuthroizationType.Ed25519BasicToken)
30
+ .send(fullAuth, null).sign(appKP, EnvelopeKind.Token)
31
+
32
+ return `${AuthroizationType.Ed25519BasicToken.toUpperCase()} ${signed}`
33
+ }
34
+
35
+ const initContext = async () => {
36
+ const context = makeTestContext()
37
+ context.configure()
38
+ await context.init()
39
+ return context
40
+ }
41
+
42
+ describe('@owlmeans/client-auth — update', () => {
43
+ test('stores token and parses Auth from bearer', async () => {
44
+ const context = await initContext()
45
+ const authService = context.service<AuthService>(DEFAULT_ALIAS)
46
+
47
+ const bearer = await makeBearer({ userId: 'user-update-test', scopes: ['read'] })
48
+ await authService.update(bearer)
49
+
50
+ // Token should be stored in memory
51
+ expect(authService.token).toBe(bearer)
52
+
53
+ // Auth should be parsed from the envelope
54
+ expect(authService.auth).not.toBeUndefined()
55
+ expect(authService.auth!.userId).toBe('user-update-test')
56
+ expect(authService.auth!.scopes).toEqual(['read'])
57
+
58
+ // Token should be persisted in the resource store
59
+ const store = context.resource<Resource<ClientAuthRecord>>(AUTH_RESOURCE)
60
+ const record = await store.load(USER_ID)
61
+ expect(record).not.toBeNull()
62
+ expect(record!.token).toBe(bearer)
63
+ })
64
+
65
+ test('clears auth when token is null', async () => {
66
+ const context = await initContext()
67
+ const authService = context.service<AuthService>(DEFAULT_ALIAS)
68
+
69
+ // First set a valid token
70
+ const bearer = await makeBearer({ userId: 'user-to-clear' })
71
+ await authService.update(bearer)
72
+ expect(authService.auth).not.toBeUndefined()
73
+
74
+ // Now clear it
75
+ await authService.update(null as any)
76
+
77
+ expect(authService.token).toBeNull()
78
+ expect(authService.auth).toBeUndefined()
79
+ })
80
+ })
81
+
82
+ describe('@owlmeans/client-auth — authenticated', () => {
83
+ test('returns null when no token exists', async () => {
84
+ const context = await initContext()
85
+ const authService = context.service<AuthService>(DEFAULT_ALIAS)
86
+
87
+ const result = await authService.authenticated()
88
+ expect(result).toBeNull()
89
+ })
90
+
91
+ test('loads token from storage on first call', async () => {
92
+ const context = await initContext()
93
+ const authService = context.service<AuthService>(DEFAULT_ALIAS)
94
+
95
+ // Pre-populate storage directly (simulating previous session)
96
+ const bearer = await makeBearer({ userId: 'persisted-user' })
97
+ const store = context.resource<Resource<ClientAuthRecord>>(AUTH_RESOURCE)
98
+ await store.create({ id: USER_ID, token: bearer })
99
+
100
+ // Now call authenticated — it should load from store
101
+ const result = await authService.authenticated()
102
+ expect(result).toBe(bearer)
103
+ expect(authService.auth).not.toBeUndefined()
104
+ expect(authService.auth!.userId).toBe('persisted-user')
105
+ })
106
+
107
+ test('returns in-memory token without re-reading storage', async () => {
108
+ const context = await initContext()
109
+ const authService = context.service<AuthService>(DEFAULT_ALIAS)
110
+
111
+ const bearer = await makeBearer({ userId: 'memory-user' })
112
+ await authService.update(bearer)
113
+
114
+ const result = await authService.authenticated()
115
+ expect(result).toBe(bearer)
116
+ })
117
+ })
118
+
119
+ describe('@owlmeans/client-auth — user', () => {
120
+ test('throws AuthorizationError when not authenticated', async () => {
121
+ const context = await initContext()
122
+ const authService = context.service<AuthService>(DEFAULT_ALIAS)
123
+
124
+ expect(() => authService.user()).toThrow(AuthorizationError)
125
+ })
126
+
127
+ test('returns Auth after update', async () => {
128
+ const context = await initContext()
129
+ const authService = context.service<AuthService>(DEFAULT_ALIAS)
130
+
131
+ const bearer = await makeBearer({ userId: 'user-for-user-call', role: AuthRole.Guest })
132
+ await authService.update(bearer)
133
+
134
+ const auth = authService.user()
135
+ expect(auth.userId).toBe('user-for-user-call')
136
+ expect(auth.role).toBe(AuthRole.Guest)
137
+ })
138
+ })
@@ -0,0 +1,12 @@
1
+ {
2
+ "extends": [
3
+ "@owlmeans/dep-config/tsconfig.base.json",
4
+ "@owlmeans/dep-config/tsconfig.react.json"
5
+ ],
6
+ "compilerOptions": {
7
+ "types": ["bun"],
8
+ "rootDir": "../",
9
+ "noEmit": true
10
+ },
11
+ "include": ["./**/*", "../src/**/*"]
12
+ }
package/tsconfig.json CHANGED
@@ -1,15 +1,11 @@
1
1
  {
2
2
  "extends": [
3
- "../tsconfig.default.json",
4
- "../tsconfig.react.json",
3
+ "@owlmeans/dep-config/tsconfig.base.json",
4
+ "@owlmeans/dep-config/tsconfig.react.json"
5
5
  ],
6
6
  "compilerOptions": {
7
- "rootDir": "./src/", /* Specify the root folder within your source files. */
8
- "outDir": "./build/", /* Specify an output folder for all emitted files. */
7
+ "rootDir": "./src/",
8
+ "outDir": "./build/"
9
9
  },
10
- "exclude": [
11
- "./dist/**/*",
12
- "./build/**/*",
13
- "./*.ts"
14
- ]
15
- }
10
+ "exclude": ["./dist/**/*", "./build/**/*", "./tests/**/*", "./*.ts"]
11
+ }