docs: function token (options) {
    options = options || {}
  
    var self = this
    var uri = this.configuration.token_endpoint
    var code = options.code
    var grantType = options.grant_type || 'authorization_code'
    var scope = options.scope || self.scope
    var redirectUri = options.redirect_uri || self.redirect_uri
    var refreshToken = options.refresh_token
    var formRequestData
  
    if (grantType === 'client_credentials') {
      // 'client_credentials' grants do not need a code or redirect uri
      // Assumes this client is registered and has had the 'authority' role
      // added to it previously
      formRequestData = {
        grant_type: grantType,
        scope: scope
      }
    } else if (grantType === 'refresh_token') {
      if (!refreshToken) {
        return Promise.reject(
          new Error('Refresh token grant types require refresh_token'))
      }
      formRequestData = {
        grant_type: grantType,
        refresh_token: refreshToken,
        scope: scope
      }
    } else {
      // For 'authorization_code' grant type, need to get the code
      // if code is not passed in explicitly, try extract it from responseUri
      if (!code && options.responseUri) {
        var u = url.parse(options.responseUri)
        code = qs.parse(u.query).code
      }
      if (!code) {
        return Promise.reject(new Error('Missing authorization code'))
      }
      formRequestData = {
        code: code,
        grant_type: grantType,
        redirect_uri: redirectUri
      }
    }
  
    var requestOptions = {
      url: uri,
      method: 'POST',
      form: formRequestData,
      json: true,
      auth: {
        user: self.client_id,
        pass: self.client_secret
      },
      agentOptions: self.agentOptions
    }
    if (self.proxy) {
      requestOptions.proxy = self.proxy
    }
  
    return new Promise(function (resolve, reject) {
      request(requestOptions)
        .then(function (data) {
          var verifyClaims = {
            access_claims: function (done) {
              AccessToken.verify(data.access_token, {
                key: self.jwks.keys[0],
                issuer: self.issuer
              }, function (err, claims) {
                if (err) { return done(err) }
                done(null, claims)
              })
            }
          }
          // when requesting a token using client credentials no ID information is
          // returned
          if (formRequestData.grant_type !== 'client_credentials') {
            verifyClaims.id_claims = function (done) {
              IDToken.verify(data.id_token, {
                iss: self.issuer,
                aud: self.client_id,
                key: self.jwks.keys[0]
              }, function (err, token) {
                if (err) { return done(err) }
                done(null, token.payload)
              })
            }
          }
          // verify tokens
          async.parallel(verifyClaims, function (err, result) {
            if (err) {
              return reject(err)
            }
  
            data.id_claims = result.id_claims
            data.access_claims = result.access_claims
  
            resolve(data)
          })
        })
        .catch(function (err) {
          reject(err)
        })
    })
  }