aboutsummaryrefslogtreecommitdiffstats
path: root/lib/caldav/http/oauth2.js
blob: 75e63349655ac6c295a0f576bdee35c9ca5fce8f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
(function(module, ns) {

  var XHR = ns.require('xhr');
  var QueryString = ns.require('querystring');
  var Connection = ns.require('connection');
  var OAuth = ns.require('oauth2');

  /**
   * Creates an XHR like object given a connection and a set of options
   * (passed directly to the superclass)
   *
   * @param {Caldav.Connection} connection used for apiCredentials.
   * @param {Object} options typical XHR options.
   */
  function Oauth2(connection, options) {
    if (
      !connection ||
      !connection.oauth ||
      (
        !connection.oauth.code &&
        !connection.oauth.refresh_token
      )
    ) {
      throw new Error('connection .oauth must have code or refresh_token');
    }

    this.connection = connection;

    this.oauth =
      new OAuth(connection.apiCredentials);

    // create a clone of options
    var clone = Object.create(null);

    if (typeof(options) !== 'undefined') {
      for (var key in options) {
        clone[key] = options[key];
      }
    }

    XHR.call(this, clone);
  }

  Oauth2.prototype = {
    __proto__: XHR.prototype,

    _sendXHR: function(xhr) {
      xhr.setRequestHeader(
        'Authorization', 'Bearer ' + this.connection.oauth.access_token
      );

      xhr.send(this._serialize());
      return xhr;
    },

    _updateConnection: function(credentials) {
      var oauth = this.connection.oauth;
      var update = { oauth: credentials };

      if (oauth.refresh_token && !credentials.refresh_token)
        credentials.refresh_token = oauth.refresh_token;

      if (credentials.user) {
        update.user = credentials.user;
        delete credentials.user;
      }

      return this.connection.update(update);
    },

    send: function(callback) {
      var xhr = this._buildXHR(callback);
      var oauth = this.connection.oauth;

      // everything is fine just send
      if (this.oauth.accessTokenValid(oauth)) {
        return this._sendXHR(xhr);
      }

      var handleTokenUpdates = (function handleTokenUpdates(err, credentials) {
        if (err) {
          return callback(err);
        }
        this._updateConnection(credentials);
        return this._sendXHR(xhr);
      }.bind(this));

      if (oauth.code) {
        this.oauth.authenticateCode(oauth.code, handleTokenUpdates);

        // it should be impossible to have both code and refresh_token
        // but we return as a guard
        return xhr;
      }

      if (oauth.refresh_token) {
        this.oauth.refreshToken(oauth.refresh_token, handleTokenUpdates);
        return xhr;
      }
    }

  };


  module.exports = Oauth2;

}.apply(
  this,
  (this.Caldav) ?
    [Caldav('http/oauth2'), Caldav] :
    [module, require('../caldav')]
));