@hitchy/plugin-auth 0.3.7 → 0.3.8

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.
Files changed (118) hide show
  1. package/.editorconfig +9 -0
  2. package/api/model/authorization/rule.js +1 -1
  3. package/api/model/role.js +1 -1
  4. package/api/model/user.js +1 -1
  5. package/api/policy/authentication.js +44 -13
  6. package/api/policy/user.js +1 -1
  7. package/api/service/auth/manager.js +2 -2
  8. package/api/service/authentication/passport.js +3 -3
  9. package/api/service/authentication/strategies.js +4 -4
  10. package/api/service/authorization/node.js +25 -17
  11. package/api/service/authorization/tree.js +10 -8
  12. package/api/service/session.js +150 -0
  13. package/index.js +2 -2
  14. package/package.json +12 -12
  15. package/public/404.html +21 -0
  16. package/public/api/config.html +100 -0
  17. package/public/api/controller/index.html +24 -0
  18. package/public/api/controller/user.html +29 -0
  19. package/public/api/index.html +24 -0
  20. package/public/api/model/authorization-rule.html +24 -0
  21. package/public/api/model/index.html +24 -0
  22. package/public/api/model/role.html +24 -0
  23. package/public/api/model/user-to-role.html +24 -0
  24. package/public/api/model/user.html +24 -0
  25. package/public/api/policy/authentication.html +28 -0
  26. package/public/api/policy/authorization.html +31 -0
  27. package/public/api/policy/index.html +24 -0
  28. package/public/api/policy/user.html +24 -0
  29. package/public/api/routing.html +40 -0
  30. package/public/api/service/auth-manager.html +24 -0
  31. package/public/api/service/authentication-passport.html +24 -0
  32. package/public/api/service/authentication-strategies.html +24 -0
  33. package/public/api/service/authorization-node.html +24 -0
  34. package/public/api/service/authorization-policy-generator.html +42 -0
  35. package/public/api/service/authorization-tree.html +24 -0
  36. package/public/api/service/index.html +24 -0
  37. package/public/assets/api_config.md.BiPnBhyk.js +77 -0
  38. package/public/assets/api_config.md.BiPnBhyk.lean.js +1 -0
  39. package/public/assets/api_controller_index.md.mhiyhr_C.js +1 -0
  40. package/public/assets/api_controller_index.md.mhiyhr_C.lean.js +1 -0
  41. package/public/assets/api_controller_user.md.BiFYPTow.js +6 -0
  42. package/public/assets/api_controller_user.md.BiFYPTow.lean.js +1 -0
  43. package/public/assets/api_index.md.j6eBaebO.js +1 -0
  44. package/public/assets/api_index.md.j6eBaebO.lean.js +1 -0
  45. package/public/assets/api_model_authorization-rule.md.CFNqudsp.js +1 -0
  46. package/public/assets/api_model_authorization-rule.md.CFNqudsp.lean.js +1 -0
  47. package/public/assets/api_model_index.md.Dw3UH73J.js +1 -0
  48. package/public/assets/api_model_index.md.Dw3UH73J.lean.js +1 -0
  49. package/public/assets/api_model_role.md.DFCGXTBA.js +1 -0
  50. package/public/assets/api_model_role.md.DFCGXTBA.lean.js +1 -0
  51. package/public/assets/api_model_user-to-role.md.QNC96rs-.js +1 -0
  52. package/public/assets/api_model_user-to-role.md.QNC96rs-.lean.js +1 -0
  53. package/public/assets/api_model_user.md.C2GSzwZj.js +1 -0
  54. package/public/assets/api_model_user.md.C2GSzwZj.lean.js +1 -0
  55. package/public/assets/api_policy_authentication.md.Ccj8Rneb.js +5 -0
  56. package/public/assets/api_policy_authentication.md.Ccj8Rneb.lean.js +1 -0
  57. package/public/assets/api_policy_authorization.md.CP3y7VOT.js +8 -0
  58. package/public/assets/api_policy_authorization.md.CP3y7VOT.lean.js +1 -0
  59. package/public/assets/api_policy_index.md.CmaeRtru.js +1 -0
  60. package/public/assets/api_policy_index.md.CmaeRtru.lean.js +1 -0
  61. package/public/assets/api_policy_user.md.ePU_LHGT.js +1 -0
  62. package/public/assets/api_policy_user.md.ePU_LHGT.lean.js +1 -0
  63. package/public/assets/api_routing.md.BP98xeNw.js +17 -0
  64. package/public/assets/api_routing.md.BP98xeNw.lean.js +1 -0
  65. package/public/assets/api_service_auth-manager.md.CcpV6slZ.js +1 -0
  66. package/public/assets/api_service_auth-manager.md.CcpV6slZ.lean.js +1 -0
  67. package/public/assets/api_service_authentication-passport.md.DvhoW1TR.js +1 -0
  68. package/public/assets/api_service_authentication-passport.md.DvhoW1TR.lean.js +1 -0
  69. package/public/assets/api_service_authentication-strategies.md.DjDT2F9g.js +1 -0
  70. package/public/assets/api_service_authentication-strategies.md.DjDT2F9g.lean.js +1 -0
  71. package/public/assets/api_service_authorization-node.md.DAN4WdDZ.js +1 -0
  72. package/public/assets/api_service_authorization-node.md.DAN4WdDZ.lean.js +1 -0
  73. package/public/assets/api_service_authorization-policy-generator.md.IaQjgxfZ.js +19 -0
  74. package/public/assets/api_service_authorization-policy-generator.md.IaQjgxfZ.lean.js +1 -0
  75. package/public/assets/api_service_authorization-tree.md.I7ff4vao.js +1 -0
  76. package/public/assets/api_service_authorization-tree.md.I7ff4vao.lean.js +1 -0
  77. package/public/assets/api_service_index.md.Bfk1E4Zn.js +1 -0
  78. package/public/assets/api_service_index.md.Bfk1E4Zn.lean.js +1 -0
  79. package/public/assets/app.Bnek3cfe.js +1 -0
  80. package/public/assets/chunks/framework.BaHG-QLs.js +17 -0
  81. package/public/assets/chunks/idp-login.B596H5Zv.js +1 -0
  82. package/public/assets/chunks/theme.BUrgq2uM.js +1 -0
  83. package/public/assets/guides_getting-started.md.BMwF59kE.js +5 -0
  84. package/public/assets/guides_getting-started.md.BMwF59kE.lean.js +1 -0
  85. package/public/assets/guides_index.md.CUqoqPFW.js +1 -0
  86. package/public/assets/guides_index.md.CUqoqPFW.lean.js +1 -0
  87. package/public/assets/guides_openid-connect.md.CWezg52j.js +49 -0
  88. package/public/assets/guides_openid-connect.md.CWezg52j.lean.js +1 -0
  89. package/public/assets/guides_saml.md.BBlq_CTl.js +44 -0
  90. package/public/assets/guides_saml.md.BBlq_CTl.lean.js +1 -0
  91. package/public/assets/idp-login.B4Dj1tzS.png +0 -0
  92. package/public/assets/idp-saml-cert.Dyrxdyfk.png +0 -0
  93. package/public/assets/index.md.B8uyAhM4.js +1 -0
  94. package/public/assets/index.md.B8uyAhM4.lean.js +1 -0
  95. package/public/assets/inter-italic-cyrillic-ext.r48I6akx.woff2 +0 -0
  96. package/public/assets/inter-italic-cyrillic.By2_1cv3.woff2 +0 -0
  97. package/public/assets/inter-italic-greek-ext.1u6EdAuj.woff2 +0 -0
  98. package/public/assets/inter-italic-greek.DJ8dCoTZ.woff2 +0 -0
  99. package/public/assets/inter-italic-latin-ext.CN1xVJS-.woff2 +0 -0
  100. package/public/assets/inter-italic-latin.C2AdPX0b.woff2 +0 -0
  101. package/public/assets/inter-italic-vietnamese.BSbpV94h.woff2 +0 -0
  102. package/public/assets/inter-roman-cyrillic-ext.BBPuwvHQ.woff2 +0 -0
  103. package/public/assets/inter-roman-cyrillic.C5lxZ8CY.woff2 +0 -0
  104. package/public/assets/inter-roman-greek-ext.CqjqNYQ-.woff2 +0 -0
  105. package/public/assets/inter-roman-greek.BBVDIX6e.woff2 +0 -0
  106. package/public/assets/inter-roman-latin-ext.4ZJIpNVo.woff2 +0 -0
  107. package/public/assets/inter-roman-latin.Di8DUHzh.woff2 +0 -0
  108. package/public/assets/inter-roman-vietnamese.BjW4sHH5.woff2 +0 -0
  109. package/public/assets/introduction.md.DjcXFFe8.js +9 -0
  110. package/public/assets/introduction.md.DjcXFFe8.lean.js +1 -0
  111. package/public/assets/style.C4vbPc5Z.css +1 -0
  112. package/public/guides/getting-started.html +28 -0
  113. package/public/guides/index.html +24 -0
  114. package/public/guides/openid-connect.html +73 -0
  115. package/public/guides/saml.html +68 -0
  116. package/public/hashmap.json +1 -0
  117. package/public/index.html +24 -0
  118. package/public/introduction.html +32 -0
package/.editorconfig ADDED
@@ -0,0 +1,9 @@
1
+ root = true
2
+
3
+ [*]
4
+ indent_size = 4
5
+ indent_style = tab
6
+ end_of_line = lf
7
+ charset = utf-8
8
+ ij_javascript_use_double_quotes = true
9
+ ij_javascript_force_quote_style = true
@@ -2,7 +2,7 @@
2
2
 
3
3
  module.exports = function() {
4
4
  const api = this;
5
- const { services } = api.runtime;
5
+ const { services } = api;
6
6
 
7
7
  /**
8
8
  * Implements properties and behavior of a single authorization rule
package/api/model/role.js CHANGED
@@ -7,7 +7,7 @@
7
7
  */
8
8
  module.exports = function() {
9
9
  const api = this;
10
- const { models } = api.runtime;
10
+ const { models } = api;
11
11
 
12
12
  /**
13
13
  * Implements model of a role users can have for common authorization.
package/api/model/user.js CHANGED
@@ -9,7 +9,7 @@ const crypto = require( "crypto" );
9
9
  */
10
10
  module.exports = function() {
11
11
  const api = this;
12
- const { services, models } = api.runtime;
12
+ const { services, models } = api;
13
13
 
14
14
  /**
15
15
  * Implements model of a user suitable for authenticating as.
@@ -2,10 +2,10 @@
2
2
 
3
3
  module.exports = function() {
4
4
  const api = this;
5
- const { models, services } = api.runtime;
5
+ const { models, service } = api;
6
6
 
7
- const logAlert = api.log( "hitchy:plugin:auth:alert" );
8
- const logDebug = api.log( "hitchy:plugin:auth:debug" );
7
+ const logAlert = api.log( "hitchy:auth:alert" );
8
+ const logDebug = api.log( "hitchy:auth:debug" );
9
9
 
10
10
  /**
11
11
  * Implements policy handlers transparently managing authentication process
@@ -28,7 +28,7 @@ module.exports = function() {
28
28
 
29
29
  this.local.passportInjected = true;
30
30
 
31
- const { AuthenticationPassport } = services;
31
+ const { AuthenticationPassport } = service;
32
32
 
33
33
  AuthenticationPassport.initialize()( req, res, initializeError => {
34
34
  if ( initializeError ) {
@@ -42,7 +42,7 @@ module.exports = function() {
42
42
  res.set( "X-Authorized-As", roles.join( "," ) );
43
43
  }
44
44
 
45
- next( sessionError );
45
+ this.updateSessionId( req, res, error => next( sessionError || error ) );
46
46
  } );
47
47
  }
48
48
  } );
@@ -76,7 +76,7 @@ module.exports = function() {
76
76
  return;
77
77
  }
78
78
 
79
- services.AuthManager.checkAuthentication( parts[1], parts[2] )
79
+ service.AuthManager.checkAuthentication( parts[1], parts[2] )
80
80
  .then( user => {
81
81
  req.user = user; // eslint-disable-line no-param-reassign
82
82
 
@@ -96,7 +96,7 @@ module.exports = function() {
96
96
  */
97
97
  static login( req, res, next ) {
98
98
  const { strategy } = req.params;
99
- const { AuthenticationStrategies, AuthenticationPassport } = services;
99
+ const { AuthenticationStrategies, AuthenticationPassport } = service;
100
100
  const defaultStrategy = AuthenticationStrategies.defaultStrategy();
101
101
 
102
102
  req.fetchBody()
@@ -119,7 +119,7 @@ module.exports = function() {
119
119
  } );
120
120
  } );
121
121
  } )
122
- .then( next )
122
+ .then( () => this.updateSessionId( req, res, next ) )
123
123
  .catch( err => {
124
124
  logAlert( err );
125
125
 
@@ -146,7 +146,7 @@ module.exports = function() {
146
146
  if ( req.user ) {
147
147
  const { uuid, name } = req.user;
148
148
 
149
- services.AuthManager.listRolesOfUser( new models.User( uuid ) )
149
+ service.AuthManager.listRolesOfUser( new models.User( uuid ) )
150
150
  .then( roles => {
151
151
  req.user.roles = roles; // eslint-disable-line no-param-reassign
152
152
 
@@ -192,21 +192,52 @@ module.exports = function() {
192
192
  .then( async willLogoutInFuture => {
193
193
  if ( !willLogoutInFuture ) {
194
194
  if ( typeof req.logout === "function" ) {
195
- await req.logout();
195
+ await new Promise( ( resolve, reject ) => {
196
+ req.logout( error => ( error ? reject( error ) : resolve() ) );
197
+ } );
198
+ } else {
199
+ // re-generating session on change of user authentication mitigates session-related attacks
200
+ // -> passport's logout does not seem to exist, so we can't rely on passport regenerating the session
201
+ await new Promise( ( resolve, reject ) => req.session.regenerate( error => ( error ? reject( error ) : resolve() ) ) );
196
202
  }
197
203
 
198
- req.session.drop();
199
204
  req.user = undefined; // eslint-disable-line no-param-reassign
200
205
 
201
206
  res.set( "X-Authenticated-As", undefined );
202
207
  res.set( "X-Authorized-As", undefined );
203
-
204
- next();
205
208
  }
209
+
210
+ this.updateSessionId( req, res, next );
206
211
  } )
207
212
  .catch( next );
208
213
  }
209
214
 
215
+ /**
216
+ * Promotes current session's ID in case it has changed compared to what
217
+ * the request was providing.
218
+ *
219
+ * @param {Hitchy.Core.IncomingMessage} req request descriptor
220
+ * @param {Hitchy.Core.ServerResponse} res response manager
221
+ * @param {Hitchy.Core.ContinuationHandler} next callback to invoke to continue processing a request
222
+ * @returns {void}
223
+ */
224
+ static updateSessionId( req, res, next ) {
225
+ const { Session } = service;
226
+
227
+ const sid = req.session?.id;
228
+
229
+ if ( sid != null ) {
230
+ if ( sid !== Session.getSessionIdOfRequest( req ) ) {
231
+ // session might have been re-generated, hence its ID has
232
+ // changed and we need to inform the client to use a
233
+ // different session ID on next request
234
+ req.session.$promoteSessionIdInResponse( res );
235
+ }
236
+ }
237
+
238
+ next();
239
+ }
240
+
210
241
  /**
211
242
  * Ensures current request's user has authenticated.
212
243
  *
@@ -2,7 +2,7 @@
2
2
 
3
3
  module.exports = function() {
4
4
  const api = this;
5
- const { models } = api.runtime;
5
+ const { models } = api;
6
6
 
7
7
  return {
8
8
  /**
@@ -2,9 +2,9 @@
2
2
 
3
3
  module.exports = function() {
4
4
  const api = this;
5
- const { models, services } = api.runtime;
5
+ const { models, services } = api;
6
6
 
7
- const logDebug = api.log( "hitchy:plugin:auth:debug" );
7
+ const logDebug = api.log( "hitchy:auth:debug" );
8
8
 
9
9
  /**
10
10
  * Implements several helper methods meant to simplify user/role management.
@@ -5,8 +5,8 @@ const PassportLib = require( "passport" );
5
5
  module.exports = function() {
6
6
  const api = this;
7
7
 
8
- const logAlert = api.log( "hitchy:plugin:auth:alert" );
9
- const logDebug = api.log( "hitchy:plugin:auth:debug" );
8
+ const logAlert = api.log( "hitchy:auth:alert" );
9
+ const logDebug = api.log( "hitchy:auth:debug" );
10
10
 
11
11
  const passport = new PassportLib.Passport();
12
12
 
@@ -16,7 +16,7 @@ module.exports = function() {
16
16
  * @returns {void}
17
17
  */
18
18
  passport.integrateWithHitchy = () => {
19
- const { models: { User }, services: { AuthManager } } = api.runtime;
19
+ const { models: { User }, services: { AuthManager } } = api;
20
20
  const { strategies } = api.config.auth;
21
21
 
22
22
 
@@ -17,9 +17,9 @@ const RemoteAuthCustomData = new Map();
17
17
 
18
18
  module.exports = function() {
19
19
  const api = this;
20
- const { models, services } = api.runtime;
20
+ const { models, services } = api;
21
21
 
22
- const logAlert = api.log( "hitchy:plugin:auth:alert" );
22
+ const logAlert = api.log( "hitchy:auth:alert" );
23
23
 
24
24
  /**
25
25
  * Fetches named user's local profile.
@@ -232,7 +232,7 @@ module.exports = function() {
232
232
  getLocalProfile( strategyName, userInfo.preferred_username, true, done );
233
233
  };
234
234
 
235
- const { Issuer, Strategy } = require( "openid-client" );
235
+ const { Issuer, Strategy, generators } = require( "openid-client" );
236
236
  const issuer = await Issuer.discover( config.discovery_url );
237
237
  const client = new issuer.Client( config );
238
238
 
@@ -248,7 +248,7 @@ module.exports = function() {
248
248
  return Promise.resolve( false );
249
249
  }
250
250
 
251
- const state = req.session[key] = require( "crypto" ).randomBytes( 32 ).toString( "base64" ); // eslint-disable-line no-param-reassign
251
+ const state = req.session[key] = generators.state( 32 ); // eslint-disable-line no-param-reassign
252
252
 
253
253
  // redirect user to discovered end_session_url of IdP
254
254
  req.context.response.redirect( 302, client.endSessionUrl( { state } ) );
@@ -146,36 +146,44 @@ module.exports = function() {
146
146
  * @returns {number} >0 on accepting user(s)/role(s) explicitly, <0 on rejecting explicitly, ==0 on missing explicit impact on user(s)/role(s)
147
147
  */
148
148
  isAuthorized( userName, roleName ) {
149
+ const state = { accept: 0, reject: 0 };
150
+
151
+ if ( this.users.accept?.["*"] > 0 || this.roles.accept?.["*"] > 0 ) {
152
+ state.accept = 1;
153
+ }
154
+
155
+ if ( this.users.reject?.["*"] > 0 || this.roles.reject?.["*"] > 0 ) {
156
+ state.reject = 1;
157
+ }
158
+
149
159
  const users = Array.isArray( userName ) ? userName : userName ? [userName] : [];
150
160
  const roles = Array.isArray( roleName ) ? roleName : roleName ? [roleName] : [];
151
- const actions = [ "accept", "reject" ];
152
- const state = { accept: false, reject: false };
153
-
154
- for ( const [ names, rules ] of [ [ users, this.users ], [ roles, this.roles ] ] ) {
155
- for ( let i = 0, length = names.length; i < length; i++ ) {
156
- const name = names[i];
157
161
 
158
- if ( name && typeof name === "string" && name.trim().length > 0 ) {
159
- for ( let j = 0; j < 2; j++ ) {
160
- const action = actions[j];
162
+ for ( const [ names, rules, level ] of [ [ users, this.users, 3 ], [ roles, this.roles, 2 ] ] ) {
163
+ for ( const name of names ) {
164
+ if ( name && typeof name === "string" ) {
165
+ const _name = name.trim();
161
166
 
162
- if ( rules[action][name] > 0 ) {
163
- state[action] = true;
167
+ if ( _name !== "" ) {
168
+ for ( const action of [ "accept", "reject" ] ) {
169
+ if ( rules[action][_name] > 0 ) {
170
+ state[action] = Math.max( state[action], level );
171
+ }
164
172
  }
165
173
  }
166
174
  }
167
175
  }
168
176
  }
169
177
 
170
- if ( state.accept ) {
171
- if ( state.reject ) {
172
- throw new Error( `granting and revoking access on "${this.path()}" to/from user(s) "${userName || "<none>"}" and/or role(s) "${roleName || "<none>"}"` );
173
- }
178
+ if ( state.accept > 0 && state.reject === state.accept ) {
179
+ throw new Error( `granting and revoking access on "${this.path()}" to/from user(s) "${userName || "<none>"}" and/or role(s) "${roleName || "<none>"}"` );
180
+ }
174
181
 
182
+ if ( state.accept > state.reject ) {
175
183
  return 1;
176
184
  }
177
185
 
178
- if ( state.reject ) {
186
+ if ( state.accept < state.reject ) {
179
187
  return -1;
180
188
  }
181
189
 
@@ -183,7 +191,7 @@ module.exports = function() {
183
191
  }
184
192
 
185
193
  /**
186
- * Indicates if current node has any viable information.
194
+ * Indicates if current node lacks any viable information.
187
195
  *
188
196
  * @returns {boolean} true if node hasn't any viable information affecting authorization checks
189
197
  */
@@ -2,10 +2,10 @@
2
2
 
3
3
  module.exports = function() {
4
4
  const api = this;
5
- const { services, models } = api.runtime;
5
+ const { services, models } = api;
6
6
 
7
- const logAlert = api.log( "hitchy:plugin:auth:alert" );
8
- const logDebug = api.log( "hitchy:plugin:auth:debug" );
7
+ const logAlert = api.log( "hitchy:auth:alert" );
8
+ const logDebug = api.log( "hitchy:auth:debug" );
9
9
 
10
10
  let cachedTree;
11
11
 
@@ -153,16 +153,16 @@ module.exports = function() {
153
153
  *
154
154
  * @param {string} selector selector of authorization, full-stop-separated sequence of names
155
155
  * @param {string|string[]} user names(s) of zero or more users to check
156
- * @param {string|string[]} role names of zero or more roles to check
156
+ * @param {string|string[]} roles names of zero or more roles to check
157
157
  * @param {boolean} acceptByDefault fallback result ro return if neither rule is affecting selected user(s) or role(s)
158
158
  * @returns {boolean} true if authorization is granted to user(s)/role(s), false if it's rejected
159
159
  */
160
- isAuthorized( selector, user, role, acceptByDefault = false ) {
160
+ isAuthorized( selector, user, roles, acceptByDefault = false ) {
161
161
  let result = 0;
162
162
 
163
163
  try {
164
164
  this.selectNode( selector, false, node => {
165
- const localResult = node.isAuthorized( user, role, acceptByDefault );
165
+ const localResult = node.isAuthorized( user, roles, acceptByDefault );
166
166
 
167
167
  if ( localResult !== 0 ) {
168
168
  result = localResult;
@@ -267,10 +267,12 @@ module.exports = function() {
267
267
  continue;
268
268
  }
269
269
 
270
- const list = String( items ).trim().split( /\s*,[,\s]*/ ).filter( i => i != null && i !== "" );
270
+ const list = String( items ).trim().split( "," )
271
+ .map( i => String( i ?? "" ).trim().replace( /([+-])\s*/, "$1" ) ) // remove whitespace here to mitigate ReDOS vulnerability in pattern below
272
+ .filter( i => i != null && i !== "" );
271
273
 
272
274
  for ( const who of list ) {
273
- const [ all, mode, role, id ] = /^\s*([+-]?)\s*(@?)\s*([^\s@,+-][^\s@,]*)\s*$/.exec( who ) || [];
275
+ const [ all, mode, role, id ] = /^([+-]?)(@?)\s*([^\s@,+-][^\s@,]*)$/.exec( who ) || [];
274
276
 
275
277
  if ( all ) {
276
278
  const isGranting = mode ? mode !== "-" : key !== "revoke";
@@ -0,0 +1,150 @@
1
+ "use strict";
2
+
3
+ module.exports = function( options, HitchyPluginSession ) {
4
+ const api = this;
5
+
6
+ const logDebug = api.log( "hitchy:auth:debug" );
7
+ const logError = api.log( "hitchy:auth:error" );
8
+
9
+ /**
10
+ * Extends session implemented by `@hitchy/plugin-session` to provide
11
+ * additional methods used by passport as it is assuming to work with
12
+ * `express-session`.
13
+ */
14
+ class PassportCompatibleSession extends HitchyPluginSession {
15
+ #id;
16
+
17
+ #user;
18
+
19
+ #data;
20
+
21
+ /**
22
+ * @param {string} sessionId unique ID of session
23
+ * @param {Object<string,any>} properties copy (!) of session's properties recovered from a session store
24
+ */
25
+ constructor( sessionId, properties ) {
26
+ super( sessionId, properties, false );
27
+
28
+ this.#id = sessionId;
29
+ this.#data = properties;
30
+
31
+ Object.defineProperties( this, {
32
+ /**
33
+ * Exposes ID of session.
34
+ *
35
+ * @name Session#id
36
+ * @property {string}
37
+ * @readonly
38
+ */
39
+ id: { get: () => this.#id },
40
+
41
+ /**
42
+ * Exposes authenticated user current session is associated with.
43
+ *
44
+ * @name Session#user
45
+ * @property {{uuid:string}}
46
+ */
47
+ user: {
48
+ get: () => this.#user,
49
+ set: newUser => {
50
+ if ( !newUser || typeof newUser !== "object" || !newUser.uuid ) {
51
+ throw new Error( "invalid user descriptor rejected" );
52
+ }
53
+
54
+ if ( this.#user != null ) {
55
+ if ( newUser.uuid === this.#user.uuid ) {
56
+ return;
57
+ }
58
+
59
+ throw new Error( "invalid request for replacing user of current session" );
60
+ }
61
+
62
+ this.#user = Object.freeze( { ...newUser } );
63
+ },
64
+ },
65
+
66
+ /**
67
+ * Exposes space for saving additional custom data in context of
68
+ * current session.
69
+ *
70
+ * @name Session#data
71
+ * @property {object}
72
+ * @readonly
73
+ */
74
+ $data: { get: () => this.#data },
75
+ } );
76
+
77
+ // OIDC plugin for passport is using custom redirection that's
78
+ // preventing session from being persisted in late policy
79
+ // -> instantly persist any changes related to the OIDC integration
80
+ this.on( "data-changed", ( [key] ) => {
81
+ if ( key.startsWith( "oidc:" ) ) {
82
+ logDebug( "instantly persisting session on setting %s", key );
83
+
84
+ this.constructor.getStore().save( this.id, this.$data ).catch( cause => {
85
+ logError( "persisting session failed:", cause.stack );
86
+ } );
87
+ }
88
+ } );
89
+ }
90
+
91
+ /**
92
+ * Drops any current session and creates a new one.
93
+ *
94
+ * This method is used by passport.
95
+ *
96
+ * @param {function(error: (Error|undefined)): void} doneFn callback invoked when session has been re-generated or some error occurred
97
+ * @returns {void}
98
+ */
99
+ regenerate( doneFn ) {
100
+ logDebug( "re-generating current user session" );
101
+
102
+ const store = this.constructor.getStore();
103
+
104
+ store.drop( this.#id )
105
+ .then( () => store.create() )
106
+ .then( sessionId => {
107
+ if ( !sessionId ) {
108
+ throw new Error( "failed to assign new session ID" );
109
+ }
110
+
111
+ return store.load( sessionId )
112
+ .then( record => {
113
+ if ( !record ) {
114
+ throw new Error( "failed to recover new session record" );
115
+ }
116
+
117
+ this.#id = sessionId;
118
+ this.#data = record;
119
+ this.#user = null;
120
+
121
+ logDebug( `new session ID is ${sessionId}` );
122
+ doneFn();
123
+ } );
124
+ } )
125
+ .catch( cause => {
126
+ logError( "re-generating current user session failed:", cause.stack );
127
+
128
+ doneFn( cause );
129
+ } );
130
+ }
131
+
132
+ /**
133
+ * Explicitly triggers saving of current session to configured store.
134
+ *
135
+ * This method is used by passport.
136
+ *
137
+ * @param {function(error: (Error|undefined)): void} doneFn callback invoked when session has been saved or some error occurred
138
+ * @returns {void}
139
+ */
140
+ save( doneFn ) {
141
+ const store = this.constructor.getStore();
142
+
143
+ store.save( this.#id, this.#data )
144
+ .then( () => doneFn() )
145
+ .catch( doneFn );
146
+ }
147
+ }
148
+
149
+ return PassportCompatibleSession;
150
+ };
package/index.js CHANGED
@@ -10,7 +10,7 @@ module.exports = function( options, plugins ) {
10
10
  const { strategies } = api.config.auth;
11
11
 
12
12
  if ( !strategies.local ) {
13
- strategies.local = api.runtime.services.AuthenticationStrategies.generateLocal();
13
+ strategies.local = api.services.AuthenticationStrategies.generateLocal();
14
14
  }
15
15
  },
16
16
 
@@ -22,7 +22,7 @@ module.exports = function( options, plugins ) {
22
22
  async initialize() {
23
23
  const {
24
24
  services: { AuthorizationTree, AuthenticationPassport, AuthManager },
25
- } = api.runtime;
25
+ } = api;
26
26
  const { current } = AuthorizationTree;
27
27
 
28
28
  AuthenticationPassport.integrateWithHitchy();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hitchy/plugin-auth",
3
- "version": "0.3.7",
3
+ "version": "0.3.8",
4
4
  "description": "user authentication and authorization for Hitchy",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -8,8 +8,8 @@
8
8
  "lint": "eslint .",
9
9
  "test": "hitchy-pm session cookies odem --exec c8 -r text -r html mocha --recursive --exclude **/*.dist.js ./test/scripts --ui bdd",
10
10
  "test:smoke": "hitchy-pm session cookies odem --exec hitchy --project test/project/testbed --plugins . --plugin . --debug",
11
- "docs:dev": "vuepress dev docs",
12
- "docs:build": "vuepress build docs"
11
+ "docs:dev": "vitepress dev docs",
12
+ "docs:build": "vitepress build docs"
13
13
  },
14
14
  "repository": {
15
15
  "type": "git",
@@ -25,24 +25,24 @@
25
25
  "@hitchy/core": "0.8.x"
26
26
  },
27
27
  "devDependencies": {
28
- "@hitchy/server-dev-tools": "^0.4.8",
28
+ "@hitchy/server-dev-tools": "^0.4.9",
29
29
  "@hitchy/types": "^0.1.3",
30
- "c8": "^8.0.1",
31
- "eslint": "^8.47.0",
32
- "eslint-config-cepharum": "^1.0.13",
30
+ "c8": "^10.1.2",
31
+ "eslint": "^8.57.0",
32
+ "eslint-config-cepharum": "^1.0.14",
33
33
  "eslint-plugin-promise": "^6.1.1",
34
- "mocha": "^10.2.0",
35
- "openid-client": "^5.4.3",
34
+ "mocha": "^10.6.0",
35
+ "openid-client": "^5.6.5",
36
36
  "passport-saml": "^3.2.4",
37
37
  "should": "^13.2.3",
38
38
  "should-http": "^0.1.1",
39
- "vuepress": "^1.9.10"
39
+ "vitepress": "^1.3.1"
40
40
  },
41
41
  "dependencies": {
42
42
  "@hitchy/plugin-cookies": "^0.1.8",
43
43
  "@hitchy/plugin-odem": "^0.7.8",
44
- "@hitchy/plugin-session": "^0.1.12",
45
- "passport": "^0.5.3",
44
+ "@hitchy/plugin-session": "^0.4.0",
45
+ "passport": "^0.7.0",
46
46
  "passport-local": "^1.0.0"
47
47
  }
48
48
  }
@@ -0,0 +1,21 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en-US" dir="ltr">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <title>404 | Hitchy Auth Manual</title>
7
+ <meta name="description" content="Not Found">
8
+ <meta name="generator" content="VitePress v1.3.1">
9
+ <link rel="preload stylesheet" href="/assets/style.C4vbPc5Z.css" as="style">
10
+
11
+ <script type="module" src="/assets/app.Bnek3cfe.js"></script>
12
+ <link rel="preload" href="/assets/inter-roman-latin.Di8DUHzh.woff2" as="font" type="font/woff2" crossorigin="">
13
+ <script id="check-dark-mode">(()=>{const e=localStorage.getItem("vitepress-theme-appearance")||"auto",a=window.matchMedia("(prefers-color-scheme: dark)").matches;(!e||e==="auto"?a:e==="dark")&&document.documentElement.classList.add("dark")})();</script>
14
+ <script id="check-mac-os">document.documentElement.classList.toggle("mac",/Mac|iPhone|iPod|iPad/i.test(navigator.platform));</script>
15
+ </head>
16
+ <body>
17
+ <div id="app"></div>
18
+ <script>window.__VP_HASH_MAP__=JSON.parse("{\"api_config.md\":\"BiPnBhyk\",\"api_controller_index.md\":\"mhiyhr_C\",\"api_controller_user.md\":\"BiFYPTow\",\"api_index.md\":\"j6eBaebO\",\"api_model_authorization-rule.md\":\"CFNqudsp\",\"api_model_index.md\":\"Dw3UH73J\",\"api_model_role.md\":\"DFCGXTBA\",\"api_model_user-to-role.md\":\"QNC96rs-\",\"api_model_user.md\":\"C2GSzwZj\",\"api_policy_authentication.md\":\"Ccj8Rneb\",\"api_policy_authorization.md\":\"CP3y7VOT\",\"api_policy_index.md\":\"CmaeRtru\",\"api_policy_user.md\":\"ePU_LHGT\",\"api_routing.md\":\"BP98xeNw\",\"api_service_auth-manager.md\":\"CcpV6slZ\",\"api_service_authentication-passport.md\":\"DvhoW1TR\",\"api_service_authentication-strategies.md\":\"DjDT2F9g\",\"api_service_authorization-node.md\":\"DAN4WdDZ\",\"api_service_authorization-policy-generator.md\":\"IaQjgxfZ\",\"api_service_authorization-tree.md\":\"I7ff4vao\",\"api_service_index.md\":\"Bfk1E4Zn\",\"guides_getting-started.md\":\"BMwF59kE\",\"guides_index.md\":\"CUqoqPFW\",\"guides_openid-connect.md\":\"CWezg52j\",\"guides_saml.md\":\"BBlq_CTl\",\"index.md\":\"B8uyAhM4\",\"introduction.md\":\"DjcXFFe8\"}");window.__VP_SITE_DATA__=JSON.parse("{\"lang\":\"en-US\",\"dir\":\"ltr\",\"title\":\"Hitchy Auth Manual\",\"description\":\"A VitePress site\",\"base\":\"/\",\"head\":[],\"router\":{\"prefetchLinks\":true},\"appearance\":true,\"themeConfig\":{\"displayAllHeaders\":true,\"socialLinks\":[{\"icon\":{\"svg\":\"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" viewBox=\\\"0 0 512 512\\\"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d=\\\"M503.5 204.6L502.8 202.8L433.1 21C431.7 17.5 429.2 14.4 425.9 12.4C423.5 10.8 420.8 9.9 417.9 9.6C415 9.3 412.2 9.7 409.5 10.7C406.8 11.7 404.4 13.3 402.4 15.5C400.5 17.6 399.1 20.1 398.3 22.9L351.3 166.9H160.8L113.7 22.9C112.9 20.1 111.5 17.6 109.6 15.5C107.6 13.4 105.2 11.7 102.5 10.7C99.9 9.7 97 9.3 94.1 9.6C91.3 9.9 88.5 10.8 86.1 12.4C82.8 14.4 80.3 17.5 78.9 21L9.3 202.8L8.5 204.6C-1.5 230.8-2.7 259.6 5 286.6C12.8 313.5 29.1 337.3 51.5 354.2L51.7 354.4L52.3 354.8L158.3 434.3L210.9 474L242.9 498.2C246.6 500.1 251.2 502.5 255.9 502.5C260.6 502.5 265.2 500.1 268.9 498.2L300.9 474L353.5 434.3L460.2 354.4L460.5 354.1C482.9 337.2 499.2 313.5 506.1 286.6C514.7 259.6 513.5 230.8 503.5 204.6z\\\"/></svg>\"},\"link\":\"https://gitlab.com/hitchy/plugin-auth\",\"ariaLabel\":\"Link to code repository\"}],\"nav\":[{\"text\":\"Home\",\"link\":\"/\"},{\"text\":\"Guides\",\"link\":\"/guides/\"},{\"text\":\"API\",\"link\":\"/api/\"},{\"text\":\"Hitchy\",\"items\":[{\"text\":\"Core\",\"link\":\"https://core.hitchy.org/\"},{\"text\":\"Plugins\",\"items\":[{\"text\":\"Odem\",\"link\":\"https://odem.hitchy.org/\"},{\"text\":\"Auth\",\"link\":\"/\"}]},{\"text\":\"Tools\",\"items\":[{\"text\":\"SDT\",\"link\":\"https://sdt.hitchy.org/\"}]}]}]},\"locales\":{},\"scrollOffset\":134,\"cleanUrls\":false}");</script>
19
+
20
+ </body>
21
+ </html>