Skip to content

Add support for other storage options #97

Open
@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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions