Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Map Providers / Interface Providers / Array Providers #2469

Open
darkbasic opened this issue Nov 4, 2023 · 0 comments
Open

Map Providers / Interface Providers / Array Providers #2469

darkbasic opened this issue Nov 4, 2023 · 0 comments

Comments

@darkbasic
Copy link
Contributor

darkbasic commented Nov 4, 2023

While rearchitecting accounts.js 1.0 I've played around the concept of Map Providers.
Basically in Accounts.js we have multiple types of authentication services (password, magic link, oauth, sms, etc..) which implement the same known interface and while most of them are hosted in the monorepo the design allows the user to create and provide its own authentication service. That means that injecting all of them into shared services like AccountsServer (which is part of the core module) is not an option, because this way the user would not be allowed to create its own auth service.

To solve this issue I'd like to introduce the concept of Map Providers: a type of provider which acts similarly to Javascript Maps where the map is constructed by declaring new providers which add certain keys to the Map.

Basically you would inject the AuthenticationServicesToken like this:

export const AuthenticationServicesToken = new InjectionToken<AuthenticationServices>(
  'AuthenticationServices'
);

// AuthenticationServices is either a Map or a Map-like object
constructor(@Inject(AuthenticationServicesToken) public services: AuthenticationServices<CustomUser>) {

and other modules like the password module would provide the various elements like this:

{
  provide: AuthenticationServicesToken,
  useMap: ['password', AccountsPassword], // [key, value] tuple
},

Another way we could look at this is the concept of Interface Providers, where each element of the Map must implement a known interface in order to extend the associated provider:

// services is a Map/Map-like Object where each element of the Map implements the AuthenticationServiceInterface
constructor(@Inject(AuthenticationServiceInterface) public services: Map<string, AuthenticationServiceInterface<CustomUser>>) {
{
  provide: AuthenticationServiceInterface,
  useInterface: ['password', AccountsPassword], // [key, value] tuple, AccountsPassword implements AuthenticationServiceInterface
},

The downside of this approach is that I'm not sure if you can use Typescript interfaces as Injection Tokens.

Instead of a Map/Map-like object we could even inject an array:

// services is an array
constructor(@Inject(AuthenticationServiceInterface) public services: AuthenticationServiceInterface<CustomUser>[]) {
{
  provide: AuthenticationServiceInterface,
  useInterface: AccountsPassword, // AccountsPassword implements AuthenticationServiceInterface
},

But the downside of this approach is that you will have to go through each and every element of the array every time you look for a certain authentication service because you won't have a key.

With the current feature set the various modules which implement the different authentication services cannot provide the new authentication service on their own: after including the module the user has to manually provide the AuthenticationServicesToken.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant