@travetto/auth-session 6.0.0-rc.0 → 6.0.0-rc.2

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
@@ -13,11 +13,11 @@ npm install @travetto/auth-session
13
13
  yarn add @travetto/auth-session
14
14
  ```
15
15
 
16
- This is a module that adds session support to the [Authentication](https://github.com/travetto/travetto/tree/main/module/auth#readme "Authentication scaffolding for the Travetto framework") framework, via [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations.") storage. The concept here, is that the [Authentication](https://github.com/travetto/travetto/tree/main/module/auth#readme "Authentication scaffolding for the Travetto framework") module provides the solid foundation for ensuring authentication to the system, and transitively to the session data. The [Principal](https://github.com/travetto/travetto/tree/main/module/auth/src/types/principal.ts#L8) provides a session identifier, which refers to a unique authentication session. Each login will produce a novel session id. This id provides the contract between [Authentication](https://github.com/travetto/travetto/tree/main/module/auth#readme "Authentication scaffolding for the Travetto framework") and[Auth Session](https://github.com/travetto/travetto/tree/main/module/auth-session#readme "Session provider for the travetto auth module.").
16
+ This is a module that adds session support to the [Authentication](https://github.com/travetto/travetto/tree/main/module/auth#readme "Authentication scaffolding for the Travetto framework") framework, via [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations.") storage. The concept here, is that the [Authentication](https://github.com/travetto/travetto/tree/main/module/auth#readme "Authentication scaffolding for the Travetto framework") module provides the solid foundation for ensuring authentication to the system, and transitively to the session data. The [Principal](https://github.com/travetto/travetto/tree/main/module/auth/src/types/principal.ts#L7) provides a session identifier, which refers to a unique authentication session. Each login will produce a novel session id. This id provides the contract between [Authentication](https://github.com/travetto/travetto/tree/main/module/auth#readme "Authentication scaffolding for the Travetto framework") and[Auth Session](https://github.com/travetto/travetto/tree/main/module/auth-session#readme "Session provider for the travetto auth module.").
17
17
 
18
- This session identifier, is then used when retrieving data from [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations.") storage. This storage mechanism is not tied to a request/response model, but the [Rest Auth Session](https://github.com/travetto/travetto/tree/main/module/auth-rest-session#readme "Rest authentication session integration support for the Travetto framework") does provide a natural integration with the [RESTful API](https://github.com/travetto/travetto/tree/main/module/rest#readme "Declarative api for RESTful APIs with support for the dependency injection module.") module.
18
+ This session identifier, is then used when retrieving data from [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations.") storage. This storage mechanism is not tied to a request/response model, but the [Web Auth Session](https://github.com/travetto/travetto/tree/main/module/auth-web-session#readme "Web authentication session integration support for the Travetto framework") does provide a natural integration with the [Web API](https://github.com/travetto/travetto/tree/main/module/web#readme "Declarative api for Web Applications with support for the dependency injection.") module.
19
19
 
20
- Within the framework the sessions are stored against any [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations.") implementation that provides [ModelExpirySupport](https://github.com/travetto/travetto/tree/main/module/model/src/service/expiry.ts), as the data needs to be able to be expired appropriately. The list of supported model providers are:
20
+ Within the framework the sessions are stored against any [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations.") implementation that provides [ModelExpirySupport](https://github.com/travetto/travetto/tree/main/module/model/src/types/expiry.ts#L10), as the data needs to be able to be expired appropriately. The list of supported model providers are:
21
21
  * [Redis Model Support](https://github.com/travetto/travetto/tree/main/module/model-redis#readme "Redis backing for the travetto model module.")
22
22
  * [MongoDB Model Support](https://github.com/travetto/travetto/tree/main/module/model-mongo#readme "Mongo backing for the travetto model module.")
23
23
  * [S3 Model Support](https://github.com/travetto/travetto/tree/main/module/model-s3#readme "S3 backing for the travetto model module.")
@@ -25,31 +25,32 @@ Within the framework the sessions are stored against any [Data Modeling Support]
25
25
  * [Elasticsearch Model Source](https://github.com/travetto/travetto/tree/main/module/model-elasticsearch#readme "Elasticsearch backing for the travetto model module, with real-time modeling support for Elasticsearch mappings.")
26
26
  * [File Model Support](https://github.com/travetto/travetto/tree/main/module/model-file#readme "File system backing for the travetto model module.")
27
27
  * [Memory Model Support](https://github.com/travetto/travetto/tree/main/module/model-memory#readme "Memory backing for the travetto model module.")
28
- While the expiry is not necessarily a hard requirement, the implementation without it can be quite messy. To that end, the ability to add [ModelExpirySupport](https://github.com/travetto/travetto/tree/main/module/model/src/service/expiry.ts) to the model provider would be the natural extension point if more expiry support is needed.
28
+ While the expiry is not necessarily a hard requirement, the implementation without it can be quite messy. To that end, the ability to add [ModelExpirySupport](https://github.com/travetto/travetto/tree/main/module/model/src/types/expiry.ts#L10) to the model provider would be the natural extension point if more expiry support is needed.
29
29
 
30
30
  **Code: Sample usage of Session Service**
31
31
  ```typescript
32
- class RestSessionConfig implements ManagedInterceptorConfig { }
32
+ export class AuthSessionInterceptor implements WebInterceptor {
33
33
 
34
- /**
35
- * Loads session, and provides ability to create session as needed, persists when complete.
36
- */
37
- @Injectable()
38
- export class AuthSessionInterceptor implements RestInterceptor {
39
-
40
- dependsOn: Class<RestInterceptor>[] = [AuthContextInterceptor];
41
- runsBefore: Class<RestInterceptor>[] = [];
34
+ category: WebInterceptorCategory = 'application';
35
+ dependsOn = [AuthContextInterceptor];
42
36
 
43
37
  @Inject()
44
38
  service: SessionService;
45
39
 
46
40
  @Inject()
47
- config: RestSessionConfig;
41
+ context: SessionContext;
42
+
43
+ @Inject()
44
+ webAsyncContext: WebAsyncContext;
45
+
46
+ postConstruct(): void {
47
+ this.webAsyncContext.registerSource(toConcrete<Session>(), () => this.context.get(true));
48
+ this.webAsyncContext.registerSource(toConcrete<SessionData>(), () => this.context.get(true).data);
49
+ }
48
50
 
49
- async intercept(ctx: FilterContext, next: FilterNext): Promise<unknown> {
51
+ async filter({ next }: WebChainedContext): Promise<WebResponse> {
50
52
  try {
51
53
  await this.service.load();
52
- Object.defineProperty(ctx.req, 'session', { get: () => this.service.getOrCreate() });
53
54
  return await next();
54
55
  } finally {
55
56
  await this.service.persist();
@@ -58,7 +59,7 @@ export class AuthSessionInterceptor implements RestInterceptor {
58
59
  }
59
60
  ```
60
61
 
61
- The [SessionService](https://github.com/travetto/travetto/tree/main/module/auth-session/src/service.ts#L16) provides the basic integration with the [AuthContext](https://github.com/travetto/travetto/tree/main/module/auth/src/context.ts#L16) to authenticate and isolate session data. The usage is fairly simple, but the import pattern to follow is:
62
+ The [SessionService](https://github.com/travetto/travetto/tree/main/module/auth-session/src/service.ts#L14) provides the basic integration with the [AuthContext](https://github.com/travetto/travetto/tree/main/module/auth/src/context.ts#L14) to authenticate and isolate session data. The usage is fairly simple, but the import pattern to follow is:
62
63
  * load
63
64
  * read/modify
64
65
  * persist
package/__index__.ts CHANGED
@@ -1,3 +1,4 @@
1
- export * from './src/service';
2
- export * from './src/model';
3
- export * from './src/session';
1
+ export * from './src/service.ts';
2
+ export * from './src/context.ts';
3
+ export * from './src/model.ts';
4
+ export * from './src/session.ts';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/auth-session",
3
- "version": "6.0.0-rc.0",
3
+ "version": "6.0.0-rc.2",
4
4
  "description": "Session provider for the travetto auth module.",
5
5
  "keywords": [
6
6
  "auth",
@@ -25,12 +25,12 @@
25
25
  "directory": "module/auth-session"
26
26
  },
27
27
  "dependencies": {
28
- "@travetto/auth": "^6.0.0-rc.0",
29
- "@travetto/config": "^6.0.0-rc.0",
30
- "@travetto/model": "^6.0.0-rc.0"
28
+ "@travetto/auth": "^6.0.0-rc.2",
29
+ "@travetto/config": "^6.0.0-rc.2",
30
+ "@travetto/model": "^6.0.0-rc.2"
31
31
  },
32
32
  "peerDependencies": {
33
- "@travetto/test": "^6.0.0-rc.0"
33
+ "@travetto/test": "^6.0.0-rc.2"
34
34
  },
35
35
  "peerDependenciesMeta": {
36
36
  "@travetto/test": {
package/src/context.ts ADDED
@@ -0,0 +1,62 @@
1
+ import { Injectable, Inject } from '@travetto/di';
2
+ import { AsyncContext, AsyncContextValue } from '@travetto/context';
3
+ import { AuthContext, AuthenticationError } from '@travetto/auth';
4
+
5
+ import { Session } from './session.ts';
6
+
7
+ /**
8
+ * Session context, injectable wherever needed
9
+ */
10
+ @Injectable()
11
+ export class SessionContext {
12
+
13
+ @Inject()
14
+ context: AsyncContext;
15
+
16
+ @Inject()
17
+ authContext: AuthContext;
18
+
19
+ #value = new AsyncContextValue<Session>(this, { failIfUnbound: { write: true } });
20
+
21
+ #create(): Session {
22
+ const principal = this.authContext.principal;
23
+ if (!principal) {
24
+ throw new AuthenticationError('Unable to establish session without first authenticating');
25
+ }
26
+ return new Session({
27
+ id: principal.sessionId,
28
+ expiresAt: principal.expiresAt,
29
+ issuedAt: principal.issuedAt,
30
+ action: 'create',
31
+ data: {},
32
+ });
33
+ }
34
+
35
+ /**
36
+ * Get session if defined
37
+ */
38
+ get(createIfMissing: true): Session;
39
+ get(): Session | undefined;
40
+ get(createIfMissing?: boolean): Session | undefined {
41
+ let val = this.#value.get();
42
+ if (!val && createIfMissing) {
43
+ this.set(val = this.#create());
44
+ }
45
+ return val;
46
+ }
47
+
48
+ /**
49
+ * Set the session state directly
50
+ */
51
+ set(session: Session | undefined): void {
52
+ this.#value.set(session);
53
+ }
54
+
55
+ /**
56
+ * Destroy
57
+ */
58
+ destroy(): void {
59
+ this.get()?.destroy();
60
+ this.set(undefined);
61
+ }
62
+ }
package/src/service.ts CHANGED
@@ -1,22 +1,20 @@
1
1
  import { Injectable, Inject } from '@travetto/di';
2
- import { isStorageSupported } from '@travetto/model/src/internal/service/common';
3
2
  import { Runtime, Util } from '@travetto/runtime';
4
- import { ModelExpirySupport, NotFoundError } from '@travetto/model';
5
- import { AsyncContext, AsyncContextValue } from '@travetto/context';
6
- import { AuthContext, AuthenticationError, AuthService } from '@travetto/auth';
3
+ import { ModelExpirySupport, NotFoundError, ModelStorageUtil } from '@travetto/model';
4
+ import { AuthContext, AuthService } from '@travetto/auth';
7
5
 
8
- import { Session } from './session';
9
- import { SessionEntry, SessionModelSymbol } from './model';
6
+ import { Session } from './session.ts';
7
+ import { SessionEntry, SessionModelSymbol } from './model.ts';
8
+ import { SessionContext } from './context.ts';
10
9
 
11
10
  /**
12
- * Rest service for supporting the session and managing the session state
13
- * during the normal lifecycle of requests.
11
+ * Service for supporting the session and managing the session state
14
12
  */
15
13
  @Injectable()
16
14
  export class SessionService {
17
15
 
18
16
  @Inject()
19
- context: AsyncContext;
17
+ context: SessionContext;
20
18
 
21
19
  @Inject()
22
20
  authContext: AuthContext;
@@ -26,24 +24,15 @@ export class SessionService {
26
24
 
27
25
  #modelService: ModelExpirySupport;
28
26
 
29
- #session = new AsyncContextValue<Session>(this);
30
-
31
27
  constructor(@Inject(SessionModelSymbol) service: ModelExpirySupport) {
32
28
  this.#modelService = service;
33
29
  }
34
30
 
35
- /**
36
- * Disconnect active session
37
- */
38
- clear(): void {
39
- this.#session.set(undefined);
40
- }
41
-
42
31
  /**
43
32
  * Initialize service if none defined
44
33
  */
45
34
  async postConstruct(): Promise<void> {
46
- if (isStorageSupported(this.#modelService) && Runtime.dynamic) {
35
+ if (ModelStorageUtil.isSupported(this.#modelService) && Runtime.dynamic) {
47
36
  await this.#modelService.createModel?.(SessionEntry);
48
37
  }
49
38
  }
@@ -79,7 +68,7 @@ export class SessionService {
79
68
  * Persist session
80
69
  */
81
70
  async persist(): Promise<void> {
82
- const session = this.#session.get();
71
+ const session = this.context.get();
83
72
 
84
73
  // If missing or new and no data
85
74
  if (!session || (session.action === 'create' && session.isEmpty())) {
@@ -110,48 +99,15 @@ export class SessionService {
110
99
  }
111
100
 
112
101
  /**
113
- * Get or recreate session
114
- */
115
- getOrCreate(): Session {
116
- const principal = this.authContext.principal;
117
- if (!principal) {
118
- throw new AuthenticationError('Unable to establish session without first authenticating');
119
- }
120
- const existing = this.#session.get();
121
- const val = (existing?.action === 'destroy' ? undefined : existing) ??
122
- new Session({
123
- id: principal.sessionId,
124
- expiresAt: principal.expiresAt,
125
- issuedAt: principal.issuedAt,
126
- action: 'create',
127
- data: {},
128
- });
129
- this.#session.set(val);
130
- return val;
131
- }
132
-
133
- /**
134
- * Get session if defined
135
- */
136
- get(): Session | undefined {
137
- return this.#session.get();
138
- }
139
-
140
- /**
141
- * Load from request
102
+ * Load from principal
142
103
  */
143
104
  async load(): Promise<Session | undefined> {
144
- if (!this.#session.get()) {
105
+ if (!this.context.get()) {
145
106
  const principal = this.authContext.principal;
146
107
  if (principal?.sessionId) {
147
- this.#session.set(await this.#load(principal.sessionId));
108
+ this.context.set(await this.#load(principal.sessionId));
148
109
  }
149
110
  }
150
- return this.#session.get();
151
- }
152
-
153
- destroy(): void {
154
- this.get()?.destroy();
155
- this.clear();
111
+ return this.context.get();
156
112
  }
157
113
  }
package/src/session.ts CHANGED
@@ -1,14 +1,12 @@
1
1
  import { AnyMap, castKey, castTo } from '@travetto/runtime';
2
2
 
3
3
  /**
4
- * @concrete ./internal/types#SessionDataTarget
5
- * @augments `@travetto/rest:Context`
4
+ * @concrete
6
5
  */
7
6
  export interface SessionData extends AnyMap { }
8
7
 
9
8
  /**
10
9
  * Full session object, with metadata
11
- * @augments `@travetto/rest:Context`
12
10
  */
13
11
  export class Session<T extends SessionData = SessionData> {
14
12
  /**
@@ -2,17 +2,16 @@ import assert from 'node:assert';
2
2
 
3
3
  import { Suite, Test } from '@travetto/test';
4
4
  import { Inject } from '@travetto/di';
5
- import { SessionService } from '@travetto/auth-session';
5
+ import { SessionContext, SessionService } from '@travetto/auth-session';
6
6
  import { AuthContext, AuthenticationError } from '@travetto/auth';
7
7
  import { AsyncContext, WithAsyncContext } from '@travetto/context';
8
8
  import { Util } from '@travetto/runtime';
9
9
 
10
- import { InjectableSuite } from '@travetto/di/support/test/suite';
11
- import { BaseRestSuite } from '@travetto/rest/support/test/base';
10
+ import { InjectableSuite } from '@travetto/di/support/test/suite.ts';
12
11
 
13
12
  @Suite()
14
13
  @InjectableSuite()
15
- export abstract class AuthSessionServerSuite extends BaseRestSuite {
14
+ export abstract class AuthSessionServerSuite {
16
15
 
17
16
  timeScale = 1;
18
17
 
@@ -22,33 +21,34 @@ export abstract class AuthSessionServerSuite extends BaseRestSuite {
22
21
  @Inject()
23
22
  session: SessionService;
24
23
 
24
+ @Inject()
25
+ sessionContext: SessionContext;
26
+
25
27
  @Inject()
26
28
  context: AsyncContext;
27
29
 
28
30
  @WithAsyncContext()
29
31
  @Test()
30
32
  async testSessionEstablishment() {
31
- await this.auth.init();
32
-
33
33
  this.auth.principal = {
34
34
  id: 'orange',
35
35
  details: {},
36
36
  sessionId: Util.uuid()
37
37
  };
38
38
 
39
- assert(this.session.get() === undefined);
39
+ assert(this.sessionContext.get() === undefined);
40
40
  assert(await this.session.load() === undefined);
41
41
 
42
- const sess = this.session.getOrCreate();
43
- assert(sess.id === this.auth.principal.sessionId);
44
- sess.data = { name: 'bob' };
42
+ const session = this.sessionContext.get(true);
43
+ assert(session.id === this.auth.principal.sessionId);
44
+ session.data = { name: 'bob' };
45
45
  await this.session.persist();
46
46
 
47
- this.session.clear(); // Disconnect
47
+ this.sessionContext.set(undefined); // Disconnect
48
48
 
49
49
  assert(await this.session.load() !== undefined);
50
- const sess2 = this.session.getOrCreate();
51
- assert(sess2.data?.name === 'bob');
50
+ const session2 = this.sessionContext.get(true);
51
+ assert(session2.data?.name === 'bob');
52
52
 
53
53
  this.auth.principal = {
54
54
  id: 'orange',
@@ -56,17 +56,17 @@ export abstract class AuthSessionServerSuite extends BaseRestSuite {
56
56
  sessionId: Util.uuid()
57
57
  };
58
58
 
59
- this.session.clear(); // Disconnect
59
+ this.sessionContext.set(undefined); // Disconnect
60
60
 
61
61
  assert(await this.session.load() === undefined);
62
- const sess3 = this.session.getOrCreate();
63
- assert.deepStrictEqual(sess3.data, {});
62
+ const session3 = this.sessionContext.get(true);
63
+ assert.deepStrictEqual(session3.data, {});
64
64
  }
65
65
 
66
66
  @WithAsyncContext()
67
67
  @Test()
68
68
  async testUnauthenticatedSession() {
69
- await assert.throws(() => this.session.getOrCreate(), AuthenticationError);
69
+ await assert.throws(() => this.sessionContext.get(true), AuthenticationError);
70
70
 
71
71
  }
72
72
  }
@@ -1,4 +0,0 @@
1
- /**
2
- * Session data, will basically be a key/value map
3
- */
4
- export class SessionDataTarget { }