@loopback/authentication 4.2.9 → 6.0.1

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/CHANGELOG.md CHANGED
@@ -3,6 +3,62 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [6.0.1](https://github.com/strongloop/loopback-next/compare/@loopback/authentication@6.0.0...@loopback/authentication@6.0.1) (2020-08-27)
7
+
8
+ **Note:** Version bump only for package @loopback/authentication
9
+
10
+
11
+
12
+
13
+
14
+ # [6.0.0](https://github.com/strongloop/loopback-next/compare/@loopback/authentication@5.0.0...@loopback/authentication@6.0.0) (2020-08-19)
15
+
16
+
17
+ ### Features
18
+
19
+ * **authentication:** add support for multiple strategies on same method ([f2f1580](https://github.com/strongloop/loopback-next/commit/f2f15806189d568d0a2c6d6198de74e6801f094c)), closes [#5310](https://github.com/strongloop/loopback-next/issues/5310)
20
+ * **authentication:** update signature of authenticate decorator ([ae6c0e6](https://github.com/strongloop/loopback-next/commit/ae6c0e68a58a2b574fd534242e599aa2a96fc855))
21
+
22
+
23
+ ### BREAKING CHANGES
24
+
25
+ * **authentication:** The `@authenticate` signature changed, options are no longer
26
+ a separate input parameter but instead have to be provided in the metadata object.
27
+ The metadata value is now `AuthenticationMetadata[]`.
28
+
29
+ Signed-off-by: nflaig <nflaig@protonmail.com>
30
+
31
+
32
+
33
+
34
+
35
+ # [5.0.0](https://github.com/strongloop/loopback-next/compare/@loopback/authentication@4.2.10...@loopback/authentication@5.0.0) (2020-08-05)
36
+
37
+
38
+ ### Features
39
+
40
+ * **authentication:** add a middleware for authentication ([de6f96c](https://github.com/strongloop/loopback-next/commit/de6f96c7af946486ded0425e643ff22c92d6f04f))
41
+ * **authentication:** authentication action is no longer needed ([041fa21](https://github.com/strongloop/loopback-next/commit/041fa213482bcfe723dd075518fa890dce3936e0))
42
+
43
+
44
+ ### BREAKING CHANGES
45
+
46
+ * **authentication:** with the newly introduced middleware-based sequence for
47
+ '@loopback/rest', it is no longer needed to explicitly add the authentication
48
+ action for middleware-based sequence.
49
+
50
+
51
+
52
+
53
+
54
+ ## [4.2.10](https://github.com/strongloop/loopback-next/compare/@loopback/authentication@4.2.9...@loopback/authentication@4.2.10) (2020-07-20)
55
+
56
+ **Note:** Version bump only for package @loopback/authentication
57
+
58
+
59
+
60
+
61
+
6
62
  ## [4.2.9](https://github.com/strongloop/loopback-next/compare/@loopback/authentication@4.2.8...@loopback/authentication@4.2.9) (2020-06-30)
7
63
 
8
64
  **Note:** Version bump only for package @loopback/authentication
package/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # @loopback/authentication
2
2
 
3
- A LoopBack 4 component for authentication support.
3
+ A LoopBack 4 component for authentication support. Its corresponding
4
+ documentation is in
5
+ [LoopBack component authentication](https://loopback.io/doc/en/lb4/Loopback-component-authentication.html)
4
6
 
5
7
  ## Overview
6
8
 
@@ -13,7 +15,8 @@ It contains:
13
15
 
14
16
  - A decorator to express an authentication requirement on controller methods
15
17
  - A provider to access method-level authentication metadata
16
- - An action in the REST sequence to enforce authentication
18
+ - An action in the REST sequence to enforce authentication (**No longer needed
19
+ for middleware based sequence**)
17
20
  - An extension point to discover all authentication strategies and handle the
18
21
  delegation
19
22
 
@@ -25,22 +28,23 @@ npm install --save @loopback/authentication
25
28
 
26
29
  ## Basic Use
27
30
 
28
- [Load the AuthenticationComponent](https://loopback.io/doc/en/lb4/Loopback-component-authentication.html#authentication-component)
31
+ [Load the AuthenticationComponent](https://loopback.io/doc/en/lb4/Loopback-component-authentication.html#mounting-authentication-component)
29
32
  into your application.
30
33
 
31
34
  **Extension developers** need to:
32
35
 
33
- - [create custom authentication strategies](https://loopback.io/doc/en/lb4/Loopback-component-authentication.html#creating-a-custom-authentication-strategy)
36
+ - [create custom authentication strategies](https://loopback.io/doc/en/lb4/Implement-your-own-strategy.html)
34
37
 
35
38
  **Application Developers** need to:
36
39
 
37
- - [decorate controller functions with the authentication decorator](https://loopback.io/doc/en/lb4/Loopback-component-authentication.html#using-the-authentication-decorator)
38
- - [add the authentication action to a custom sequence](https://loopback.io/doc/en/lb4/Loopback-component-authentication.html#adding-an-authentication-action-to-a-custom-sequence)
40
+ - [decorate controller functions with the authentication decorator](https://loopback.io/doc/en/lb4/Authentication-component-decorator.html)
41
+ - [add the authentication action to a custom sequence](https://loopback.io/doc/en/lb4/Authentication-component-action.html#adding-an-authentication-action-to-a-custom-sequence)
39
42
  and
40
- [bind the custom sequence to the application](https://loopback.io/doc/en/lb4/Loopback-component-authentication.html#binding-the-authenticating-sequence-to-the-application)
41
- - [register the authentication strategies](https://loopback.io/doc/en/lb4/Loopback-component-authentication.html#registering-a-custom-authentication-strategy)
43
+ [bind the custom sequence to the application](https://loopback.io/doc/en/lb4/Authentication-component-action.html#binding-the-authenticating-sequence-to-the-application)
44
+ (**No longer needed for middleware based sequence**)
45
+ - [register the authentication strategies](https://loopback.io/doc/en/lb4/Authentication-component-strategy.html)
42
46
 
43
- [Create and register a passport based strategy](https://www.npmjs.com/package/@loopback/authentication-passport)
47
+ [Create and register a passport based strategy](https://loopback.io/doc/en/lb4/Authentication-passport.html)
44
48
 
45
49
  ## Related resources
46
50
 
@@ -1,5 +1,7 @@
1
- import { Component, ProviderMap } from '@loopback/core';
1
+ import { Component } from '@loopback/core';
2
+ import { AuthenticateActionProvider, AuthenticationMiddlewareProvider, AuthenticationStrategyProvider, AuthMetadataProvider } from './providers';
2
3
  export declare class AuthenticationComponent implements Component {
3
- providers?: ProviderMap;
4
- constructor();
4
+ providers: {
5
+ [x: string]: typeof AuthenticateActionProvider | typeof AuthenticationMiddlewareProvider | typeof AuthMetadataProvider | typeof AuthenticationStrategyProvider;
6
+ };
5
7
  }
@@ -15,12 +15,13 @@ let AuthenticationComponent = class AuthenticationComponent {
15
15
  [keys_1.AuthenticationBindings.AUTH_ACTION.key]: providers_1.AuthenticateActionProvider,
16
16
  [keys_1.AuthenticationBindings.STRATEGY.key]: providers_1.AuthenticationStrategyProvider,
17
17
  [keys_1.AuthenticationBindings.METADATA.key]: providers_1.AuthMetadataProvider,
18
+ [keys_1.AuthenticationBindings.AUTHENTICATION_MIDDLEWARE
19
+ .key]: providers_1.AuthenticationMiddlewareProvider,
18
20
  };
19
21
  }
20
22
  };
21
23
  AuthenticationComponent = tslib_1.__decorate([
22
- core_1.bind({ tags: { [core_1.ContextTags.KEY]: keys_1.AuthenticationBindings.COMPONENT } }),
23
- tslib_1.__metadata("design:paramtypes", [])
24
+ core_1.bind({ tags: { [core_1.ContextTags.KEY]: keys_1.AuthenticationBindings.COMPONENT } })
24
25
  ], AuthenticationComponent);
25
26
  exports.AuthenticationComponent = AuthenticationComponent;
26
27
  //# sourceMappingURL=authentication.component.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"authentication.component.js","sourceRoot":"","sources":["../src/authentication.component.ts"],"names":[],"mappings":";AAAA,sDAAsD;AACtD,wCAAwC;AACxC,+CAA+C;AAC/C,gEAAgE;;;;AAEhE,yCAAyE;AACzE,iCAA8C;AAC9C,2CAIqB;AAGrB,IAAa,uBAAuB,GAApC,MAAa,uBAAuB;IAGlC;QACE,IAAI,CAAC,SAAS,GAAG;YACf,CAAC,6BAAsB,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,sCAA0B;YACpE,CAAC,6BAAsB,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,0CAA8B;YACrE,CAAC,6BAAsB,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,gCAAoB;SAC5D,CAAC;IACJ,CAAC;CACF,CAAA;AAVY,uBAAuB;IADnC,WAAI,CAAC,EAAC,IAAI,EAAE,EAAC,CAAC,kBAAW,CAAC,GAAG,CAAC,EAAE,6BAAsB,CAAC,SAAS,EAAC,EAAC,CAAC;;GACvD,uBAAuB,CAUnC;AAVY,0DAAuB"}
1
+ {"version":3,"file":"authentication.component.js","sourceRoot":"","sources":["../src/authentication.component.ts"],"names":[],"mappings":";AAAA,sDAAsD;AACtD,wCAAwC;AACxC,+CAA+C;AAC/C,gEAAgE;;;;AAEhE,yCAA4D;AAC5D,iCAA8C;AAC9C,2CAKqB;AAGrB,IAAa,uBAAuB,GAApC,MAAa,uBAAuB;IAApC;QACE,cAAS,GAAG;YACV,CAAC,6BAAsB,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,sCAA0B;YACpE,CAAC,6BAAsB,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,0CAA8B;YACrE,CAAC,6BAAsB,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,gCAAoB;YAC3D,CAAC,6BAAsB,CAAC,yBAAyB;iBAC9C,GAAG,CAAC,EAAE,4CAAgC;SAC1C,CAAC;IACJ,CAAC;CAAA,CAAA;AARY,uBAAuB;IADnC,WAAI,CAAC,EAAC,IAAI,EAAE,EAAC,CAAC,kBAAW,CAAC,GAAG,CAAC,EAAE,6BAAsB,CAAC,SAAS,EAAC,EAAC,CAAC;GACvD,uBAAuB,CAQnC;AARY,0DAAuB"}
@@ -3,11 +3,10 @@ import { AuthenticationMetadata } from '../types';
3
3
  /**
4
4
  * Mark a controller method as requiring authenticated user.
5
5
  *
6
- * @param strategyNameOrMetadata - The name of the authentication strategy to use
7
- * or the authentication metadata object.
8
- * @param options - Additional options to configure the authentication.
6
+ * @param strategies - The names of the authentication strategies to use
7
+ * or authentication metadata objects.
9
8
  */
10
- export declare function authenticate(strategyNameOrMetadata: string | AuthenticationMetadata, options?: object): (target: any, method?: string | undefined, methodDescriptor?: TypedPropertyDescriptor<any> | undefined) => any;
9
+ export declare function authenticate(...strategies: (string | AuthenticationMetadata)[]): (target: any, method?: string | undefined, methodDescriptor?: TypedPropertyDescriptor<any> | undefined) => any;
11
10
  export declare namespace authenticate {
12
11
  /**
13
12
  * `@authenticate.skip()` - a sugar decorator to skip authentication
@@ -20,4 +19,4 @@ export declare namespace authenticate {
20
19
  * @param targetClass - Target controller
21
20
  * @param methodName - Target method
22
21
  */
23
- export declare function getAuthenticateMetadata(targetClass: Constructor<{}>, methodName: string): AuthenticationMetadata | undefined;
22
+ export declare function getAuthenticateMetadata(targetClass: Constructor<{}>, methodName: string): AuthenticationMetadata[] | undefined;
@@ -12,11 +12,10 @@ class AuthenticateClassDecoratorFactory extends core_1.ClassDecoratorFactory {
12
12
  /**
13
13
  * Mark a controller method as requiring authenticated user.
14
14
  *
15
- * @param strategyNameOrMetadata - The name of the authentication strategy to use
16
- * or the authentication metadata object.
17
- * @param options - Additional options to configure the authentication.
15
+ * @param strategies - The names of the authentication strategies to use
16
+ * or authentication metadata objects.
18
17
  */
19
- function authenticate(strategyNameOrMetadata, options) {
18
+ function authenticate(...strategies) {
20
19
  return function authenticateDecoratorForClassOrMethod(
21
20
  // Class or a prototype
22
21
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -25,23 +24,27 @@ function authenticate(strategyNameOrMetadata, options) {
25
24
  // See https://github.com/strongloop/loopback-next/pull/2704
26
25
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
27
26
  methodDescriptor) {
28
- let spec;
29
- if (typeof strategyNameOrMetadata === 'object') {
30
- spec = strategyNameOrMetadata;
31
- }
32
- else {
33
- spec = { strategy: strategyNameOrMetadata, options: options !== null && options !== void 0 ? options : {} };
27
+ const specs = [];
28
+ for (const strategy of strategies) {
29
+ if (typeof strategy === 'object') {
30
+ specs.push(strategy);
31
+ }
32
+ else {
33
+ specs.push({ strategy: strategy });
34
+ }
34
35
  }
35
36
  if (method && methodDescriptor) {
36
37
  // Method
37
- return core_1.MethodDecoratorFactory.createDecorator(keys_1.AUTHENTICATION_METADATA_KEY, spec, { decoratorName: '@authenticate' })(target, method, methodDescriptor);
38
+ return core_1.MethodDecoratorFactory.createDecorator(keys_1.AUTHENTICATION_METADATA_KEY, specs, { decoratorName: '@authenticate' })(target, method, methodDescriptor);
38
39
  }
39
40
  if (typeof target === 'function' && !method && !methodDescriptor) {
40
41
  // Class
41
- return AuthenticateClassDecoratorFactory.createDecorator(keys_1.AUTHENTICATION_METADATA_CLASS_KEY, spec, { decoratorName: '@authenticate' })(target);
42
+ return AuthenticateClassDecoratorFactory.createDecorator(keys_1.AUTHENTICATION_METADATA_CLASS_KEY, specs, {
43
+ decoratorName: '@authenticate',
44
+ })(target);
42
45
  }
43
46
  // Not on a class or method
44
- throw new Error('@intercept cannot be used on a property: ' +
47
+ throw new Error('@authenticate cannot be used on a property: ' +
45
48
  core_1.DecoratorFactory.getTargetName(target, method, methodDescriptor));
46
49
  };
47
50
  }
@@ -1 +1 @@
1
- {"version":3,"file":"authenticate.decorator.js","sourceRoot":"","sources":["../../src/decorators/authenticate.decorator.ts"],"names":[],"mappings":";AAAA,sDAAsD;AACtD,wCAAwC;AACxC,+CAA+C;AAC/C,gEAAgE;;;AAEhE,yCAMwB;AACxB,kCAIiB;AAGjB,MAAM,iCAAkC,SAAQ,4BAE/C;CAAG;AAEJ;;;;;;GAMG;AACH,SAAgB,YAAY,CAC1B,sBAAuD,EACvD,OAAgB;IAEhB,OAAO,SAAS,qCAAqC;IACnD,uBAAuB;IACvB,8DAA8D;IAC9D,MAAW,EACX,MAAe;IACf,6CAA6C;IAC7C,4DAA4D;IAC5D,8DAA8D;IAC9D,gBAA+C;QAE/C,IAAI,IAA4B,CAAC;QACjC,IAAI,OAAO,sBAAsB,KAAK,QAAQ,EAAE;YAC9C,IAAI,GAAG,sBAAsB,CAAC;SAC/B;aAAM;YACL,IAAI,GAAG,EAAC,QAAQ,EAAE,sBAAsB,EAAE,OAAO,EAAE,OAAO,aAAP,OAAO,cAAP,OAAO,GAAI,EAAE,EAAC,CAAC;SACnE;QACD,IAAI,MAAM,IAAI,gBAAgB,EAAE;YAC9B,SAAS;YACT,OAAO,6BAAsB,CAAC,eAAe,CAC3C,kCAA2B,EAC3B,IAAI,EACJ,EAAC,aAAa,EAAE,eAAe,EAAC,CACjC,CAAC,MAAM,EAAE,MAAM,EAAE,gBAAgB,CAAC,CAAC;SACrC;QACD,IAAI,OAAO,MAAM,KAAK,UAAU,IAAI,CAAC,MAAM,IAAI,CAAC,gBAAgB,EAAE;YAChE,QAAQ;YACR,OAAO,iCAAiC,CAAC,eAAe,CACtD,wCAAiC,EACjC,IAAI,EACJ,EAAC,aAAa,EAAE,eAAe,EAAC,CACjC,CAAC,MAAM,CAAC,CAAC;SACX;QACD,2BAA2B;QAC3B,MAAM,IAAI,KAAK,CACb,2CAA2C;YACzC,uBAAgB,CAAC,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,gBAAgB,CAAC,CACnE,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC;AA1CD,oCA0CC;AAED,WAAiB,YAAY;IAC3B;;OAEG;IACU,iBAAI,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,EAAC,QAAQ,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAC,CAAC,CAAC;AACrE,CAAC,EALgB,YAAY,GAAZ,oBAAY,KAAZ,oBAAY,QAK5B;AAED;;;;;GAKG;AACH,SAAgB,uBAAuB,CACrC,WAA4B,EAC5B,UAAkB;IAElB,2BAA2B;IAC3B,IAAI,QAAQ,GAAG,wBAAiB,CAAC,iBAAiB,CAChD,yCAAkC,EAClC,WAAW,CAAC,SAAS,EACrB,UAAU,CACX,CAAC;IACF,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC9B,+CAA+C;IAC/C,QAAQ,GAAG,wBAAiB,CAAC,gBAAgB,CAC3C,wCAAiC,EACjC,WAAW,CACZ,CAAC;IACF,OAAO,QAAQ,CAAC;AAClB,CAAC;AAjBD,0DAiBC"}
1
+ {"version":3,"file":"authenticate.decorator.js","sourceRoot":"","sources":["../../src/decorators/authenticate.decorator.ts"],"names":[],"mappings":";AAAA,sDAAsD;AACtD,wCAAwC;AACxC,+CAA+C;AAC/C,gEAAgE;;;AAEhE,yCAMwB;AACxB,kCAIiB;AAGjB,MAAM,iCAAkC,SAAQ,4BAE/C;CAAG;AAEJ;;;;;GAKG;AACH,SAAgB,YAAY,CAC1B,GAAG,UAA+C;IAElD,OAAO,SAAS,qCAAqC;IACnD,uBAAuB;IACvB,8DAA8D;IAC9D,MAAW,EACX,MAAe;IACf,6CAA6C;IAC7C,4DAA4D;IAC5D,8DAA8D;IAC9D,gBAA+C;QAE/C,MAAM,KAAK,GAA6B,EAAE,CAAC;QAE3C,KAAK,MAAM,QAAQ,IAAI,UAAU,EAAE;YACjC,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE;gBAChC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;aACtB;iBAAM;gBACL,KAAK,CAAC,IAAI,CAAC,EAAC,QAAQ,EAAE,QAAQ,EAAC,CAAC,CAAC;aAClC;SACF;QAED,IAAI,MAAM,IAAI,gBAAgB,EAAE;YAC9B,SAAS;YACT,OAAO,6BAAsB,CAAC,eAAe,CAC3C,kCAA2B,EAC3B,KAAK,EACL,EAAC,aAAa,EAAE,eAAe,EAAC,CACjC,CAAC,MAAM,EAAE,MAAM,EAAE,gBAAgB,CAAC,CAAC;SACrC;QACD,IAAI,OAAO,MAAM,KAAK,UAAU,IAAI,CAAC,MAAM,IAAI,CAAC,gBAAgB,EAAE;YAChE,QAAQ;YACR,OAAO,iCAAiC,CAAC,eAAe,CAEtD,wCAAiC,EAAE,KAAK,EAAE;gBAC1C,aAAa,EAAE,eAAe;aAC/B,CAAC,CAAC,MAAM,CAAC,CAAC;SACZ;QACD,2BAA2B;QAC3B,MAAM,IAAI,KAAK,CACb,8CAA8C;YAC5C,uBAAgB,CAAC,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,gBAAgB,CAAC,CACnE,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC;AA7CD,oCA6CC;AAED,WAAiB,YAAY;IAC3B;;OAEG;IACU,iBAAI,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,EAAC,QAAQ,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAC,CAAC,CAAC;AACrE,CAAC,EALgB,YAAY,GAAZ,oBAAY,KAAZ,oBAAY,QAK5B;AAED;;;;;GAKG;AACH,SAAgB,uBAAuB,CACrC,WAA4B,EAC5B,UAAkB;IAElB,2BAA2B;IAC3B,IAAI,QAAQ,GAAG,wBAAiB,CAAC,iBAAiB,CAChD,yCAAkC,EAClC,WAAW,CAAC,SAAS,EACrB,UAAU,CACX,CAAC;IACF,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC9B,+CAA+C;IAC/C,QAAQ,GAAG,wBAAiB,CAAC,gBAAgB,CAC3C,wCAAiC,EACjC,WAAW,CACZ,CAAC;IACF,OAAO,QAAQ,CAAC;AAClB,CAAC;AAjBD,0DAiBC"}
package/dist/keys.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { BindingKey, MetadataAccessor } from '@loopback/core';
2
2
  import { UserProfile } from '@loopback/security';
3
+ import { Middleware } from '@loopback/rest';
3
4
  import { AuthenticationComponent } from './authentication.component';
4
5
  import { AuthenticateFn, AuthenticationMetadata, AuthenticationStrategy, UserProfileFactory } from './types';
5
6
  /**
@@ -21,8 +22,8 @@ export declare namespace AuthenticationBindings {
21
22
  */
22
23
  const USER_PROFILE_FACTORY: BindingKey<UserProfileFactory<any>>;
23
24
  /**
24
- * Key used to bind an authentication strategy to the context for the
25
- * authentication function to use.
25
+ * Key used to bind an authentication strategy or multiple strategies
26
+ * to the context for the authentication function to use.
26
27
  *
27
28
  * @example
28
29
  * ```ts
@@ -31,7 +32,7 @@ export declare namespace AuthenticationBindings {
31
32
  * .toProvider(MyAuthenticationStrategy);
32
33
  * ```
33
34
  */
34
- const STRATEGY: BindingKey<AuthenticationStrategy | undefined>;
35
+ const STRATEGY: BindingKey<AuthenticationStrategy | AuthenticationStrategy[] | undefined>;
35
36
  /**
36
37
  * Key used to inject the authentication function into the sequence.
37
38
  *
@@ -64,6 +65,10 @@ export declare namespace AuthenticationBindings {
64
65
  * ```
65
66
  */
66
67
  const AUTH_ACTION: BindingKey<AuthenticateFn>;
68
+ /**
69
+ * Binding key for AUTHENTICATION_MIDDLEWARE
70
+ */
71
+ const AUTHENTICATION_MIDDLEWARE: BindingKey<Middleware>;
67
72
  /**
68
73
  * Key used to inject authentication metadata, which is used to determine
69
74
  * whether a request requires authentication or not.
@@ -73,18 +78,17 @@ export declare namespace AuthenticationBindings {
73
78
  * class MyPassportStrategyProvider implements Provider<Strategy | undefined> {
74
79
  * constructor(
75
80
  * @inject(AuthenticationBindings.METADATA)
76
- * private metadata: AuthenticationMetadata,
81
+ * private metadata?: AuthenticationMetadata[],
77
82
  * ) {}
78
83
  * value(): ValueOrPromise<Strategy | undefined> {
79
- * if (this.metadata) {
80
- * const name = this.metadata.strategy;
84
+ * if (this.metadata?.length) {
81
85
  * // logic to determine which authentication strategy to return
82
86
  * }
83
87
  * }
84
88
  * }
85
89
  * ```
86
90
  */
87
- const METADATA: BindingKey<AuthenticationMetadata | undefined>;
91
+ const METADATA: BindingKey<AuthenticationMetadata[] | undefined>;
88
92
  const AUTHENTICATION_STRATEGY_EXTENSION_POINT_NAME = "authentication.strategies";
89
93
  const CURRENT_USER: BindingKey<UserProfile>;
90
94
  const AUTHENTICATION_REDIRECT_URL: BindingKey<string>;
package/dist/keys.js CHANGED
@@ -28,8 +28,8 @@ var AuthenticationBindings;
28
28
  /* eslint-disable @typescript-eslint/no-explicit-any */
29
29
  AuthenticationBindings.USER_PROFILE_FACTORY = core_1.BindingKey.create('authentication.userProfileFactory');
30
30
  /**
31
- * Key used to bind an authentication strategy to the context for the
32
- * authentication function to use.
31
+ * Key used to bind an authentication strategy or multiple strategies
32
+ * to the context for the authentication function to use.
33
33
  *
34
34
  * @example
35
35
  * ```ts
@@ -71,6 +71,10 @@ var AuthenticationBindings;
71
71
  * ```
72
72
  */
73
73
  AuthenticationBindings.AUTH_ACTION = core_1.BindingKey.create('authentication.actions.authenticate');
74
+ /**
75
+ * Binding key for AUTHENTICATION_MIDDLEWARE
76
+ */
77
+ AuthenticationBindings.AUTHENTICATION_MIDDLEWARE = core_1.BindingKey.create('middleware.authentication');
74
78
  /**
75
79
  * Key used to inject authentication metadata, which is used to determine
76
80
  * whether a request requires authentication or not.
@@ -80,11 +84,10 @@ var AuthenticationBindings;
80
84
  * class MyPassportStrategyProvider implements Provider<Strategy | undefined> {
81
85
  * constructor(
82
86
  * @inject(AuthenticationBindings.METADATA)
83
- * private metadata: AuthenticationMetadata,
87
+ * private metadata?: AuthenticationMetadata[],
84
88
  * ) {}
85
89
  * value(): ValueOrPromise<Strategy | undefined> {
86
- * if (this.metadata) {
87
- * const name = this.metadata.strategy;
90
+ * if (this.metadata?.length) {
88
91
  * // logic to determine which authentication strategy to return
89
92
  * }
90
93
  * }
package/dist/keys.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"keys.js","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":";AAAA,sDAAsD;AACtD,wCAAwC;AACxC,+CAA+C;AAC/C,gEAAgE;;;AAEhE,yCAA4D;AAC5D,iDAAiE;AASjE;;GAEG;AACH,IAAiB,sBAAsB,CA+GtC;AA/GD,WAAiB,sBAAsB;IACxB,gCAAS,GAAG,iBAAU,CAAC,MAAM,CACxC,oCAAoC,CACrC,CAAC;IAEF;;;;;;;;;;;OAWG;IACH,uDAAuD;IAC1C,2CAAoB,GAAG,iBAAU,CAAC,MAAM,CAEnD,mCAAmC,CAAC,CAAC;IAEvC;;;;;;;;;;OAUG;IACU,+BAAQ,GAAG,iBAAU,CAAC,MAAM,CACvC,yBAAyB,CAC1B,CAAC;IAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8BG;IACU,kCAAW,GAAG,iBAAU,CAAC,MAAM,CAC1C,qCAAqC,CACtC,CAAC;IAEF;;;;;;;;;;;;;;;;;;;OAmBG;IACU,+BAAQ,GAAG,iBAAU,CAAC,MAAM,CACvC,kCAAkC,CACnC,CAAC;IAEW,mEAA4C,GACvD,2BAA2B,CAAC;IAE9B,oFAAoF;IACvE,mCAAY,GAA4B,2BAAgB,CAAC,IAAI,CAAC;IAE3E,+CAA+C;IAClC,kDAA2B,GAAG,iBAAU,CAAC,MAAM,CAC1D,6BAA6B,CAC9B,CAAC;IAEF,2FAA2F;IAC9E,qDAA8B,GAAG,iBAAU,CAAC,MAAM,CAC7D,gCAAgC,CACjC,CAAC;AACJ,CAAC,EA/GgB,sBAAsB,GAAtB,8BAAsB,KAAtB,8BAAsB,QA+GtC;AAED;;GAEG;AACU,QAAA,kCAAkC,GAAG,uBAAgB,CAAC,MAAM,CAGvE,uBAAuB,CAAC,CAAC;AAE3B;;GAEG;AACU,QAAA,2BAA2B,GAAG,0CAAkC,CAAC;AAE9E;;GAEG;AACU,QAAA,iCAAiC,GAAG,uBAAgB,CAAC,MAAM,CAGtE,sBAAsB,CAAC,CAAC"}
1
+ {"version":3,"file":"keys.js","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":";AAAA,sDAAsD;AACtD,wCAAwC;AACxC,+CAA+C;AAC/C,gEAAgE;;;AAEhE,yCAA4D;AAC5D,iDAAiE;AAUjE;;GAEG;AACH,IAAiB,sBAAsB,CAqHtC;AArHD,WAAiB,sBAAsB;IACxB,gCAAS,GAAG,iBAAU,CAAC,MAAM,CACxC,oCAAoC,CACrC,CAAC;IAEF;;;;;;;;;;;OAWG;IACH,uDAAuD;IAC1C,2CAAoB,GAAG,iBAAU,CAAC,MAAM,CAEnD,mCAAmC,CAAC,CAAC;IAEvC;;;;;;;;;;OAUG;IACU,+BAAQ,GAAG,iBAAU,CAAC,MAAM,CAEvC,yBAAyB,CAAC,CAAC;IAE7B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8BG;IACU,kCAAW,GAAG,iBAAU,CAAC,MAAM,CAC1C,qCAAqC,CACtC,CAAC;IAEF;;OAEG;IACU,gDAAyB,GAAG,iBAAU,CAAC,MAAM,CACxD,2BAA2B,CAC5B,CAAC;IAEF;;;;;;;;;;;;;;;;;;OAkBG;IACU,+BAAQ,GAAG,iBAAU,CAAC,MAAM,CAEvC,kCAAkC,CAAC,CAAC;IAEzB,mEAA4C,GACvD,2BAA2B,CAAC;IAE9B,oFAAoF;IACvE,mCAAY,GAA4B,2BAAgB,CAAC,IAAI,CAAC;IAE3E,+CAA+C;IAClC,kDAA2B,GAAG,iBAAU,CAAC,MAAM,CAC1D,6BAA6B,CAC9B,CAAC;IAEF,2FAA2F;IAC9E,qDAA8B,GAAG,iBAAU,CAAC,MAAM,CAC7D,gCAAgC,CACjC,CAAC;AACJ,CAAC,EArHgB,sBAAsB,GAAtB,8BAAsB,KAAtB,8BAAsB,QAqHtC;AAED;;GAEG;AACU,QAAA,kCAAkC,GAAG,uBAAgB,CAAC,MAAM,CAGvE,uBAAuB,CAAC,CAAC;AAE3B;;GAEG;AACU,QAAA,2BAA2B,GAAG,0CAAkC,CAAC;AAE9E;;GAEG;AACU,QAAA,iCAAiC,GAAG,uBAAgB,CAAC,MAAM,CAGtE,sBAAsB,CAAC,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import { Getter, Provider, Setter } from '@loopback/core';
2
- import { Request } from '@loopback/rest';
2
+ import { Middleware, Request } from '@loopback/rest';
3
3
  import { UserProfile } from '@loopback/security';
4
4
  import { AuthenticateFn, AuthenticationStrategy } from '../types';
5
5
  /**
@@ -7,11 +7,11 @@ import { AuthenticateFn, AuthenticationStrategy } from '../types';
7
7
  * @example `context.bind('authentication.actions.authenticate').toProvider(AuthenticateActionProvider)`
8
8
  */
9
9
  export declare class AuthenticateActionProvider implements Provider<AuthenticateFn> {
10
- readonly getStrategy: Getter<AuthenticationStrategy>;
10
+ readonly getStrategies: Getter<AuthenticationStrategy | AuthenticationStrategy[] | undefined>;
11
11
  readonly setCurrentUser: Setter<UserProfile>;
12
12
  readonly setRedirectUrl: Setter<string>;
13
13
  readonly setRedirectStatus: Setter<number>;
14
- constructor(getStrategy: Getter<AuthenticationStrategy>, setCurrentUser: Setter<UserProfile>, setRedirectUrl: Setter<string>, setRedirectStatus: Setter<number>);
14
+ constructor(getStrategies: Getter<AuthenticationStrategy | AuthenticationStrategy[] | undefined>, setCurrentUser: Setter<UserProfile>, setRedirectUrl: Setter<string>, setRedirectStatus: Setter<number>);
15
15
  /**
16
16
  * @returns authenticateFn
17
17
  */
@@ -22,3 +22,8 @@ export declare class AuthenticateActionProvider implements Provider<Authenticate
22
22
  */
23
23
  action(request: Request): Promise<UserProfile | undefined>;
24
24
  }
25
+ export declare class AuthenticationMiddlewareProvider implements Provider<Middleware> {
26
+ private authenticate;
27
+ constructor(authenticate: AuthenticateFn);
28
+ value(): Middleware;
29
+ }
@@ -4,7 +4,7 @@
4
4
  // This file is licensed under the MIT License.
5
5
  // License text available at https://opensource.org/licenses/MIT
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.AuthenticateActionProvider = void 0;
7
+ exports.AuthenticationMiddlewareProvider = exports.AuthenticateActionProvider = void 0;
8
8
  const tslib_1 = require("tslib");
9
9
  const core_1 = require("@loopback/core");
10
10
  const rest_1 = require("@loopback/rest");
@@ -24,8 +24,8 @@ let AuthenticateActionProvider = class AuthenticateActionProvider {
24
24
  // To solve this, we are injecting a getter function that will
25
25
  // defer resolution of the strategy until authenticate() action
26
26
  // is executed.
27
- getStrategy, setCurrentUser, setRedirectUrl, setRedirectStatus) {
28
- this.getStrategy = getStrategy;
27
+ getStrategies, setCurrentUser, setRedirectUrl, setRedirectStatus) {
28
+ this.getStrategies = getStrategies;
29
29
  this.setCurrentUser = setCurrentUser;
30
30
  this.setRedirectUrl = setRedirectUrl;
31
31
  this.setRedirectStatus = setRedirectStatus;
@@ -41,35 +41,57 @@ let AuthenticateActionProvider = class AuthenticateActionProvider {
41
41
  * @param request - The incoming request provided by the REST layer
42
42
  */
43
43
  async action(request) {
44
- const strategy = await this.getStrategy();
45
- if (!strategy) {
44
+ let strategies = await this.getStrategies();
45
+ if (!strategies) {
46
46
  // The invoked operation does not require authentication.
47
47
  return undefined;
48
48
  }
49
- const authResponse = await strategy.authenticate(request);
49
+ // convert to array if required
50
+ strategies = Array.isArray(strategies) ? strategies : [strategies];
51
+ let authenticated = false;
52
+ let redirected = false;
53
+ let authError;
54
+ let authResponse;
50
55
  let userProfile;
51
- // response from `strategy.authenticate()` could return an object of type UserProfile or RedirectRoute
52
- if (rest_1.RedirectRoute.isRedirectRoute(authResponse)) {
53
- const redirectOptions = authResponse;
54
- // bind redirection url and status to the context
55
- // controller should handle actual redirection
56
- this.setRedirectUrl(redirectOptions.targetLocation);
57
- this.setRedirectStatus(redirectOptions.statusCode);
56
+ for (const strategy of strategies) {
57
+ // the first strategy to succeed or redirect will halt the execution chain
58
+ if (authenticated || redirected)
59
+ break;
60
+ try {
61
+ authResponse = await strategy.authenticate(request);
62
+ // response from `strategy.authenticate()` could return an object of type UserProfile or RedirectRoute
63
+ if (rest_1.RedirectRoute.isRedirectRoute(authResponse)) {
64
+ redirected = true;
65
+ const redirectOptions = authResponse;
66
+ // bind redirection url and status to the context
67
+ // controller should handle actual redirection
68
+ this.setRedirectUrl(redirectOptions.targetLocation);
69
+ this.setRedirectStatus(redirectOptions.statusCode);
70
+ }
71
+ else if (authResponse) {
72
+ authenticated = true;
73
+ // if `strategy.authenticate()` returns an object of type UserProfile, set it as current user
74
+ userProfile = authResponse;
75
+ }
76
+ else if (!authResponse) {
77
+ // important to throw a non-protocol-specific error here
78
+ const error = new Error(`User profile not returned from strategy's authenticate function`);
79
+ Object.assign(error, {
80
+ code: types_1.USER_PROFILE_NOT_FOUND,
81
+ });
82
+ throw error;
83
+ }
84
+ }
85
+ catch (error) {
86
+ authError = authError !== null && authError !== void 0 ? authError : error;
87
+ }
58
88
  }
59
- else if (authResponse) {
60
- // if `strategy.authenticate()` returns an object of type UserProfile, set it as current user
61
- userProfile = authResponse;
89
+ if (!authenticated && !redirected)
90
+ throw authError;
91
+ if (userProfile) {
62
92
  this.setCurrentUser(userProfile);
63
93
  return userProfile;
64
94
  }
65
- else if (!authResponse) {
66
- // important to throw a non-protocol-specific error here
67
- const error = new Error(`User profile not returned from strategy's authenticate function`);
68
- Object.assign(error, {
69
- code: types_1.USER_PROFILE_NOT_FOUND,
70
- });
71
- throw error;
72
- }
73
95
  }
74
96
  };
75
97
  AuthenticateActionProvider = tslib_1.__decorate([
@@ -80,4 +102,33 @@ AuthenticateActionProvider = tslib_1.__decorate([
80
102
  tslib_1.__metadata("design:paramtypes", [Function, Function, Function, Function])
81
103
  ], AuthenticateActionProvider);
82
104
  exports.AuthenticateActionProvider = AuthenticateActionProvider;
105
+ let AuthenticationMiddlewareProvider = class AuthenticationMiddlewareProvider {
106
+ constructor(authenticate) {
107
+ this.authenticate = authenticate;
108
+ }
109
+ value() {
110
+ return async (ctx, next) => {
111
+ try {
112
+ await this.authenticate(ctx.request);
113
+ }
114
+ catch (error) {
115
+ if (error.code === types_1.AUTHENTICATION_STRATEGY_NOT_FOUND ||
116
+ error.code === types_1.USER_PROFILE_NOT_FOUND) {
117
+ error.statusCode = 401;
118
+ }
119
+ throw error;
120
+ }
121
+ return next();
122
+ };
123
+ }
124
+ };
125
+ AuthenticationMiddlewareProvider = tslib_1.__decorate([
126
+ core_1.bind(rest_1.asMiddleware({
127
+ group: rest_1.RestMiddlewareGroups.AUTHENTICATION,
128
+ upstreamGroups: [rest_1.RestMiddlewareGroups.FIND_ROUTE],
129
+ })),
130
+ tslib_1.__param(0, core_1.inject(keys_1.AuthenticationBindings.AUTH_ACTION)),
131
+ tslib_1.__metadata("design:paramtypes", [Function])
132
+ ], AuthenticationMiddlewareProvider);
133
+ exports.AuthenticationMiddlewareProvider = AuthenticationMiddlewareProvider;
83
134
  //# sourceMappingURL=auth-action.provider.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"auth-action.provider.js","sourceRoot":"","sources":["../../src/providers/auth-action.provider.ts"],"names":[],"mappings":";AAAA,sDAAsD;AACtD,wCAAwC;AACxC,+CAA+C;AAC/C,gEAAgE;;;;AAEhE,yCAAgE;AAChE,yCAAsD;AACtD,iDAAiE;AACjE,kCAA+C;AAC/C,oCAIkB;AAClB;;;GAGG;AACH,IAAa,0BAA0B,GAAvC,MAAa,0BAA0B;IACrC;IACE,yDAAyD;IACzD,4DAA4D;IAC5D,qDAAqD;IACrD,qCAAqC;IACrC,8DAA8D;IAC9D,+DAA+D;IAC/D,eAAe;IAEN,WAA2C,EAE3C,cAAmC,EAEnC,cAA8B,EAE9B,iBAAiC;QANjC,gBAAW,GAAX,WAAW,CAAgC;QAE3C,mBAAc,GAAd,cAAc,CAAqB;QAEnC,mBAAc,GAAd,cAAc,CAAgB;QAE9B,sBAAiB,GAAjB,iBAAiB,CAAgB;IACzC,CAAC;IAEJ;;OAEG;IACH,KAAK;QACH,OAAO,OAAO,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACzC,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,MAAM,CAAC,OAAgB;QAC3B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;QAC1C,IAAI,CAAC,QAAQ,EAAE;YACb,yDAAyD;YACzD,OAAO,SAAS,CAAC;SAClB;QAED,MAAM,YAAY,GAAG,MAAM,QAAQ,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;QAC1D,IAAI,WAAwB,CAAC;QAE7B,sGAAsG;QACtG,IAAI,oBAAa,CAAC,eAAe,CAAC,YAAY,CAAC,EAAE;YAC/C,MAAM,eAAe,GAAG,YAAY,CAAC;YACrC,iDAAiD;YACjD,8CAA8C;YAC9C,IAAI,CAAC,cAAc,CAAC,eAAe,CAAC,cAAc,CAAC,CAAC;YACpD,IAAI,CAAC,iBAAiB,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC;SACpD;aAAM,IAAI,YAAY,EAAE;YACvB,6FAA6F;YAC7F,WAAW,GAAG,YAA2B,CAAC;YAC1C,IAAI,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC;YACjC,OAAO,WAAW,CAAC;SACpB;aAAM,IAAI,CAAC,YAAY,EAAE;YACxB,wDAAwD;YACxD,MAAM,KAAK,GAAG,IAAI,KAAK,CACrB,iEAAiE,CAClE,CAAC;YACF,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE;gBACnB,IAAI,EAAE,8BAAsB;aAC7B,CAAC,CAAC;YACH,MAAM,KAAK,CAAC;SACb;IACH,CAAC;CACF,CAAA;AA/DY,0BAA0B;IASlC,mBAAA,aAAM,CAAC,MAAM,CAAC,6BAAsB,CAAC,QAAQ,CAAC,CAAA;IAE9C,mBAAA,aAAM,CAAC,MAAM,CAAC,2BAAgB,CAAC,IAAI,CAAC,CAAA;IAEpC,mBAAA,aAAM,CAAC,MAAM,CAAC,6BAAsB,CAAC,2BAA2B,CAAC,CAAA;IAEjE,mBAAA,aAAM,CAAC,MAAM,CAAC,6BAAsB,CAAC,8BAA8B,CAAC,CAAA;;GAf5D,0BAA0B,CA+DtC;AA/DY,gEAA0B"}
1
+ {"version":3,"file":"auth-action.provider.js","sourceRoot":"","sources":["../../src/providers/auth-action.provider.ts"],"names":[],"mappings":"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"}
@@ -4,7 +4,7 @@ import { AuthenticationMetadata, AuthenticationOptions } from '../types';
4
4
  * Provides authentication metadata of a controller method
5
5
  * @example `context.bind('authentication.operationMetadata').toProvider(AuthMetadataProvider)`
6
6
  */
7
- export declare class AuthMetadataProvider implements Provider<AuthenticationMetadata | undefined> {
7
+ export declare class AuthMetadataProvider implements Provider<AuthenticationMetadata[] | undefined> {
8
8
  private readonly controllerClass;
9
9
  private readonly methodName;
10
10
  private readonly options;
@@ -12,5 +12,5 @@ export declare class AuthMetadataProvider implements Provider<AuthenticationMeta
12
12
  /**
13
13
  * @returns AuthenticationMetadata
14
14
  */
15
- value(): AuthenticationMetadata | undefined;
15
+ value(): AuthenticationMetadata[] | undefined;
16
16
  }
@@ -23,11 +23,12 @@ let AuthMetadataProvider = class AuthMetadataProvider {
23
23
  * @returns AuthenticationMetadata
24
24
  */
25
25
  value() {
26
+ var _a;
26
27
  if (!this.controllerClass || !this.methodName)
27
28
  return;
28
29
  const metadata = decorators_1.getAuthenticateMetadata(this.controllerClass, this.methodName);
29
30
  // Skip authentication if `skip` is `true`
30
- if (metadata === null || metadata === void 0 ? void 0 : metadata.skip)
31
+ if ((_a = metadata === null || metadata === void 0 ? void 0 : metadata[0]) === null || _a === void 0 ? void 0 : _a.skip)
31
32
  return undefined;
32
33
  if (metadata)
33
34
  return metadata;
@@ -1 +1 @@
1
- {"version":3,"file":"auth-metadata.provider.js","sourceRoot":"","sources":["../../src/providers/auth-metadata.provider.ts"],"names":[],"mappings":";AAAA,sDAAsD;AACtD,wCAAwC;AACxC,+CAA+C;AAC/C,gEAAgE;;;;AAEhE,yCAMwB;AACxB,8CAAsD;AACtD,kCAA+C;AAG/C;;;GAGG;AACH,IAAa,oBAAoB,GAAjC,MAAa,oBAAoB;IAE/B,YAEmB,eAAgC,EAEhC,UAAkB,EAElB,UAAiC,EAAE;QAJnC,oBAAe,GAAf,eAAe,CAAiB;QAEhC,eAAU,GAAV,UAAU,CAAQ;QAElB,YAAO,GAAP,OAAO,CAA4B;IACnD,CAAC;IAEJ;;OAEG;IACH,KAAK;QACH,IAAI,CAAC,IAAI,CAAC,eAAe,IAAI,CAAC,IAAI,CAAC,UAAU;YAAE,OAAO;QACtD,MAAM,QAAQ,GAAG,oCAAuB,CACtC,IAAI,CAAC,eAAe,EACpB,IAAI,CAAC,UAAU,CAChB,CAAC;QACF,0CAA0C;QAC1C,IAAI,QAAQ,aAAR,QAAQ,uBAAR,QAAQ,CAAE,IAAI;YAAE,OAAO,SAAS,CAAC;QACrC,IAAI,QAAQ;YAAE,OAAO,QAAQ,CAAC;QAC9B,gCAAgC;QAChC,OAAO,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC;IACtC,CAAC;CACF,CAAA;AA1BY,oBAAoB;IAG5B,mBAAA,aAAM,CAAC,mBAAY,CAAC,gBAAgB,EAAE,EAAC,QAAQ,EAAE,IAAI,EAAC,CAAC,CAAA;IAEvD,mBAAA,aAAM,CAAC,mBAAY,CAAC,sBAAsB,EAAE,EAAC,QAAQ,EAAE,IAAI,EAAC,CAAC,CAAA;IAE7D,mBAAA,aAAM,CAAC,EAAC,WAAW,EAAE,6BAAsB,CAAC,SAAS,EAAC,CAAC,CAAA;;GAP/C,oBAAoB,CA0BhC;AA1BY,oDAAoB"}
1
+ {"version":3,"file":"auth-metadata.provider.js","sourceRoot":"","sources":["../../src/providers/auth-metadata.provider.ts"],"names":[],"mappings":";AAAA,sDAAsD;AACtD,wCAAwC;AACxC,+CAA+C;AAC/C,gEAAgE;;;;AAEhE,yCAMwB;AACxB,8CAAsD;AACtD,kCAA+C;AAG/C;;;GAGG;AACH,IAAa,oBAAoB,GAAjC,MAAa,oBAAoB;IAE/B,YAEmB,eAAgC,EAEhC,UAAkB,EAElB,UAAiC,EAAE;QAJnC,oBAAe,GAAf,eAAe,CAAiB;QAEhC,eAAU,GAAV,UAAU,CAAQ;QAElB,YAAO,GAAP,OAAO,CAA4B;IACnD,CAAC;IAEJ;;OAEG;IACH,KAAK;;QACH,IAAI,CAAC,IAAI,CAAC,eAAe,IAAI,CAAC,IAAI,CAAC,UAAU;YAAE,OAAO;QACtD,MAAM,QAAQ,GAAG,oCAAuB,CACtC,IAAI,CAAC,eAAe,EACpB,IAAI,CAAC,UAAU,CAChB,CAAC;QACF,0CAA0C;QAC1C,UAAI,QAAQ,aAAR,QAAQ,uBAAR,QAAQ,CAAG,CAAC,2CAAG,IAAI;YAAE,OAAO,SAAS,CAAC;QAC1C,IAAI,QAAQ;YAAE,OAAO,QAAQ,CAAC;QAC9B,gCAAgC;QAChC,OAAO,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC;IACtC,CAAC;CACF,CAAA;AA1BY,oBAAoB;IAG5B,mBAAA,aAAM,CAAC,mBAAY,CAAC,gBAAgB,EAAE,EAAC,QAAQ,EAAE,IAAI,EAAC,CAAC,CAAA;IAEvD,mBAAA,aAAM,CAAC,mBAAY,CAAC,sBAAsB,EAAE,EAAC,QAAQ,EAAE,IAAI,EAAC,CAAC,CAAA;IAE7D,mBAAA,aAAM,CAAC,EAAC,WAAW,EAAE,6BAAsB,CAAC,SAAS,EAAC,CAAC,CAAA;;GAP/C,oBAAoB,CA0BhC;AA1BY,oDAAoB"}
@@ -9,10 +9,10 @@ import { AuthenticationMetadata, AuthenticationStrategy } from '../types';
9
9
  *
10
10
  * @example `context.bind('authentication.strategy').toProvider(AuthenticationStrategyProvider)`
11
11
  */
12
- export declare class AuthenticationStrategyProvider implements Provider<AuthenticationStrategy | undefined> {
12
+ export declare class AuthenticationStrategyProvider implements Provider<AuthenticationStrategy[] | undefined> {
13
13
  protected authenticationStrategies: Getter<AuthenticationStrategy[]>;
14
- protected metadata?: AuthenticationMetadata | undefined;
15
- constructor(authenticationStrategies: Getter<AuthenticationStrategy[]>, metadata?: AuthenticationMetadata | undefined);
16
- value(): Promise<AuthenticationStrategy | undefined>;
17
- findAuthenticationStrategy(name: string): Promise<AuthenticationStrategy | undefined>;
14
+ protected metadata?: AuthenticationMetadata[] | undefined;
15
+ constructor(authenticationStrategies: Getter<AuthenticationStrategy[]>, metadata?: AuthenticationMetadata[] | undefined);
16
+ value(): Promise<AuthenticationStrategy[] | undefined>;
17
+ private findAuthenticationStrategies;
18
18
  }
@@ -24,25 +24,31 @@ let AuthenticationStrategyProvider = class AuthenticationStrategyProvider {
24
24
  this.metadata = metadata;
25
25
  }
26
26
  async value() {
27
- if (!this.metadata) {
27
+ var _a;
28
+ if (!((_a = this.metadata) === null || _a === void 0 ? void 0 : _a.length)) {
28
29
  return undefined;
29
30
  }
30
- const name = this.metadata.strategy;
31
- const strategy = await this.findAuthenticationStrategy(name);
32
- if (!strategy) {
33
- // important to throw a non-protocol-specific error here
34
- const error = new Error(`The strategy '${name}' is not available.`);
35
- Object.assign(error, {
36
- code: types_1.AUTHENTICATION_STRATEGY_NOT_FOUND,
37
- });
38
- throw error;
39
- }
40
- return strategy;
31
+ return this.findAuthenticationStrategies(this.metadata);
41
32
  }
42
- async findAuthenticationStrategy(name) {
43
- const strategies = await this.authenticationStrategies();
44
- const matchingAuthStrategy = strategies.find(a => a.name === name);
45
- return matchingAuthStrategy;
33
+ async findAuthenticationStrategies(metadata) {
34
+ const strategies = [];
35
+ const existingStrategies = await this.authenticationStrategies();
36
+ const findStrategy = (name) => {
37
+ const strategy = existingStrategies.find(a => a.name === name);
38
+ if (!strategy) {
39
+ const error = new Error(`The strategy '${name}' is not available.`);
40
+ Object.assign(error, {
41
+ code: types_1.AUTHENTICATION_STRATEGY_NOT_FOUND,
42
+ });
43
+ throw error;
44
+ }
45
+ return strategy;
46
+ };
47
+ for (const data of metadata) {
48
+ const strategy = findStrategy(data.strategy);
49
+ strategies.push(strategy);
50
+ }
51
+ return strategies;
46
52
  }
47
53
  };
48
54
  AuthenticationStrategyProvider = tslib_1.__decorate([
@@ -50,7 +56,7 @@ AuthenticationStrategyProvider = tslib_1.__decorate([
50
56
  ,
51
57
  tslib_1.__param(0, core_1.extensions()),
52
58
  tslib_1.__param(1, core_1.inject(keys_1.AuthenticationBindings.METADATA)),
53
- tslib_1.__metadata("design:paramtypes", [Function, Object])
59
+ tslib_1.__metadata("design:paramtypes", [Function, Array])
54
60
  ], AuthenticationStrategyProvider);
55
61
  exports.AuthenticationStrategyProvider = AuthenticationStrategyProvider;
56
62
  //# sourceMappingURL=auth-strategy.provider.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"auth-strategy.provider.js","sourceRoot":"","sources":["../../src/providers/auth-strategy.provider.ts"],"names":[],"mappings":";AAAA,iDAAiD;AACjD,wCAAwC;AACxC,+CAA+C;AAC/C,gEAAgE;;;;AAEhE,yCAOwB;AACxB,kCAA+C;AAC/C,oCAIkB;AAElB;;;;;;;;GAQG;AAKH,IAAa,8BAA8B,GAA3C,MAAa,8BAA8B;IAEzC,YAEY,wBAA0D,EAE1D,QAAiC;QAFjC,6BAAwB,GAAxB,wBAAwB,CAAkC;QAE1D,aAAQ,GAAR,QAAQ,CAAyB;IAC1C,CAAC;IACJ,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE;YAClB,OAAO,SAAS,CAAC;SAClB;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;QACpC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,0BAA0B,CAAC,IAAI,CAAC,CAAC;QAC7D,IAAI,CAAC,QAAQ,EAAE;YACb,wDAAwD;YACxD,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,iBAAiB,IAAI,qBAAqB,CAAC,CAAC;YACpE,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE;gBACnB,IAAI,EAAE,yCAAiC;aACxC,CAAC,CAAC;YACH,MAAM,KAAK,CAAC;SACb;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,KAAK,CAAC,0BAA0B,CAAC,IAAY;QAC3C,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,wBAAwB,EAAE,CAAC;QACzD,MAAM,oBAAoB,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;QACnE,OAAO,oBAAoB,CAAC;IAC9B,CAAC;CACF,CAAA;AA9BY,8BAA8B;IAJ1C,qBAAc,CACb,6BAAsB,CAAC,4CAA4C,EACnE,EAAC,KAAK,EAAE,mBAAY,CAAC,SAAS,EAAC,CAChC,CAAC,6DAA6D;;IAI1D,mBAAA,iBAAU,EAAE,CAAA;IAEZ,mBAAA,aAAM,CAAC,6BAAsB,CAAC,QAAQ,CAAC,CAAA;;GAL/B,8BAA8B,CA8B1C;AA9BY,wEAA8B"}
1
+ {"version":3,"file":"auth-strategy.provider.js","sourceRoot":"","sources":["../../src/providers/auth-strategy.provider.ts"],"names":[],"mappings":";AAAA,iDAAiD;AACjD,wCAAwC;AACxC,+CAA+C;AAC/C,gEAAgE;;;;AAEhE,yCAOwB;AACxB,kCAA+C;AAC/C,oCAIkB;AAElB;;;;;;;;GAQG;AAKH,IAAa,8BAA8B,GAA3C,MAAa,8BAA8B;IAEzC,YAEY,wBAA0D,EAE1D,QAAmC;QAFnC,6BAAwB,GAAxB,wBAAwB,CAAkC;QAE1D,aAAQ,GAAR,QAAQ,CAA2B;IAC5C,CAAC;IACJ,KAAK,CAAC,KAAK;;QACT,IAAI,QAAC,IAAI,CAAC,QAAQ,0CAAE,MAAM,CAAA,EAAE;YAC1B,OAAO,SAAS,CAAC;SAClB;QACD,OAAO,IAAI,CAAC,4BAA4B,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC1D,CAAC;IAEO,KAAK,CAAC,4BAA4B,CACxC,QAAkC;QAElC,MAAM,UAAU,GAA6B,EAAE,CAAC;QAEhD,MAAM,kBAAkB,GAAG,MAAM,IAAI,CAAC,wBAAwB,EAAE,CAAC;QAEjE,MAAM,YAAY,GAAG,CAAC,IAAY,EAAE,EAAE;YACpC,MAAM,QAAQ,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;YAC/D,IAAI,CAAC,QAAQ,EAAE;gBACb,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,iBAAiB,IAAI,qBAAqB,CAAC,CAAC;gBACpE,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE;oBACnB,IAAI,EAAE,yCAAiC;iBACxC,CAAC,CAAC;gBACH,MAAM,KAAK,CAAC;aACb;YACD,OAAO,QAAQ,CAAC;QAClB,CAAC,CAAC;QAEF,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE;YAC3B,MAAM,QAAQ,GAAG,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC7C,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;SAC3B;QAED,OAAO,UAAU,CAAC;IACpB,CAAC;CACF,CAAA;AAzCY,8BAA8B;IAJ1C,qBAAc,CACb,6BAAsB,CAAC,4CAA4C,EACnE,EAAC,KAAK,EAAE,mBAAY,CAAC,SAAS,EAAC,CAChC,CAAC,6DAA6D;;IAI1D,mBAAA,iBAAU,EAAE,CAAA;IAEZ,mBAAA,aAAM,CAAC,6BAAsB,CAAC,QAAQ,CAAC,CAAA;;GAL/B,8BAA8B,CAyC1C;AAzCY,wEAA8B"}
package/dist/types.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Binding, BindingTemplate, Constructor, Context } from '@loopback/core';
2
- import { Request, RedirectRoute } from '@loopback/rest';
2
+ import { RedirectRoute, Request } from '@loopback/rest';
3
3
  import { UserProfile } from '@loopback/security';
4
4
  /**
5
5
  * Authentication metadata stored via Reflection API
@@ -43,7 +43,7 @@ export interface AuthenticationOptions {
43
43
  * `@authenticate`. If not set, no default authentication will be enforced for
44
44
  * those methods without authentication metadata.
45
45
  */
46
- defaultMetadata?: AuthenticationMetadata;
46
+ defaultMetadata?: AuthenticationMetadata[];
47
47
  }
48
48
  /**
49
49
  * An interface that describes the common authentication strategy.
@@ -87,3 +87,10 @@ export declare function registerAuthenticationStrategy(context: Context, strateg
87
87
  * A binding template for auth strategy contributor extensions
88
88
  */
89
89
  export declare const asAuthStrategy: BindingTemplate;
90
+ /**
91
+ * Get the authentication metadata object for the specified strategy.
92
+ *
93
+ * @param metadata - Array of authentication metadata objects
94
+ * @param strategyName - Name of the authentication strategy
95
+ */
96
+ export declare function getAuthenticationMetadataForStrategy(metadata: AuthenticationMetadata[], strategyName: string): AuthenticationMetadata | undefined;
package/dist/types.js CHANGED
@@ -4,7 +4,7 @@
4
4
  // This file is licensed under the MIT License.
5
5
  // License text available at https://opensource.org/licenses/MIT
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.asAuthStrategy = exports.registerAuthenticationStrategy = exports.USER_PROFILE_NOT_FOUND = exports.AUTHENTICATION_STRATEGY_NOT_FOUND = void 0;
7
+ exports.getAuthenticationMetadataForStrategy = exports.asAuthStrategy = exports.registerAuthenticationStrategy = exports.USER_PROFILE_NOT_FOUND = exports.AUTHENTICATION_STRATEGY_NOT_FOUND = void 0;
8
8
  const core_1 = require("@loopback/core");
9
9
  const keys_1 = require("./keys");
10
10
  exports.AUTHENTICATION_STRATEGY_NOT_FOUND = 'AUTHENTICATION_STRATEGY_NOT_FOUND';
@@ -32,4 +32,14 @@ exports.asAuthStrategy = binding => {
32
32
  namespace: keys_1.AuthenticationBindings.AUTHENTICATION_STRATEGY_EXTENSION_POINT_NAME,
33
33
  });
34
34
  };
35
+ /**
36
+ * Get the authentication metadata object for the specified strategy.
37
+ *
38
+ * @param metadata - Array of authentication metadata objects
39
+ * @param strategyName - Name of the authentication strategy
40
+ */
41
+ function getAuthenticationMetadataForStrategy(metadata, strategyName) {
42
+ return metadata.find(data => data.strategy === strategyName);
43
+ }
44
+ exports.getAuthenticationMetadataForStrategy = getAuthenticationMetadataForStrategy;
35
45
  //# sourceMappingURL=types.js.map
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA,sDAAsD;AACtD,wCAAwC;AACxC,+CAA+C;AAC/C,gEAAgE;;;AAEhE,yCAOwB;AAGxB,iCAA8C;AAgFjC,QAAA,iCAAiC,GAC5C,mCAAmC,CAAC;AAEzB,QAAA,sBAAsB,GAAG,wBAAwB,CAAC;AAE/D;;;;;;;GAOG;AACH,SAAgB,8BAA8B,CAC5C,OAAgB,EAChB,aAAkD;IAElD,OAAO,mBAAY,CACjB,OAAO,EACP,6BAAsB,CAAC,4CAA4C,EACnE,aAAa,EACb;QACE,SAAS,EACP,6BAAsB,CAAC,4CAA4C;KACtE,CACF,CAAC;AACJ,CAAC;AAbD,wEAaC;AAED;;GAEG;AACU,QAAA,cAAc,GAAoB,OAAO,CAAC,EAAE;IACvD,mBAAY,CACV,6BAAsB,CAAC,4CAA4C,CACpE,CAAC,OAAO,CAAC,CAAC;IACX,OAAO,CAAC,GAAG,CAAC;QACV,SAAS,EACP,6BAAsB,CAAC,4CAA4C;KACtE,CAAC,CAAC;AACL,CAAC,CAAC"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA,sDAAsD;AACtD,wCAAwC;AACxC,+CAA+C;AAC/C,gEAAgE;;;AAEhE,yCAOwB;AAGxB,iCAA8C;AAgFjC,QAAA,iCAAiC,GAC5C,mCAAmC,CAAC;AAEzB,QAAA,sBAAsB,GAAG,wBAAwB,CAAC;AAE/D;;;;;;;GAOG;AACH,SAAgB,8BAA8B,CAC5C,OAAgB,EAChB,aAAkD;IAElD,OAAO,mBAAY,CACjB,OAAO,EACP,6BAAsB,CAAC,4CAA4C,EACnE,aAAa,EACb;QACE,SAAS,EACP,6BAAsB,CAAC,4CAA4C;KACtE,CACF,CAAC;AACJ,CAAC;AAbD,wEAaC;AAED;;GAEG;AACU,QAAA,cAAc,GAAoB,OAAO,CAAC,EAAE;IACvD,mBAAY,CACV,6BAAsB,CAAC,4CAA4C,CACpE,CAAC,OAAO,CAAC,CAAC;IACX,OAAO,CAAC,GAAG,CAAC;QACV,SAAS,EACP,6BAAsB,CAAC,4CAA4C;KACtE,CAAC,CAAC;AACL,CAAC,CAAC;AAEF;;;;;GAKG;AACH,SAAgB,oCAAoC,CAClD,QAAkC,EAClC,YAAoB;IAEpB,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,KAAK,YAAY,CAAC,CAAC;AAC/D,CAAC;AALD,oFAKC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loopback/authentication",
3
- "version": "4.2.9",
3
+ "version": "6.0.1",
4
4
  "description": "A LoopBack component for authentication support.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -24,21 +24,20 @@
24
24
  "access": "public"
25
25
  },
26
26
  "dependencies": {
27
- "@loopback/core": "^2.9.1",
28
- "@loopback/openapi-v3": "^3.4.5",
29
- "@loopback/rest": "^5.2.0",
30
- "@loopback/security": "^0.2.14",
31
- "@types/express": "^4.17.6",
32
- "@types/lodash": "^4.14.157",
33
- "lodash": "^4.17.15",
34
- "tslib": "^2.0.0"
27
+ "@loopback/core": "^2.9.5",
28
+ "@loopback/rest": "^6.2.0",
29
+ "@loopback/security": "^0.2.18",
30
+ "@types/express": "^4.17.7",
31
+ "@types/lodash": "^4.14.160",
32
+ "lodash": "^4.17.20",
33
+ "tslib": "^2.0.1"
35
34
  },
36
35
  "devDependencies": {
37
- "@loopback/build": "^6.1.0",
38
- "@loopback/eslint-config": "^8.0.3",
39
- "@loopback/openapi-spec-builder": "^2.1.9",
40
- "@loopback/testlab": "^3.2.0",
41
- "@types/node": "^10.17.26",
36
+ "@loopback/build": "^6.2.2",
37
+ "@loopback/eslint-config": "^9.0.2",
38
+ "@loopback/openapi-spec-builder": "^2.1.13",
39
+ "@loopback/testlab": "^3.2.4",
40
+ "@types/node": "^10.17.28",
42
41
  "jsonwebtoken": "^8.5.1"
43
42
  },
44
43
  "keywords": [
@@ -56,5 +55,5 @@
56
55
  "url": "https://github.com/strongloop/loopback-next.git",
57
56
  "directory": "packages/authentication"
58
57
  },
59
- "gitHead": "b89db3d3b8be6a36e63e91c2331d217fda7538de"
58
+ "gitHead": "a3f54273814de63819e0d8bc86509f8a737800bb"
60
59
  }
@@ -3,23 +3,22 @@
3
3
  // This file is licensed under the MIT License.
4
4
  // License text available at https://opensource.org/licenses/MIT
5
5
 
6
- import {bind, Component, ContextTags, ProviderMap} from '@loopback/core';
6
+ import {bind, Component, ContextTags} from '@loopback/core';
7
7
  import {AuthenticationBindings} from './keys';
8
8
  import {
9
9
  AuthenticateActionProvider,
10
+ AuthenticationMiddlewareProvider,
10
11
  AuthenticationStrategyProvider,
11
12
  AuthMetadataProvider,
12
13
  } from './providers';
13
14
 
14
15
  @bind({tags: {[ContextTags.KEY]: AuthenticationBindings.COMPONENT}})
15
16
  export class AuthenticationComponent implements Component {
16
- providers?: ProviderMap;
17
-
18
- constructor() {
19
- this.providers = {
20
- [AuthenticationBindings.AUTH_ACTION.key]: AuthenticateActionProvider,
21
- [AuthenticationBindings.STRATEGY.key]: AuthenticationStrategyProvider,
22
- [AuthenticationBindings.METADATA.key]: AuthMetadataProvider,
23
- };
24
- }
17
+ providers = {
18
+ [AuthenticationBindings.AUTH_ACTION.key]: AuthenticateActionProvider,
19
+ [AuthenticationBindings.STRATEGY.key]: AuthenticationStrategyProvider,
20
+ [AuthenticationBindings.METADATA.key]: AuthMetadataProvider,
21
+ [AuthenticationBindings.AUTHENTICATION_MIDDLEWARE
22
+ .key]: AuthenticationMiddlewareProvider,
23
+ };
25
24
  }
@@ -18,19 +18,17 @@ import {
18
18
  import {AuthenticationMetadata} from '../types';
19
19
 
20
20
  class AuthenticateClassDecoratorFactory extends ClassDecoratorFactory<
21
- AuthenticationMetadata
21
+ AuthenticationMetadata[]
22
22
  > {}
23
23
 
24
24
  /**
25
25
  * Mark a controller method as requiring authenticated user.
26
26
  *
27
- * @param strategyNameOrMetadata - The name of the authentication strategy to use
28
- * or the authentication metadata object.
29
- * @param options - Additional options to configure the authentication.
27
+ * @param strategies - The names of the authentication strategies to use
28
+ * or authentication metadata objects.
30
29
  */
31
30
  export function authenticate(
32
- strategyNameOrMetadata: string | AuthenticationMetadata,
33
- options?: object,
31
+ ...strategies: (string | AuthenticationMetadata)[]
34
32
  ) {
35
33
  return function authenticateDecoratorForClassOrMethod(
36
34
  // Class or a prototype
@@ -42,31 +40,35 @@ export function authenticate(
42
40
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
43
41
  methodDescriptor?: TypedPropertyDescriptor<any>,
44
42
  ) {
45
- let spec: AuthenticationMetadata;
46
- if (typeof strategyNameOrMetadata === 'object') {
47
- spec = strategyNameOrMetadata;
48
- } else {
49
- spec = {strategy: strategyNameOrMetadata, options: options ?? {}};
43
+ const specs: AuthenticationMetadata[] = [];
44
+
45
+ for (const strategy of strategies) {
46
+ if (typeof strategy === 'object') {
47
+ specs.push(strategy);
48
+ } else {
49
+ specs.push({strategy: strategy});
50
+ }
50
51
  }
52
+
51
53
  if (method && methodDescriptor) {
52
54
  // Method
53
- return MethodDecoratorFactory.createDecorator<AuthenticationMetadata>(
55
+ return MethodDecoratorFactory.createDecorator<AuthenticationMetadata[]>(
54
56
  AUTHENTICATION_METADATA_KEY,
55
- spec,
57
+ specs,
56
58
  {decoratorName: '@authenticate'},
57
59
  )(target, method, methodDescriptor);
58
60
  }
59
61
  if (typeof target === 'function' && !method && !methodDescriptor) {
60
62
  // Class
61
- return AuthenticateClassDecoratorFactory.createDecorator(
62
- AUTHENTICATION_METADATA_CLASS_KEY,
63
- spec,
64
- {decoratorName: '@authenticate'},
65
- )(target);
63
+ return AuthenticateClassDecoratorFactory.createDecorator<
64
+ AuthenticationMetadata[]
65
+ >(AUTHENTICATION_METADATA_CLASS_KEY, specs, {
66
+ decoratorName: '@authenticate',
67
+ })(target);
66
68
  }
67
69
  // Not on a class or method
68
70
  throw new Error(
69
- '@intercept cannot be used on a property: ' +
71
+ '@authenticate cannot be used on a property: ' +
70
72
  DecoratorFactory.getTargetName(target, method, methodDescriptor),
71
73
  );
72
74
  };
@@ -88,16 +90,16 @@ export namespace authenticate {
88
90
  export function getAuthenticateMetadata(
89
91
  targetClass: Constructor<{}>,
90
92
  methodName: string,
91
- ): AuthenticationMetadata | undefined {
93
+ ): AuthenticationMetadata[] | undefined {
92
94
  // First check method level
93
- let metadata = MetadataInspector.getMethodMetadata<AuthenticationMetadata>(
95
+ let metadata = MetadataInspector.getMethodMetadata<AuthenticationMetadata[]>(
94
96
  AUTHENTICATION_METADATA_METHOD_KEY,
95
97
  targetClass.prototype,
96
98
  methodName,
97
99
  );
98
100
  if (metadata) return metadata;
99
101
  // Check if the class level has `@authenticate`
100
- metadata = MetadataInspector.getClassMetadata<AuthenticationMetadata>(
102
+ metadata = MetadataInspector.getClassMetadata<AuthenticationMetadata[]>(
101
103
  AUTHENTICATION_METADATA_CLASS_KEY,
102
104
  targetClass,
103
105
  );
package/src/keys.ts CHANGED
@@ -5,6 +5,7 @@
5
5
 
6
6
  import {BindingKey, MetadataAccessor} from '@loopback/core';
7
7
  import {SecurityBindings, UserProfile} from '@loopback/security';
8
+ import {Middleware} from '@loopback/rest';
8
9
  import {AuthenticationComponent} from './authentication.component';
9
10
  import {
10
11
  AuthenticateFn,
@@ -39,8 +40,8 @@ export namespace AuthenticationBindings {
39
40
  >('authentication.userProfileFactory');
40
41
 
41
42
  /**
42
- * Key used to bind an authentication strategy to the context for the
43
- * authentication function to use.
43
+ * Key used to bind an authentication strategy or multiple strategies
44
+ * to the context for the authentication function to use.
44
45
  *
45
46
  * @example
46
47
  * ```ts
@@ -49,9 +50,9 @@ export namespace AuthenticationBindings {
49
50
  * .toProvider(MyAuthenticationStrategy);
50
51
  * ```
51
52
  */
52
- export const STRATEGY = BindingKey.create<AuthenticationStrategy | undefined>(
53
- 'authentication.strategy',
54
- );
53
+ export const STRATEGY = BindingKey.create<
54
+ AuthenticationStrategy | AuthenticationStrategy[] | undefined
55
+ >('authentication.strategy');
55
56
 
56
57
  /**
57
58
  * Key used to inject the authentication function into the sequence.
@@ -88,6 +89,13 @@ export namespace AuthenticationBindings {
88
89
  'authentication.actions.authenticate',
89
90
  );
90
91
 
92
+ /**
93
+ * Binding key for AUTHENTICATION_MIDDLEWARE
94
+ */
95
+ export const AUTHENTICATION_MIDDLEWARE = BindingKey.create<Middleware>(
96
+ 'middleware.authentication',
97
+ );
98
+
91
99
  /**
92
100
  * Key used to inject authentication metadata, which is used to determine
93
101
  * whether a request requires authentication or not.
@@ -97,20 +105,19 @@ export namespace AuthenticationBindings {
97
105
  * class MyPassportStrategyProvider implements Provider<Strategy | undefined> {
98
106
  * constructor(
99
107
  * @inject(AuthenticationBindings.METADATA)
100
- * private metadata: AuthenticationMetadata,
108
+ * private metadata?: AuthenticationMetadata[],
101
109
  * ) {}
102
110
  * value(): ValueOrPromise<Strategy | undefined> {
103
- * if (this.metadata) {
104
- * const name = this.metadata.strategy;
111
+ * if (this.metadata?.length) {
105
112
  * // logic to determine which authentication strategy to return
106
113
  * }
107
114
  * }
108
115
  * }
109
116
  * ```
110
117
  */
111
- export const METADATA = BindingKey.create<AuthenticationMetadata | undefined>(
112
- 'authentication.operationMetadata',
113
- );
118
+ export const METADATA = BindingKey.create<
119
+ AuthenticationMetadata[] | undefined
120
+ >('authentication.operationMetadata');
114
121
 
115
122
  export const AUTHENTICATION_STRATEGY_EXTENSION_POINT_NAME =
116
123
  'authentication.strategies';
@@ -3,13 +3,20 @@
3
3
  // This file is licensed under the MIT License.
4
4
  // License text available at https://opensource.org/licenses/MIT
5
5
 
6
- import {Getter, inject, Provider, Setter} from '@loopback/core';
7
- import {Request, RedirectRoute} from '@loopback/rest';
6
+ import {bind, Getter, inject, Provider, Setter} from '@loopback/core';
7
+ import {
8
+ asMiddleware,
9
+ Middleware,
10
+ RedirectRoute,
11
+ Request,
12
+ RestMiddlewareGroups,
13
+ } from '@loopback/rest';
8
14
  import {SecurityBindings, UserProfile} from '@loopback/security';
9
15
  import {AuthenticationBindings} from '../keys';
10
16
  import {
11
17
  AuthenticateFn,
12
18
  AuthenticationStrategy,
19
+ AUTHENTICATION_STRATEGY_NOT_FOUND,
13
20
  USER_PROFILE_NOT_FOUND,
14
21
  } from '../types';
15
22
  /**
@@ -26,7 +33,9 @@ export class AuthenticateActionProvider implements Provider<AuthenticateFn> {
26
33
  // defer resolution of the strategy until authenticate() action
27
34
  // is executed.
28
35
  @inject.getter(AuthenticationBindings.STRATEGY)
29
- readonly getStrategy: Getter<AuthenticationStrategy>,
36
+ readonly getStrategies: Getter<
37
+ AuthenticationStrategy | AuthenticationStrategy[] | undefined
38
+ >,
30
39
  @inject.setter(SecurityBindings.USER)
31
40
  readonly setCurrentUser: Setter<UserProfile>,
32
41
  @inject.setter(AuthenticationBindings.AUTHENTICATION_REDIRECT_URL)
@@ -47,36 +56,89 @@ export class AuthenticateActionProvider implements Provider<AuthenticateFn> {
47
56
  * @param request - The incoming request provided by the REST layer
48
57
  */
49
58
  async action(request: Request): Promise<UserProfile | undefined> {
50
- const strategy = await this.getStrategy();
51
- if (!strategy) {
59
+ let strategies = await this.getStrategies();
60
+ if (!strategies) {
52
61
  // The invoked operation does not require authentication.
53
62
  return undefined;
54
63
  }
64
+ // convert to array if required
65
+ strategies = Array.isArray(strategies) ? strategies : [strategies];
66
+
67
+ let authenticated = false;
68
+ let redirected = false;
69
+ let authError: Error | undefined;
70
+ let authResponse: UserProfile | RedirectRoute | undefined;
71
+ let userProfile: UserProfile | undefined;
72
+
73
+ for (const strategy of strategies) {
74
+ // the first strategy to succeed or redirect will halt the execution chain
75
+ if (authenticated || redirected) break;
76
+
77
+ try {
78
+ authResponse = await strategy.authenticate(request);
79
+
80
+ // response from `strategy.authenticate()` could return an object of type UserProfile or RedirectRoute
81
+ if (RedirectRoute.isRedirectRoute(authResponse)) {
82
+ redirected = true;
83
+ const redirectOptions = authResponse;
84
+ // bind redirection url and status to the context
85
+ // controller should handle actual redirection
86
+ this.setRedirectUrl(redirectOptions.targetLocation);
87
+ this.setRedirectStatus(redirectOptions.statusCode);
88
+ } else if (authResponse) {
89
+ authenticated = true;
90
+ // if `strategy.authenticate()` returns an object of type UserProfile, set it as current user
91
+ userProfile = authResponse as UserProfile;
92
+ } else if (!authResponse) {
93
+ // important to throw a non-protocol-specific error here
94
+ const error = new Error(
95
+ `User profile not returned from strategy's authenticate function`,
96
+ );
97
+ Object.assign(error, {
98
+ code: USER_PROFILE_NOT_FOUND,
99
+ });
100
+ throw error;
101
+ }
102
+ } catch (error) {
103
+ authError = authError ?? error;
104
+ }
105
+ }
55
106
 
56
- const authResponse = await strategy.authenticate(request);
57
- let userProfile: UserProfile;
107
+ if (!authenticated && !redirected) throw authError;
58
108
 
59
- // response from `strategy.authenticate()` could return an object of type UserProfile or RedirectRoute
60
- if (RedirectRoute.isRedirectRoute(authResponse)) {
61
- const redirectOptions = authResponse;
62
- // bind redirection url and status to the context
63
- // controller should handle actual redirection
64
- this.setRedirectUrl(redirectOptions.targetLocation);
65
- this.setRedirectStatus(redirectOptions.statusCode);
66
- } else if (authResponse) {
67
- // if `strategy.authenticate()` returns an object of type UserProfile, set it as current user
68
- userProfile = authResponse as UserProfile;
109
+ if (userProfile) {
69
110
  this.setCurrentUser(userProfile);
70
111
  return userProfile;
71
- } else if (!authResponse) {
72
- // important to throw a non-protocol-specific error here
73
- const error = new Error(
74
- `User profile not returned from strategy's authenticate function`,
75
- );
76
- Object.assign(error, {
77
- code: USER_PROFILE_NOT_FOUND,
78
- });
79
- throw error;
80
112
  }
81
113
  }
82
114
  }
115
+
116
+ @bind(
117
+ asMiddleware({
118
+ group: RestMiddlewareGroups.AUTHENTICATION,
119
+ upstreamGroups: [RestMiddlewareGroups.FIND_ROUTE],
120
+ }),
121
+ )
122
+ export class AuthenticationMiddlewareProvider implements Provider<Middleware> {
123
+ constructor(
124
+ @inject(AuthenticationBindings.AUTH_ACTION)
125
+ private authenticate: AuthenticateFn,
126
+ ) {}
127
+
128
+ value(): Middleware {
129
+ return async (ctx, next) => {
130
+ try {
131
+ await this.authenticate(ctx.request);
132
+ } catch (error) {
133
+ if (
134
+ error.code === AUTHENTICATION_STRATEGY_NOT_FOUND ||
135
+ error.code === USER_PROFILE_NOT_FOUND
136
+ ) {
137
+ error.statusCode = 401;
138
+ }
139
+ throw error;
140
+ }
141
+ return next();
142
+ };
143
+ }
144
+ }
@@ -6,9 +6,9 @@
6
6
  import {
7
7
  config,
8
8
  Constructor,
9
+ CoreBindings,
9
10
  inject,
10
11
  Provider,
11
- CoreBindings,
12
12
  } from '@loopback/core';
13
13
  import {getAuthenticateMetadata} from '../decorators';
14
14
  import {AuthenticationBindings} from '../keys';
@@ -19,7 +19,7 @@ import {AuthenticationMetadata, AuthenticationOptions} from '../types';
19
19
  * @example `context.bind('authentication.operationMetadata').toProvider(AuthMetadataProvider)`
20
20
  */
21
21
  export class AuthMetadataProvider
22
- implements Provider<AuthenticationMetadata | undefined> {
22
+ implements Provider<AuthenticationMetadata[] | undefined> {
23
23
  constructor(
24
24
  @inject(CoreBindings.CONTROLLER_CLASS, {optional: true})
25
25
  private readonly controllerClass: Constructor<{}>,
@@ -32,14 +32,14 @@ export class AuthMetadataProvider
32
32
  /**
33
33
  * @returns AuthenticationMetadata
34
34
  */
35
- value(): AuthenticationMetadata | undefined {
35
+ value(): AuthenticationMetadata[] | undefined {
36
36
  if (!this.controllerClass || !this.methodName) return;
37
37
  const metadata = getAuthenticateMetadata(
38
38
  this.controllerClass,
39
39
  this.methodName,
40
40
  );
41
41
  // Skip authentication if `skip` is `true`
42
- if (metadata?.skip) return undefined;
42
+ if (metadata?.[0]?.skip) return undefined;
43
43
  if (metadata) return metadata;
44
44
  // Fall back to default metadata
45
45
  return this.options.defaultMetadata;
@@ -5,10 +5,10 @@
5
5
 
6
6
  import {
7
7
  BindingScope,
8
- Getter,
9
- inject,
10
8
  extensionPoint,
11
9
  extensions,
10
+ Getter,
11
+ inject,
12
12
  Provider,
13
13
  } from '@loopback/core';
14
14
  import {AuthenticationBindings} from '../keys';
@@ -32,33 +32,44 @@ import {
32
32
  {scope: BindingScope.TRANSIENT},
33
33
  ) //this needs to be transient, e.g. for request level context.
34
34
  export class AuthenticationStrategyProvider
35
- implements Provider<AuthenticationStrategy | undefined> {
35
+ implements Provider<AuthenticationStrategy[] | undefined> {
36
36
  constructor(
37
37
  @extensions()
38
38
  protected authenticationStrategies: Getter<AuthenticationStrategy[]>,
39
39
  @inject(AuthenticationBindings.METADATA)
40
- protected metadata?: AuthenticationMetadata,
40
+ protected metadata?: AuthenticationMetadata[],
41
41
  ) {}
42
- async value(): Promise<AuthenticationStrategy | undefined> {
43
- if (!this.metadata) {
42
+ async value(): Promise<AuthenticationStrategy[] | undefined> {
43
+ if (!this.metadata?.length) {
44
44
  return undefined;
45
45
  }
46
- const name = this.metadata.strategy;
47
- const strategy = await this.findAuthenticationStrategy(name);
48
- if (!strategy) {
49
- // important to throw a non-protocol-specific error here
50
- const error = new Error(`The strategy '${name}' is not available.`);
51
- Object.assign(error, {
52
- code: AUTHENTICATION_STRATEGY_NOT_FOUND,
53
- });
54
- throw error;
55
- }
56
- return strategy;
46
+ return this.findAuthenticationStrategies(this.metadata);
57
47
  }
58
48
 
59
- async findAuthenticationStrategy(name: string) {
60
- const strategies = await this.authenticationStrategies();
61
- const matchingAuthStrategy = strategies.find(a => a.name === name);
62
- return matchingAuthStrategy;
49
+ private async findAuthenticationStrategies(
50
+ metadata: AuthenticationMetadata[],
51
+ ): Promise<AuthenticationStrategy[]> {
52
+ const strategies: AuthenticationStrategy[] = [];
53
+
54
+ const existingStrategies = await this.authenticationStrategies();
55
+
56
+ const findStrategy = (name: string) => {
57
+ const strategy = existingStrategies.find(a => a.name === name);
58
+ if (!strategy) {
59
+ const error = new Error(`The strategy '${name}' is not available.`);
60
+ Object.assign(error, {
61
+ code: AUTHENTICATION_STRATEGY_NOT_FOUND,
62
+ });
63
+ throw error;
64
+ }
65
+ return strategy;
66
+ };
67
+
68
+ for (const data of metadata) {
69
+ const strategy = findStrategy(data.strategy);
70
+ strategies.push(strategy);
71
+ }
72
+
73
+ return strategies;
63
74
  }
64
75
  }
package/src/types.ts CHANGED
@@ -11,7 +11,7 @@ import {
11
11
  Context,
12
12
  extensionFor,
13
13
  } from '@loopback/core';
14
- import {Request, RedirectRoute} from '@loopback/rest';
14
+ import {RedirectRoute, Request} from '@loopback/rest';
15
15
  import {UserProfile} from '@loopback/security';
16
16
  import {AuthenticationBindings} from './keys';
17
17
 
@@ -59,7 +59,7 @@ export interface AuthenticationOptions {
59
59
  * `@authenticate`. If not set, no default authentication will be enforced for
60
60
  * those methods without authentication metadata.
61
61
  */
62
- defaultMetadata?: AuthenticationMetadata;
62
+ defaultMetadata?: AuthenticationMetadata[];
63
63
  }
64
64
 
65
65
  /**
@@ -133,3 +133,16 @@ export const asAuthStrategy: BindingTemplate = binding => {
133
133
  AuthenticationBindings.AUTHENTICATION_STRATEGY_EXTENSION_POINT_NAME,
134
134
  });
135
135
  };
136
+
137
+ /**
138
+ * Get the authentication metadata object for the specified strategy.
139
+ *
140
+ * @param metadata - Array of authentication metadata objects
141
+ * @param strategyName - Name of the authentication strategy
142
+ */
143
+ export function getAuthenticationMetadataForStrategy(
144
+ metadata: AuthenticationMetadata[],
145
+ strategyName: string,
146
+ ): AuthenticationMetadata | undefined {
147
+ return metadata.find(data => data.strategy === strategyName);
148
+ }