Skip to content

Add support for other storage options #97

@joaogranado

Description

@joaogranado

There are some issues and pull requests that try to fix this by implementing other storage managers, based on Web Storage API. I think we should think about a different approach, which would be simpler to maintain, and would lead to a more user-friendly API. It would be a major breaking change, but would simplify the API by removing the dependency on $cookies service. My suggestion takes some clues from reflective meta programming, and it is quite simple. I consists on redefining the semantics of the storage operations by passing a target object, such as $cookies it would be possible to map its methods to an abstract storage service. Example:

Configuration remains the same.
// oauth-token-config.js
myModule.config(OAuthTokenProvider => {
  OAuthTokenProvider.configure({
    name: 'token',
    options: { secure: true }
  });
});

Using $cookies. This should be done in a run block to allow injecting third party dependencies.

// oauth-token-run.js
myModule.run(($cookies, OAuthToken) => {
  // Cookies example.
  OAuthToken.extend($cookies, config => {
    return {
      getToken: {
        method: 'getObject',
        args: [config.name]
      },
      removeToken: {
        method: 'removeToken',
        args: [config.name, config.options]
      },
      setToken: data => ({
        method: 'putObject',
        args: [config.name, data, config.options]
      })
    }
  }));

Using sessionStorage:

// oauth-token-run.js
myModule.run(() => {
  // sessionStorage example.
  OAuthToken.extend(sessionStorage, config => {
    return {
      getToken: {
        method: 'getItem',
        args: [config.name]
      },
      removeToken: {
        method: 'removeItem',
        args: [config.name]
      },
      setToken: data => ({
        method: 'setItem',
        args: [config.name, JSON.stringify(data)]
      }),
    }
  }));

The implementation of the OAuthToken would be straight forward, such as:

class OAuthToken {
  extend(target, handlers) {
    this.storageService = target;
    this.handlers = handlers(config);
  }

  setToken(data) {
    // We should execute validations to check it the storage service was set.
    const { args, method } = this.handlers.setToken(data);

    this.storageService[method](...args);
  }

  getToken() {
    const { args, method } = this.handlers.getToken;

    this.storageService[method](...args);
  }

  removeToken() {
    const { args, method } = this.handlers.removeToken;

    this.storageService[method](...args);
  }
}

As I've mentioned in the comment on setToken we should validate on each method if both target and the required method were defined. This might seem a lot more configuration, and should be well documented, but this solves the following recurrent issues:

  • Removes dependencies from third-party storage libraries - $cookies.
  • Avoids maintaining a large code base prone to bugs, and not scalable, with naive implementations, that don't take basic security considerations.
  • Adds flexibility to implementing other modules than Web Storage (both localStorage and sessionStorage) and cookies.

@ruipenso what do you thing? It would be awesome if someone this :)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions