From 126ceff377fd4ea595025f4211b580407d5d32a9 Mon Sep 17 00:00:00 2001
From: jankapunkt <jkuester@uni-bremen.de>
Date: Fri, 9 Jun 2023 08:36:14 +0200
Subject: [PATCH 1/7] breaking(deps): remove bluebird and promisify-any

---
 .mocharc.yml                                  |   2 +-
 lib/grant-types/abstract-grant-type.js        |  35 ++-
 .../authorization-code-grant-type.js          | 203 +++++++---------
 .../client-credentials-grant-type.js          |  50 ++--
 lib/grant-types/password-grant-type.js        |  67 ++----
 lib/grant-types/refresh-token-grant-type.js   | 129 +++++-----
 lib/handlers/authenticate-handler.js          | 116 +++++----
 lib/handlers/authorize-handler.js             | 225 +++++++++---------
 lib/handlers/token-handler.js                 | 110 ++++-----
 lib/server.js                                 |  19 +-
 lib/utils/token-util.js                       |  10 +-
 package.json                                  |   2 -
 .../grant-types/abstract-grant-type_test.js   |   1 -
 .../authorization-code-grant-type_test.js     |  93 ++------
 .../client-credentials-grant-type_test.js     |  32 +--
 .../grant-types/password-grant-type_test.js   |  71 ++----
 .../refresh-token-grant-type_test.js          |  68 +-----
 .../handlers/authenticate-handler_test.js     |  69 ++----
 .../handlers/authorize-handler_test.js        |  91 ++-----
 .../handlers/token-handler_test.js            |  56 ++---
 test/integration/server_test.js               | 114 +++------
 test/integration/utils/token-util_test.js     |  10 +-
 .../authorization-code-grant-type_test.js     |   1 -
 test/unit/handlers/authorize-handler_test.js  |   1 -
 test/unit/server_test.js                      |   1 -
 25 files changed, 574 insertions(+), 1002 deletions(-)

diff --git a/.mocharc.yml b/.mocharc.yml
index 83fda38..1b4a955 100644
--- a/.mocharc.yml
+++ b/.mocharc.yml
@@ -1,6 +1,6 @@
 recursive: true
 reporter: "spec"
-retries: 1
+retries: 0
 slow: 20
 timeout: 2000
 ui: "bdd"
diff --git a/lib/grant-types/abstract-grant-type.js b/lib/grant-types/abstract-grant-type.js
index d9894b6..4fd0243 100644
--- a/lib/grant-types/abstract-grant-type.js
+++ b/lib/grant-types/abstract-grant-type.js
@@ -6,8 +6,6 @@
 
 const InvalidArgumentError = require('../errors/invalid-argument-error');
 const InvalidScopeError = require('../errors/invalid-scope-error');
-const Promise = require('bluebird');
-const promisify = require('promisify-any').use(Promise);
 const isFormat = require('@node-oauth/formats');
 const tokenUtil = require('../utils/token-util');
 
@@ -36,12 +34,10 @@ function AbstractGrantType(options) {
  * Generate access token.
  */
 
-AbstractGrantType.prototype.generateAccessToken = function(client, user, scope) {
+AbstractGrantType.prototype.generateAccessToken = async function(client, user, scope) {
   if (this.model.generateAccessToken) {
-    return promisify(this.model.generateAccessToken, 3).call(this.model, client, user, scope)
-      .then(function(accessToken) {
-        return accessToken || tokenUtil.generateRandomToken();
-      });
+    const accessToken = await this.model.generateAccessToken(client, user, scope);
+    return accessToken || tokenUtil.generateRandomToken();
   }
 
   return tokenUtil.generateRandomToken();
@@ -51,12 +47,10 @@ AbstractGrantType.prototype.generateAccessToken = function(client, user, scope)
  * Generate refresh token.
  */
 
-AbstractGrantType.prototype.generateRefreshToken = function(client, user, scope) {
+AbstractGrantType.prototype.generateRefreshToken = async function(client, user, scope) {
   if (this.model.generateRefreshToken) {
-    return promisify(this.model.generateRefreshToken, 3).call(this.model, client, user, scope)
-      .then(function(refreshToken) {
-        return refreshToken || tokenUtil.generateRandomToken();
-      });
+    const refreshToken = await this.model.generateRefreshToken(client, user, scope);
+    return refreshToken || tokenUtil.generateRandomToken();
   }
 
   return tokenUtil.generateRandomToken();
@@ -93,16 +87,15 @@ AbstractGrantType.prototype.getScope = function(request) {
 /**
  * Validate requested scope.
  */
-AbstractGrantType.prototype.validateScope = function(user, client, scope) {
+AbstractGrantType.prototype.validateScope = async function(user, client, scope) {
   if (this.model.validateScope) {
-    return promisify(this.model.validateScope, 3).call(this.model, user, client, scope)
-      .then(function (scope) {
-        if (!scope) {
-          throw new InvalidScopeError('Invalid scope: Requested scope is invalid');
-        }
-
-        return scope;
-      });
+    const validatedScope = await this.model.validateScope(user, client, scope);
+
+    if (!validatedScope) {
+      throw new InvalidScopeError('Invalid scope: Requested scope is invalid');
+    }
+
+    return validatedScope;
   } else {
     return scope;
   }
diff --git a/lib/grant-types/authorization-code-grant-type.js b/lib/grant-types/authorization-code-grant-type.js
index 73dec6d..2101462 100644
--- a/lib/grant-types/authorization-code-grant-type.js
+++ b/lib/grant-types/authorization-code-grant-type.js
@@ -8,8 +8,6 @@ const AbstractGrantType = require('./abstract-grant-type');
 const InvalidArgumentError = require('../errors/invalid-argument-error');
 const InvalidGrantError = require('../errors/invalid-grant-error');
 const InvalidRequestError = require('../errors/invalid-request-error');
-const Promise = require('bluebird');
-const promisify = require('promisify-any').use(Promise);
 const ServerError = require('../errors/server-error');
 const isFormat = require('@node-oauth/formats');
 const pkce = require('../pkce/pkce');
@@ -45,7 +43,7 @@ class AuthorizationCodeGrantType extends AbstractGrantType {
 	 * @see https://tools.ietf.org/html/rfc6749#section-4.1.3
 	 */
 
-  handle(request, client) {
+  async handle(request, client) {
     if (!request) {
       throw new InvalidArgumentError('Missing parameter: `request`');
     }
@@ -54,26 +52,18 @@ class AuthorizationCodeGrantType extends AbstractGrantType {
       throw new InvalidArgumentError('Missing parameter: `client`');
     }
 
-    return Promise.bind(this)
-      .then(function () {
-        return this.getAuthorizationCode(request, client);
-      })
-      .tap(function (code) {
-        return this.validateRedirectUri(request, code);
-      })
-      .tap(function (code) {
-        return this.revokeAuthorizationCode(code);
-      })
-      .then(function (code) {
-        return this.saveToken(code.user, client, code.authorizationCode, code.scope);
-      });
+    const code = await this.getAuthorizationCode(request, client);
+    await this.validateRedirectUri(request, code);
+    await this.revokeAuthorizationCode(code);
+
+    return this.saveToken(code.user, client, code.authorizationCode, code.scope);
   }
 
   /**
 	 * Get the authorization code.
 	 */
 
-  getAuthorizationCode(request, client) {
+  async getAuthorizationCode(request, client) {
     if (!request.body.code) {
       throw new InvalidRequestError('Missing parameter: `code`');
     }
@@ -81,69 +71,68 @@ class AuthorizationCodeGrantType extends AbstractGrantType {
     if (!isFormat.vschar(request.body.code)) {
       throw new InvalidRequestError('Invalid parameter: `code`');
     }
-    return promisify(this.model.getAuthorizationCode, 1)
-      .call(this.model, request.body.code)
-      .then((code) => {
-        if (!code) {
-          throw new InvalidGrantError('Invalid grant: authorization code is invalid');
-        }
-
-        if (!code.client) {
-          throw new ServerError('Server error: `getAuthorizationCode()` did not return a `client` object');
-        }
-
-        if (!code.user) {
-          throw new ServerError('Server error: `getAuthorizationCode()` did not return a `user` object');
-        }
-
-        if (code.client.id !== client.id) {
-          throw new InvalidGrantError('Invalid grant: authorization code is invalid');
-        }
-
-        if (!(code.expiresAt instanceof Date)) {
-          throw new ServerError('Server error: `expiresAt` must be a Date instance');
-        }
-
-        if (code.expiresAt < new Date()) {
-          throw new InvalidGrantError('Invalid grant: authorization code has expired');
-        }
-
-        if (code.redirectUri && !isFormat.uri(code.redirectUri)) {
-          throw new InvalidGrantError('Invalid grant: `redirect_uri` is not a valid URI');
-        }
-
-        // optional: PKCE code challenge
-
-        if (code.codeChallenge) {
-          if (!request.body.code_verifier) {
-            throw new InvalidGrantError('Missing parameter: `code_verifier`');
-          }
-
-          const hash = pkce.getHashForCodeChallenge({
-            method: code.codeChallengeMethod,
-            verifier: request.body.code_verifier
-          });
-
-          if (!hash) {
-            // notice that we assume that codeChallengeMethod is already
-            // checked at an earlier stage when being read from
-            // request.body.code_challenge_method
-            throw new ServerError('Server error: `getAuthorizationCode()` did not return a valid `codeChallengeMethod` property');
-          }
-
-          if (code.codeChallenge !== hash) {
-            throw new InvalidGrantError('Invalid grant: code verifier is invalid');
-          }
-        }
-        else {
-          if (request.body.code_verifier) {
-            // No code challenge but code_verifier was passed in.
-            throw new InvalidGrantError('Invalid grant: code verifier is invalid');
-          }
-        }
-
-        return code;
+
+    const code = await this.model.getAuthorizationCode(request.body.code);
+
+    if (!code) {
+      throw new InvalidGrantError('Invalid grant: authorization code is invalid');
+    }
+
+    if (!code.client) {
+      throw new ServerError('Server error: `getAuthorizationCode()` did not return a `client` object');
+    }
+
+    if (!code.user) {
+      throw new ServerError('Server error: `getAuthorizationCode()` did not return a `user` object');
+    }
+
+    if (code.client.id !== client.id) {
+      throw new InvalidGrantError('Invalid grant: authorization code is invalid');
+    }
+
+    if (!(code.expiresAt instanceof Date)) {
+      throw new ServerError('Server error: `expiresAt` must be a Date instance');
+    }
+
+    if (code.expiresAt < new Date()) {
+      throw new InvalidGrantError('Invalid grant: authorization code has expired');
+    }
+
+    if (code.redirectUri && !isFormat.uri(code.redirectUri)) {
+      throw new InvalidGrantError('Invalid grant: `redirect_uri` is not a valid URI');
+    }
+
+    // optional: PKCE code challenge
+
+    if (code.codeChallenge) {
+      if (!request.body.code_verifier) {
+        throw new InvalidGrantError('Missing parameter: `code_verifier`');
+      }
+
+      const hash = pkce.getHashForCodeChallenge({
+        method: code.codeChallengeMethod,
+        verifier: request.body.code_verifier
       });
+
+      if (!hash) {
+        // notice that we assume that codeChallengeMethod is already
+        // checked at an earlier stage when being read from
+        // request.body.code_challenge_method
+        throw new ServerError('Server error: `getAuthorizationCode()` did not return a valid `codeChallengeMethod` property');
+      }
+
+      if (code.codeChallenge !== hash) {
+        throw new InvalidGrantError('Invalid grant: code verifier is invalid');
+      }
+    }
+    else {
+      if (request.body.code_verifier) {
+        // No code challenge but code_verifier was passed in.
+        throw new InvalidGrantError('Invalid grant: code verifier is invalid');
+      }
+    }
+
+    return code;
   }
 
   /**
@@ -183,16 +172,14 @@ class AuthorizationCodeGrantType extends AbstractGrantType {
 	 * @see https://tools.ietf.org/html/rfc6749#section-4.1.2
 	 */
 
-  revokeAuthorizationCode(code) {
-    return promisify(this.model.revokeAuthorizationCode, 1)
-      .call(this.model, code)
-      .then((status) => {
-        if (!status) {
-          throw new InvalidGrantError('Invalid grant: authorization code is invalid');
-        }
+  async revokeAuthorizationCode(code) {
+    const status = await this.model.revokeAuthorizationCode(code);
 
-        return code;
-      });
+    if (!status) {
+      throw new InvalidGrantError('Invalid grant: authorization code is invalid');
+    }
+
+    return code;
   }
 
 
@@ -200,29 +187,23 @@ class AuthorizationCodeGrantType extends AbstractGrantType {
 	 * Save token.
 	 */
 
-  saveToken(user, client, authorizationCode, scope) {
-    const fns = [
-      this.validateScope(user, client, scope),
-      this.generateAccessToken(client, user, scope),
-      this.generateRefreshToken(client, user, scope),
-      this.getAccessTokenExpiresAt(),
-      this.getRefreshTokenExpiresAt(),
-    ];
-
-    return Promise.all(fns)
-      .bind(this)
-      .spread(function (scope, accessToken, refreshToken, accessTokenExpiresAt, refreshTokenExpiresAt) {
-        const token = {
-          accessToken: accessToken,
-          authorizationCode: authorizationCode,
-          accessTokenExpiresAt: accessTokenExpiresAt,
-          refreshToken: refreshToken,
-          refreshTokenExpiresAt: refreshTokenExpiresAt,
-          scope: scope,
-        };
-
-        return promisify(this.model.saveToken, 3).call(this.model, token, client, user);
-      });
+  async saveToken(user, client, authorizationCode, scope) {
+    const validatedScope = await this.validateScope(user, client, scope);
+    const accessToken = await this.generateAccessToken(client, user, scope);
+    const refreshToken = await this.generateRefreshToken(client, user, scope);
+    const accessTokenExpiresAt = await this.getAccessTokenExpiresAt();
+    const refreshTokenExpiresAt = await this.getRefreshTokenExpiresAt();
+
+    const token = {
+      accessToken: accessToken,
+      authorizationCode: authorizationCode,
+      accessTokenExpiresAt: accessTokenExpiresAt,
+      refreshToken: refreshToken,
+      refreshTokenExpiresAt: refreshTokenExpiresAt,
+      scope: validatedScope,
+    };
+
+    return this.model.saveToken(token, client, user);
   }
 }
 
diff --git a/lib/grant-types/client-credentials-grant-type.js b/lib/grant-types/client-credentials-grant-type.js
index 4b1aec5..a4ae1ed 100644
--- a/lib/grant-types/client-credentials-grant-type.js
+++ b/lib/grant-types/client-credentials-grant-type.js
@@ -7,8 +7,6 @@
 const AbstractGrantType = require('./abstract-grant-type');
 const InvalidArgumentError = require('../errors/invalid-argument-error');
 const InvalidGrantError = require('../errors/invalid-grant-error');
-const Promise = require('bluebird');
-const promisify = require('promisify-any').use(Promise);
 
 /**
  * Constructor.
@@ -37,7 +35,7 @@ class ClientCredentialsGrantType extends AbstractGrantType {
 	 * @see https://tools.ietf.org/html/rfc6749#section-4.4.2
 	 */
 
-  handle(request, client) {
+  async handle(request, client) {
     if (!request) {
       throw new InvalidArgumentError('Missing parameter: `request`');
     }
@@ -47,54 +45,44 @@ class ClientCredentialsGrantType extends AbstractGrantType {
     }
 
     const scope = this.getScope(request);
+    const user = this.getUserFromClient(client);
 
-    return Promise.bind(this)
-      .then(function () {
-        return this.getUserFromClient(client);
-      })
-      .then(function (user) {
-        return this.saveToken(user, client, scope);
-      });
+    return this.saveToken(user, client, scope);
   }
 
   /**
 	 * Retrieve the user using client credentials.
 	 */
 
-  getUserFromClient(client) {
-    return promisify(this.model.getUserFromClient, 1)
-      .call(this.model, client)
-      .then((user) => {
-        if (!user) {
-          throw new InvalidGrantError('Invalid grant: user credentials are invalid');
-        }
+  async getUserFromClient(client) {
+    const user = await this.model.getUserFromClient(client);
 
-        return user;
-      });
+    if (!user) {
+      throw new InvalidGrantError('Invalid grant: user credentials are invalid');
+    }
+
+    return user;
   }
 
   /**
 	 * Save token.
 	 */
 
-  saveToken(user, client, scope) {
+  async saveToken(user, client, scope) {
     const fns = [
       this.validateScope(user, client, scope),
       this.generateAccessToken(client, user, scope),
       this.getAccessTokenExpiresAt(client, user, scope),
     ];
 
-    return Promise.all(fns)
-      .bind(this)
-      .spread(function (scope, accessToken, accessTokenExpiresAt) {
-        const token = {
-          accessToken: accessToken,
-          accessTokenExpiresAt: accessTokenExpiresAt,
-          scope: scope,
-        };
-
-        return promisify(this.model.saveToken, 3).call(this.model, token, client, user);
-      });
+    const [validatedScope, accessToken, accessTokenExpiresAt] = await Promise.all(fns);
+    const token = {
+      accessToken: accessToken,
+      accessTokenExpiresAt: accessTokenExpiresAt,
+      scope: validatedScope,
+    };
+
+    return this.model.saveToken(token, client, user);
   }
 }
 
diff --git a/lib/grant-types/password-grant-type.js b/lib/grant-types/password-grant-type.js
index 3035cdd..f13b68a 100644
--- a/lib/grant-types/password-grant-type.js
+++ b/lib/grant-types/password-grant-type.js
@@ -8,8 +8,6 @@ const AbstractGrantType = require('./abstract-grant-type');
 const InvalidArgumentError = require('../errors/invalid-argument-error');
 const InvalidGrantError = require('../errors/invalid-grant-error');
 const InvalidRequestError = require('../errors/invalid-request-error');
-const Promise = require('bluebird');
-const promisify = require('promisify-any').use(Promise);
 const isFormat = require('@node-oauth/formats');
 
 /**
@@ -39,7 +37,7 @@ class PasswordGrantType extends AbstractGrantType {
 	 * @see https://tools.ietf.org/html/rfc6749#section-4.3.2
 	 */
 
-  handle(request, client) {
+  async handle(request, client) {
     if (!request) {
       throw new InvalidArgumentError('Missing parameter: `request`');
     }
@@ -49,21 +47,16 @@ class PasswordGrantType extends AbstractGrantType {
     }
 
     const scope = this.getScope(request);
+    const user = await this.getUser(request);
 
-    return Promise.bind(this)
-      .then(function () {
-        return this.getUser(request);
-      })
-      .then(function (user) {
-        return this.saveToken(user, client, scope);
-      });
+    return this.saveToken(user, client, scope);
   }
 
   /**
 	 * Get user using a username/password combination.
 	 */
 
-  getUser(request) {
+  async getUser(request) {
     if (!request.body.username) {
       throw new InvalidRequestError('Missing parameter: `username`');
     }
@@ -80,43 +73,35 @@ class PasswordGrantType extends AbstractGrantType {
       throw new InvalidRequestError('Invalid parameter: `password`');
     }
 
-    return promisify(this.model.getUser, 2)
-      .call(this.model, request.body.username, request.body.password)
-      .then((user) => {
-        if (!user) {
-          throw new InvalidGrantError('Invalid grant: user credentials are invalid');
-        }
+    const user = await this.model.getUser(request.body.username, request.body.password);
 
-        return user;
-      });
+    if (!user) {
+      throw new InvalidGrantError('Invalid grant: user credentials are invalid');
+    }
+
+    return user;
   }
 
   /**
 	 * Save token.
 	 */
 
-  saveToken(user, client, scope) {
-    const fns = [
-      this.validateScope(user, client, scope),
-      this.generateAccessToken(client, user, scope),
-      this.generateRefreshToken(client, user, scope),
-      this.getAccessTokenExpiresAt(),
-      this.getRefreshTokenExpiresAt(),
-    ];
-
-    return Promise.all(fns)
-      .bind(this)
-      .spread(function (scope, accessToken, refreshToken, accessTokenExpiresAt, refreshTokenExpiresAt) {
-        const token = {
-          accessToken: accessToken,
-          accessTokenExpiresAt: accessTokenExpiresAt,
-          refreshToken: refreshToken,
-          refreshTokenExpiresAt: refreshTokenExpiresAt,
-          scope: scope,
-        };
-
-        return promisify(this.model.saveToken, 3).call(this.model, token, client, user);
-      });
+  async saveToken(user, client, scope) {
+    const validatedScope = await this.validateScope(user, client, scope);
+    const accessToken = await this.generateAccessToken(client, user, scope);
+    const refreshToken = await this.generateRefreshToken(client, user, scope);
+    const accessTokenExpiresAt = await this.getAccessTokenExpiresAt();
+    const refreshTokenExpiresAt = await this.getRefreshTokenExpiresAt();
+
+    const token = {
+      accessToken: accessToken,
+      accessTokenExpiresAt: accessTokenExpiresAt,
+      refreshToken: refreshToken,
+      refreshTokenExpiresAt: refreshTokenExpiresAt,
+      scope: validatedScope,
+    };
+
+    return this.model.saveToken(token, client, user);
   }
 }
 
diff --git a/lib/grant-types/refresh-token-grant-type.js b/lib/grant-types/refresh-token-grant-type.js
index d94caca..b9e89a2 100644
--- a/lib/grant-types/refresh-token-grant-type.js
+++ b/lib/grant-types/refresh-token-grant-type.js
@@ -8,8 +8,6 @@ const AbstractGrantType = require('./abstract-grant-type');
 const InvalidArgumentError = require('../errors/invalid-argument-error');
 const InvalidGrantError = require('../errors/invalid-grant-error');
 const InvalidRequestError = require('../errors/invalid-request-error');
-const Promise = require('bluebird');
-const promisify = require('promisify-any').use(Promise);
 const ServerError = require('../errors/server-error');
 const isFormat = require('@node-oauth/formats');
 
@@ -44,7 +42,7 @@ class RefreshTokenGrantType extends AbstractGrantType {
 	 * @see https://tools.ietf.org/html/rfc6749#section-6
 	 */
 
-  handle(request, client) {
+  async handle(request, client) {
     if (!request) {
       throw new InvalidArgumentError('Missing parameter: `request`');
     }
@@ -53,23 +51,18 @@ class RefreshTokenGrantType extends AbstractGrantType {
       throw new InvalidArgumentError('Missing parameter: `client`');
     }
 
-    return Promise.bind(this)
-      .then(function () {
-        return this.getRefreshToken(request, client);
-      })
-      .tap(function (token) {
-        return this.revokeToken(token);
-      })
-      .then(function (token) {
-        return this.saveToken(token.user, client, token.scope);
-      });
+    let token;
+    token = await this.getRefreshToken(request, client);
+    token = await this.revokeToken(token);
+
+    return this.saveToken(token.user, client, token.scope);
   }
 
   /**
 	 * Get refresh token.
 	 */
 
-  getRefreshToken(request, client) {
+  async getRefreshToken(request, client) {
     if (!request.body.refresh_token) {
       throw new InvalidRequestError('Missing parameter: `refresh_token`');
     }
@@ -78,35 +71,33 @@ class RefreshTokenGrantType extends AbstractGrantType {
       throw new InvalidRequestError('Invalid parameter: `refresh_token`');
     }
 
-    return promisify(this.model.getRefreshToken, 1)
-      .call(this.model, request.body.refresh_token)
-      .then((token) => {
-        if (!token) {
-          throw new InvalidGrantError('Invalid grant: refresh token is invalid');
-        }
+    const token = await this.model.getRefreshToken(request.body.refresh_token);
+
+    if (!token) {
+      throw new InvalidGrantError('Invalid grant: refresh token is invalid');
+    }
 
-        if (!token.client) {
-          throw new ServerError('Server error: `getRefreshToken()` did not return a `client` object');
-        }
+    if (!token.client) {
+      throw new ServerError('Server error: `getRefreshToken()` did not return a `client` object');
+    }
 
-        if (!token.user) {
-          throw new ServerError('Server error: `getRefreshToken()` did not return a `user` object');
-        }
+    if (!token.user) {
+      throw new ServerError('Server error: `getRefreshToken()` did not return a `user` object');
+    }
 
-        if (token.client.id !== client.id) {
-          throw new InvalidGrantError('Invalid grant: refresh token was issued to another client');
-        }
+    if (token.client.id !== client.id) {
+      throw new InvalidGrantError('Invalid grant: refresh token was issued to another client');
+    }
 
-        if (token.refreshTokenExpiresAt && !(token.refreshTokenExpiresAt instanceof Date)) {
-          throw new ServerError('Server error: `refreshTokenExpiresAt` must be a Date instance');
-        }
+    if (token.refreshTokenExpiresAt && !(token.refreshTokenExpiresAt instanceof Date)) {
+      throw new ServerError('Server error: `refreshTokenExpiresAt` must be a Date instance');
+    }
 
-        if (token.refreshTokenExpiresAt && token.refreshTokenExpiresAt < new Date()) {
-          throw new InvalidGrantError('Invalid grant: refresh token has expired');
-        }
+    if (token.refreshTokenExpiresAt && token.refreshTokenExpiresAt < new Date()) {
+      throw new InvalidGrantError('Invalid grant: refresh token has expired');
+    }
 
-        return token;
-      });
+    return token;
   }
 
   /**
@@ -115,55 +106,41 @@ class RefreshTokenGrantType extends AbstractGrantType {
 	 * @see https://tools.ietf.org/html/rfc6749#section-6
 	 */
 
-  revokeToken(token) {
+  async revokeToken(token) {
     if (this.alwaysIssueNewRefreshToken === false) {
-      return Promise.resolve(token);
+      return token;
     }
 
-    return promisify(this.model.revokeToken, 1)
-      .call(this.model, token)
-      .then((status) => {
-        if (!status) {
-          throw new InvalidGrantError('Invalid grant: refresh token is invalid or could not be revoked');
-        }
+    const status = await this.model.revokeToken(token);
+
+    if (!status) {
+      throw new InvalidGrantError('Invalid grant: refresh token is invalid or could not be revoked');
+    }
 
-        return token;
-      });
+    return token;
   }
 
   /**
 	 * Save token.
 	 */
 
-  saveToken(user, client, scope) {
-    const fns = [
-      this.generateAccessToken(client, user, scope),
-      this.generateRefreshToken(client, user, scope),
-      this.getAccessTokenExpiresAt(),
-      this.getRefreshTokenExpiresAt(),
-    ];
-
-    return Promise.all(fns)
-      .bind(this)
-      .spread(function (accessToken, refreshToken, accessTokenExpiresAt, refreshTokenExpiresAt) {
-        const token = {
-          accessToken: accessToken,
-          accessTokenExpiresAt: accessTokenExpiresAt,
-          scope: scope,
-        };
-
-        if (this.alwaysIssueNewRefreshToken !== false) {
-          token.refreshToken = refreshToken;
-          token.refreshTokenExpiresAt = refreshTokenExpiresAt;
-        }
-
-        return token;
-      })
-      .then(function (token) {
-        return promisify(this.model.saveToken, 3)
-          .call(this.model, token, client, user)
-          .then((savedToken) => savedToken);
-      });
+  async saveToken(user, client, scope) {
+    const accessToken = await this.generateAccessToken(client, user, scope);
+    const refreshToken = await this.generateRefreshToken(client, user, scope);
+    const accessTokenExpiresAt = await this.getAccessTokenExpiresAt();
+    const refreshTokenExpiresAt = await this.getRefreshTokenExpiresAt();
+    const token = {
+      accessToken: accessToken,
+      accessTokenExpiresAt: accessTokenExpiresAt,
+      scope: scope,
+    };
+
+    if (this.alwaysIssueNewRefreshToken !== false) {
+      token.refreshToken = refreshToken;
+      token.refreshTokenExpiresAt = refreshTokenExpiresAt;
+    }
+
+    return this.model.saveToken(token, client, user);
   }
 }
 
diff --git a/lib/handlers/authenticate-handler.js b/lib/handlers/authenticate-handler.js
index 7724742..1da50f9 100644
--- a/lib/handlers/authenticate-handler.js
+++ b/lib/handlers/authenticate-handler.js
@@ -9,8 +9,6 @@ const InvalidRequestError = require('../errors/invalid-request-error');
 const InsufficientScopeError = require('../errors/insufficient-scope-error');
 const InvalidTokenError = require('../errors/invalid-token-error');
 const OAuthError = require('../errors/oauth-error');
-const Promise = require('bluebird');
-const promisify = require('promisify-any').use(Promise);
 const Request = require('../request');
 const Response = require('../response');
 const ServerError = require('../errors/server-error');
@@ -54,7 +52,7 @@ function AuthenticateHandler(options) {
  * Authenticate Handler.
  */
 
-AuthenticateHandler.prototype.handle = function(request, response) {
+AuthenticateHandler.prototype.handle = async function(request, response) {
   if (!(request instanceof Request)) {
     throw new InvalidArgumentError('Invalid argument: `request` must be an instance of Request');
   }
@@ -63,47 +61,41 @@ AuthenticateHandler.prototype.handle = function(request, response) {
     throw new InvalidArgumentError('Invalid argument: `response` must be an instance of Response');
   }
 
-  return Promise.bind(this)
-    .then(function() {
-      return this.getTokenFromRequest(request);
-    })
-    .then(function(token) {
-      return this.getAccessToken(token);
-    })
-    .tap(function(token) {
-      return this.validateAccessToken(token);
-    })
-    .tap(function(token) {
-      if (!this.scope) {
-        return;
-      }
-
-      return this.verifyScope(token);
-    })
-    .tap(function(token) {
-      return this.updateResponse(response, token);
-    })
-    .catch(function(e) {
-      // Include the "WWW-Authenticate" response header field if the client
-      // lacks any authentication information.
-      //
-      // @see https://tools.ietf.org/html/rfc6750#section-3.1
-      if (e instanceof UnauthorizedRequestError) {
-        response.set('WWW-Authenticate', 'Bearer realm="Service"');
-      } else if (e instanceof InvalidRequestError) {
-        response.set('WWW-Authenticate', 'Bearer realm="Service",error="invalid_request"');
-      } else if (e instanceof InvalidTokenError) {
-        response.set('WWW-Authenticate', 'Bearer realm="Service",error="invalid_token"');
-      } else if (e instanceof InsufficientScopeError) {
-        response.set('WWW-Authenticate', 'Bearer realm="Service",error="insufficient_scope"');
-      }
-
-      if (!(e instanceof OAuthError)) {
-        throw new ServerError(e);
-      }
-
-      throw e;
-    });
+  try {
+    const requestToken = await this.getTokenFromRequest(request);
+
+    let accessToken;
+    accessToken = await this.getAccessToken(requestToken);
+    accessToken = await this.validateAccessToken(accessToken);
+
+    if (this.scope) {
+      await this.verifyScope(accessToken);
+    }
+
+    this.updateResponse(response, accessToken);
+
+    return accessToken;
+  } catch (e) {
+    // Include the "WWW-Authenticate" response header field if the client
+    // lacks any authentication information.
+    //
+    // @see https://tools.ietf.org/html/rfc6750#section-3.1
+    if (e instanceof UnauthorizedRequestError) {
+      response.set('WWW-Authenticate', 'Bearer realm="Service"');
+    } else if (e instanceof InvalidRequestError) {
+      response.set('WWW-Authenticate', 'Bearer realm="Service",error="invalid_request"');
+    } else if (e instanceof InvalidTokenError) {
+      response.set('WWW-Authenticate', 'Bearer realm="Service",error="invalid_token"');
+    } else if (e instanceof InsufficientScopeError) {
+      response.set('WWW-Authenticate', 'Bearer realm="Service",error="insufficient_scope"');
+    }
+
+    if (!(e instanceof OAuthError)) {
+      throw new ServerError(e);
+    }
+
+    throw e;
+  }
 };
 
 /**
@@ -202,19 +194,18 @@ AuthenticateHandler.prototype.getTokenFromRequestBody = function(request) {
  * Get the access token from the model.
  */
 
-AuthenticateHandler.prototype.getAccessToken = function(token) {
-  return promisify(this.model.getAccessToken, 1).call(this.model, token)
-    .then(function(accessToken) {
-      if (!accessToken) {
-        throw new InvalidTokenError('Invalid token: access token is invalid');
-      }
+AuthenticateHandler.prototype.getAccessToken = async function(token) {
+  const accessToken = await this.model.getAccessToken(token);
+
+  if (!accessToken) {
+    throw new InvalidTokenError('Invalid token: access token is invalid');
+  }
 
-      if (!accessToken.user) {
-        throw new ServerError('Server error: `getAccessToken()` did not return a `user` object');
-      }
+  if (!accessToken.user) {
+    throw new ServerError('Server error: `getAccessToken()` did not return a `user` object');
+  }
 
-      return accessToken;
-    });
+  return accessToken;
 };
 
 /**
@@ -237,15 +228,14 @@ AuthenticateHandler.prototype.validateAccessToken = function(accessToken) {
  * Verify scope.
  */
 
-AuthenticateHandler.prototype.verifyScope = function(accessToken) {
-  return promisify(this.model.verifyScope, 2).call(this.model, accessToken, this.scope)
-    .then(function(scope) {
-      if (!scope) {
-        throw new InsufficientScopeError('Insufficient scope: authorized scope is insufficient');
-      }
+AuthenticateHandler.prototype.verifyScope = async function(accessToken) {
+  const scope = await this.model.verifyScope(accessToken, this.scope);
+
+  if (!scope) {
+    throw new InsufficientScopeError('Insufficient scope: authorized scope is insufficient');
+  }
 
-      return scope;
-    });
+  return scope;
 };
 
 /**
diff --git a/lib/handlers/authorize-handler.js b/lib/handlers/authorize-handler.js
index 57413e9..49d5515 100644
--- a/lib/handlers/authorize-handler.js
+++ b/lib/handlers/authorize-handler.js
@@ -12,8 +12,6 @@ const InvalidRequestError = require('../errors/invalid-request-error');
 const InvalidScopeError = require('../errors/invalid-scope-error');
 const UnsupportedResponseTypeError = require('../errors/unsupported-response-type-error');
 const OAuthError = require('../errors/oauth-error');
-const Promise = require('bluebird');
-const promisify = require('promisify-any').use(Promise);
 const Request = require('../request');
 const Response = require('../response');
 const ServerError = require('../errors/server-error');
@@ -69,7 +67,7 @@ function AuthorizeHandler(options) {
  * Authorize Handler.
  */
 
-AuthorizeHandler.prototype.handle = function(request, response) {
+AuthorizeHandler.prototype.handle = async function(request, response) {
   if (!(request instanceof Request)) {
     throw new InvalidArgumentError('Invalid argument: `request` must be an instance of Request');
   }
@@ -78,72 +76,65 @@ AuthorizeHandler.prototype.handle = function(request, response) {
     throw new InvalidArgumentError('Invalid argument: `response` must be an instance of Response');
   }
 
-  const fns = [
-    this.getAuthorizationCodeLifetime(),
-    this.getClient(request),
-    this.getUser(request, response)
-  ];
-
-  return Promise.all(fns)
-    .bind(this)
-    .spread(function(expiresAt, client, user) {
-      const uri = this.getRedirectUri(request, client);
-      let scope;
-      let state;
-      let ResponseType;
-
-      return Promise.bind(this)
-        .then(function() {
-          state = this.getState(request);
-          if (request.query.allowed === 'false' || request.body.allowed === 'false') {
-            throw new AccessDeniedError('Access denied: user denied access to application');
-          }
-        })
-        .then(function() {
-          const requestedScope = this.getScope(request);
-
-          return this.validateScope(user, client, requestedScope);
-        })
-        .then(function(validScope) {
-          scope = validScope;
-
-          return this.generateAuthorizationCode(client, user, scope);
-        })
-        .then(function(authorizationCode) {
-          ResponseType = this.getResponseType(request);
-          const codeChallenge = this.getCodeChallenge(request);
-          const codeChallengeMethod = this.getCodeChallengeMethod(request);
-
-          return this.saveAuthorizationCode(authorizationCode, expiresAt, scope, client, uri, user, codeChallenge, codeChallengeMethod);
-        })
-        .then(function(code) {
-          const responseType = new ResponseType(code.authorizationCode);
-          const redirectUri = this.buildSuccessRedirectUri(uri, responseType);
-
-          this.updateResponse(response, redirectUri, state);
-
-          return code;
-        })
-        .catch(function(e) {
-          if (!(e instanceof OAuthError)) {
-            e = new ServerError(e);
-          }
-          const redirectUri = this.buildErrorRedirectUri(uri, e);
-
-          this.updateResponse(response, redirectUri, state);
-
-          throw e;
-        });
-    });
+  const expiresAt = await this.getAuthorizationCodeLifetime();
+  const client = await this.getClient(request);
+  const user = await this.getUser(request, response);
+
+  let uri;
+  let state;
+
+  try {
+    uri = this.getRedirectUri(request, client);
+    state = this.getState(request);
+
+    if (request.query.allowed === 'false' || request.body.allowed === 'false') {
+      throw new AccessDeniedError('Access denied: user denied access to application');
+    }
+
+    const requestedScope = this.getScope(request);
+    const validScope = await this.validateScope(user, client, requestedScope);
+    const authorizationCode = this.generateAuthorizationCode(client, user, validScope);
+
+    const ResponseType = this.getResponseType(request);
+    const codeChallenge = this.getCodeChallenge(request);
+    const codeChallengeMethod = this.getCodeChallengeMethod(request);
+    const code = await this.saveAuthorizationCode(
+      authorizationCode,
+      expiresAt,
+      validScope,
+      client,
+      uri,
+      user,
+      codeChallenge,
+      codeChallengeMethod
+    );
+
+    const responseTypeInstance = new ResponseType(code.authorizationCode);
+    const redirectUri = this.buildSuccessRedirectUri(uri, responseTypeInstance);
+
+    this.updateResponse(response, redirectUri, state);
+
+    return code;
+  } catch (err) {
+    let e = err;
+
+    if (!(e instanceof OAuthError)) {
+      e = new ServerError(e);
+    }
+    const redirectUri = this.buildErrorRedirectUri(uri, e);
+    this.updateResponse(response, redirectUri, state);
+
+    throw e;
+  }
 };
 
 /**
  * Generate authorization code.
  */
 
-AuthorizeHandler.prototype.generateAuthorizationCode = function(client, user, scope) {
+AuthorizeHandler.prototype.generateAuthorizationCode = async function(client, user, scope) {
   if (this.model.generateAuthorizationCode) {
-    return promisify(this.model.generateAuthorizationCode, 3).call(this.model, client, user, scope);
+    return this.model.generateAuthorizationCode(client, user, scope);
   }
   return tokenUtil.generateRandomToken();
 };
@@ -163,7 +154,7 @@ AuthorizeHandler.prototype.getAuthorizationCodeLifetime = function() {
  * Get the client from the model.
  */
 
-AuthorizeHandler.prototype.getClient = function(request) {
+AuthorizeHandler.prototype.getClient = async function(request) {
   const self = this;
   const clientId = request.body.client_id || request.query.client_id;
 
@@ -180,54 +171,51 @@ AuthorizeHandler.prototype.getClient = function(request) {
   if (redirectUri && !isFormat.uri(redirectUri)) {
     throw new InvalidRequestError('Invalid request: `redirect_uri` is not a valid URI');
   }
-  return promisify(this.model.getClient, 2).call(this.model, clientId, null)
-    .then(function(client) {
-      if (!client) {
-        throw new InvalidClientError('Invalid client: client credentials are invalid');
-      }
-
-      if (!client.grants) {
-        throw new InvalidClientError('Invalid client: missing client `grants`');
-      }
-
-      if (!Array.isArray(client.grants) || !client.grants.includes('authorization_code')) {
-        throw new UnauthorizedClientError('Unauthorized client: `grant_type` is invalid');
-      }
-
-      if (!client.redirectUris || 0 === client.redirectUris.length) {
-        throw new InvalidClientError('Invalid client: missing client `redirectUri`');
-      }
-
-      if (redirectUri) {
-        return self.validateRedirectUri(redirectUri, client)
-          .then(function(valid) {
-            if (!valid) {
-              throw new InvalidClientError('Invalid client: `redirect_uri` does not match client value');
-            }
-            return client;
-          });
-      } else {
-        return client;
-      }
-    });
+
+  const client = await this.model.getClient(clientId, null);
+
+  if (!client) {
+    throw new InvalidClientError('Invalid client: client credentials are invalid');
+  }
+
+  if (!client.grants) {
+    throw new InvalidClientError('Invalid client: missing client `grants`');
+  }
+
+  if (!Array.isArray(client.grants) || !client.grants.includes('authorization_code')) {
+    throw new UnauthorizedClientError('Unauthorized client: `grant_type` is invalid');
+  }
+
+  if (!client.redirectUris || 0 === client.redirectUris.length) {
+    throw new InvalidClientError('Invalid client: missing client `redirectUri`');
+  }
+
+  if (redirectUri) {
+    const valid = await self.validateRedirectUri(redirectUri, client);
+
+    if (!valid) {
+      throw new InvalidClientError('Invalid client: `redirect_uri` does not match client value');
+    }
+  }
+
+  return client;
 };
 
 /**
  * Validate requested scope.
  */
-AuthorizeHandler.prototype.validateScope = function(user, client, scope) {
+AuthorizeHandler.prototype.validateScope = async function(user, client, scope) {
   if (this.model.validateScope) {
-    return promisify(this.model.validateScope, 3).call(this.model, user, client, scope)
-      .then(function (scope) {
-        if (!scope) {
-          throw new InvalidScopeError('Invalid scope: Requested scope is invalid');
-        }
-
-        return scope;
-      });
-  } else {
-    return Promise.resolve(scope);
+    const validatedScope = await this.model.validateScope(user, client, scope);
+
+    if (!validatedScope) {
+      throw new InvalidScopeError('Invalid scope: Requested scope is invalid');
+    }
+
+    return validatedScope;
   }
+
+  return scope;
 };
 
 /**
@@ -267,17 +255,21 @@ AuthorizeHandler.prototype.getState = function(request) {
  * Get user by calling the authenticate middleware.
  */
 
-AuthorizeHandler.prototype.getUser = function(request, response) {
+AuthorizeHandler.prototype.getUser = async function(request, response) {
   if (this.authenticateHandler instanceof AuthenticateHandler) {
-    return this.authenticateHandler.handle(request, response).get('user');
+    const handled = await this.authenticateHandler.handle(request, response);
+    return handled
+      ? handled.user
+      : undefined;
   }
-  return promisify(this.authenticateHandler.handle, 2)(request, response).then(function(user) {
-    if (!user) {
-      throw new ServerError('Server error: `handle()` did not return a `user` object');
-    }
 
-    return user;
-  });
+  const user = await this.authenticateHandler.handle(request, response);
+
+  if (!user) {
+    throw new ServerError('Server error: `handle()` did not return a `user` object');
+  }
+
+  return user;
 };
 
 /**
@@ -292,7 +284,7 @@ AuthorizeHandler.prototype.getRedirectUri = function(request, client) {
  * Save authorization code.
  */
 
-AuthorizeHandler.prototype.saveAuthorizationCode = function(authorizationCode, expiresAt, scope, client, redirectUri, user, codeChallenge, codeChallengeMethod) {
+AuthorizeHandler.prototype.saveAuthorizationCode = async function(authorizationCode, expiresAt, scope, client, redirectUri, user, codeChallenge, codeChallengeMethod) {
   let code = {
     authorizationCode: authorizationCode,
     expiresAt: expiresAt,
@@ -306,13 +298,14 @@ AuthorizeHandler.prototype.saveAuthorizationCode = function(authorizationCode, e
       codeChallengeMethod: codeChallengeMethod
     }, code);
   }
-  return promisify(this.model.saveAuthorizationCode, 3).call(this.model, code, client, user);
+
+  return this.model.saveAuthorizationCode(code, client, user);
 };
 
 
-AuthorizeHandler.prototype.validateRedirectUri = function(redirectUri, client) {
+AuthorizeHandler.prototype.validateRedirectUri = async function(redirectUri, client) {
   if (this.model.validateRedirectUri) {
-    return promisify(this.model.validateRedirectUri, 2).call(this.model, redirectUri, client);
+    return this.model.validateRedirectUri(redirectUri, client);
   }
 
   return Promise.resolve(client.redirectUris.includes(redirectUri));
diff --git a/lib/handlers/token-handler.js b/lib/handlers/token-handler.js
index 0f0c57a..4630c4f 100644
--- a/lib/handlers/token-handler.js
+++ b/lib/handlers/token-handler.js
@@ -9,8 +9,6 @@ const InvalidArgumentError = require('../errors/invalid-argument-error');
 const InvalidClientError = require('../errors/invalid-client-error');
 const InvalidRequestError = require('../errors/invalid-request-error');
 const OAuthError = require('../errors/oauth-error');
-const Promise = require('bluebird');
-const promisify = require('promisify-any').use(Promise);
 const Request = require('../request');
 const Response = require('../response');
 const ServerError = require('../errors/server-error');
@@ -68,7 +66,7 @@ function TokenHandler(options) {
  * Token Handler.
  */
 
-TokenHandler.prototype.handle = function(request, response) {
+TokenHandler.prototype.handle = async function(request, response) {
   if (!(request instanceof Request)) {
     throw new InvalidArgumentError('Invalid argument: `request` must be an instance of Request');
   }
@@ -85,35 +83,33 @@ TokenHandler.prototype.handle = function(request, response) {
     return Promise.reject(new InvalidRequestError('Invalid request: content must be application/x-www-form-urlencoded'));
   }
 
-  return Promise.bind(this)
-    .then(function() {
-      return this.getClient(request, response);
-    })
-    .then(function(client) {
-      return this.handleGrantType(request, client);
-    })
-    .tap(function(data) {
-      const model = new TokenModel(data, {allowExtendedTokenAttributes: this.allowExtendedTokenAttributes});
-      const tokenType = this.getTokenType(model);
-
-      this.updateSuccessResponse(response, tokenType);
-    }).catch(function(e) {
-      if (!(e instanceof OAuthError)) {
-        e = new ServerError(e);
-      }
-
-      this.updateErrorResponse(response, e);
-
-      throw e;
-    });
+  try {
+    const client = await this.getClient(request, response);
+    const data = await this.handleGrantType(request, client);
+    const model = new TokenModel(data, { allowExtendedTokenAttributes: this.allowExtendedTokenAttributes });
+    const tokenType = this.getTokenType(model);
+
+    this.updateSuccessResponse(response, tokenType);
+
+    return data;
+  } catch (err) {
+    let e = err;
+
+    if (!(e instanceof OAuthError)) {
+      e = new ServerError(e);
+    }
+
+    this.updateErrorResponse(response, e);
+    throw e;
+  }
 };
 
 /**
  * Get the client from the model.
  */
 
-TokenHandler.prototype.getClient = function(request, response) {
-  const credentials = this.getClientCredentials(request);
+TokenHandler.prototype.getClient = async function(request, response) {
+  const credentials = await this.getClientCredentials(request);
   const grantType = request.body.grant_type;
   const codeVerifier = request.body.code_verifier;
   const isPkce = pkce.isPKCERequest({ grantType, codeVerifier });
@@ -134,35 +130,34 @@ TokenHandler.prototype.getClient = function(request, response) {
     throw new InvalidRequestError('Invalid parameter: `client_secret`');
   }
 
-  return promisify(this.model.getClient, 2).call(this.model, credentials.clientId, credentials.clientSecret)
-    .then(function(client) {
-      if (!client) {
-        throw new InvalidClientError('Invalid client: client is invalid');
-      }
-
-      if (!client.grants) {
-        throw new ServerError('Server error: missing client `grants`');
-      }
-
-      if (!(client.grants instanceof Array)) {
-        throw new ServerError('Server error: `grants` must be an array');
-      }
-
-      return client;
-    })
-    .catch(function(e) {
-      // Include the "WWW-Authenticate" response header field if the client
-      // attempted to authenticate via the "Authorization" request header.
-      //
-      // @see https://tools.ietf.org/html/rfc6749#section-5.2.
-      if ((e instanceof InvalidClientError) && request.get('authorization')) {
-        response.set('WWW-Authenticate', 'Basic realm="Service"');
-
-        throw new InvalidClientError(e, { code: 401 });
-      }
-
-      throw e;
-    });
+  try {
+    const client = await this.model.getClient(credentials.clientId, credentials.clientSecret);
+
+    if (!client) {
+      throw new InvalidClientError('Invalid client: client is invalid');
+    }
+
+    if (!client.grants) {
+      throw new ServerError('Server error: missing client `grants`');
+    }
+
+    if (!(client.grants instanceof Array)) {
+      throw new ServerError('Server error: `grants` must be an array');
+    }
+
+    return client;
+  } catch (e) {
+    // Include the "WWW-Authenticate" response header field if the client
+    // attempted to authenticate via the "Authorization" request header.
+    //
+    // @see https://tools.ietf.org/html/rfc6749#section-5.2.
+    if ((e instanceof InvalidClientError) && request.get('authorization')) {
+      response.set('WWW-Authenticate', 'Basic realm="Service"');
+      throw new InvalidClientError(e, { code: 401 });
+    }
+
+    throw e;
+  }
 };
 
 /**
@@ -206,7 +201,7 @@ TokenHandler.prototype.getClientCredentials = function(request) {
  * Handle grant type.
  */
 
-TokenHandler.prototype.handleGrantType = function(request, client) {
+TokenHandler.prototype.handleGrantType = async function(request, client) {
   const grantType = request.body.grant_type;
 
   if (!grantType) {
@@ -236,8 +231,7 @@ TokenHandler.prototype.handleGrantType = function(request, client) {
     alwaysIssueNewRefreshToken: this.alwaysIssueNewRefreshToken
   };
 
-  return new Type(options)
-    .handle(request, client);
+  return new Type(options).handle(request, client);
 };
 
 /**
diff --git a/lib/server.js b/lib/server.js
index 53bbd2a..aca56d2 100644
--- a/lib/server.js
+++ b/lib/server.js
@@ -25,9 +25,10 @@ function OAuth2Server(options) {
 
 /**
  * Authenticate a token.
+ * Note, that callback will soon be deprecated!
  */
 
-OAuth2Server.prototype.authenticate = function(request, response, options, callback) {
+OAuth2Server.prototype.authenticate = function(request, response, options) {
   if (typeof options === 'string') {
     options = {scope: options};
   }
@@ -38,31 +39,27 @@ OAuth2Server.prototype.authenticate = function(request, response, options, callb
     allowBearerTokensInQueryString: false
   }, this.options, options);
 
-  return new AuthenticateHandler(options)
-    .handle(request, response)
-    .nodeify(callback);
+  return new AuthenticateHandler(options).handle(request, response);
 };
 
 /**
  * Authorize a request.
  */
 
-OAuth2Server.prototype.authorize = function(request, response, options, callback) {
+OAuth2Server.prototype.authorize = function(request, response, options) {
   options = Object.assign({
     allowEmptyState: false,
     authorizationCodeLifetime: 5 * 60   // 5 minutes.
   }, this.options, options);
 
-  return new AuthorizeHandler(options)
-    .handle(request, response)
-    .nodeify(callback);
+  return new AuthorizeHandler(options).handle(request, response);
 };
 
 /**
  * Create a token.
  */
 
-OAuth2Server.prototype.token = function(request, response, options, callback) {
+OAuth2Server.prototype.token = function(request, response, options) {
   options = Object.assign({
     accessTokenLifetime: 60 * 60,             // 1 hour.
     refreshTokenLifetime: 60 * 60 * 24 * 14,  // 2 weeks.
@@ -70,9 +67,7 @@ OAuth2Server.prototype.token = function(request, response, options, callback) {
     requireClientAuthentication: {}           // defaults to true for all grant types
   }, this.options, options);
 
-  return new TokenHandler(options)
-    .handle(request, response)
-    .nodeify(callback);
+  return new TokenHandler(options).handle(request, response);
 };
 
 /**
diff --git a/lib/utils/token-util.js b/lib/utils/token-util.js
index 8626dac..a1d6937 100644
--- a/lib/utils/token-util.js
+++ b/lib/utils/token-util.js
@@ -4,7 +4,7 @@
  * Module dependencies.
  */
 
-const randomBytes = require('bluebird').promisify(require('crypto').randomBytes);
+const randomBytes = require('crypto').randomBytes;
 const { createHash } = require('../utils/crypto-util');
 
 /**
@@ -17,10 +17,8 @@ module.exports = {
    * Generate random token.
    */
 
-  generateRandomToken: function() {
-    return randomBytes(256).then(function(buffer) {
-      return createHash({ data: buffer, encoding: 'hex' });
-    });
+  generateRandomToken: async function() {
+    const buffer = randomBytes(256);
+    return createHash({ data: buffer, encoding: 'hex' });
   }
-
 };
diff --git a/package.json b/package.json
index e95a0ce..2c26e6b 100644
--- a/package.json
+++ b/package.json
@@ -27,8 +27,6 @@
   "dependencies": {
     "@node-oauth/formats": "1.0.0",
     "basic-auth": "2.0.1",
-    "bluebird": "3.7.2",
-    "promisify-any": "2.0.1",
     "type-is": "1.6.18"
   },
   "devDependencies": {
diff --git a/test/integration/grant-types/abstract-grant-type_test.js b/test/integration/grant-types/abstract-grant-type_test.js
index a6c4d2b..5d9aa7a 100644
--- a/test/integration/grant-types/abstract-grant-type_test.js
+++ b/test/integration/grant-types/abstract-grant-type_test.js
@@ -6,7 +6,6 @@
 
 const AbstractGrantType = require('../../../lib/grant-types/abstract-grant-type');
 const InvalidArgumentError = require('../../../lib/errors/invalid-argument-error');
-const Promise = require('bluebird');
 const Request = require('../../../lib/request');
 const should = require('chai').should();
 
diff --git a/test/integration/grant-types/authorization-code-grant-type_test.js b/test/integration/grant-types/authorization-code-grant-type_test.js
index 6cddd53..85c007f 100644
--- a/test/integration/grant-types/authorization-code-grant-type_test.js
+++ b/test/integration/grant-types/authorization-code-grant-type_test.js
@@ -8,7 +8,6 @@ const AuthorizationCodeGrantType = require('../../../lib/grant-types/authorizati
 const InvalidArgumentError = require('../../../lib/errors/invalid-argument-error');
 const InvalidGrantError = require('../../../lib/errors/invalid-grant-error');
 const InvalidRequestError = require('../../../lib/errors/invalid-request-error');
-const Promise = require('bluebird');
 const Request = require('../../../lib/request');
 const ServerError = require('../../../lib/errors/server-error');
 const should = require('chai').should();
@@ -74,7 +73,7 @@ describe('AuthorizationCodeGrantType integration', function() {
   });
 
   describe('handle()', function() {
-    it('should throw an error if `request` is missing', function() {
+    it('should throw an error if `request` is missing', async function() {
       const model = {
         getAuthorizationCode: function() {},
         revokeAuthorizationCode: function() {},
@@ -83,9 +82,7 @@ describe('AuthorizationCodeGrantType integration', function() {
       const grantType = new AuthorizationCodeGrantType({ accessTokenLifetime: 123, model: model });
 
       try {
-        grantType.handle();
-
-        should.fail();
+        await grantType.handle();
       } catch (e) {
         e.should.be.an.instanceOf(InvalidArgumentError);
         e.message.should.equal('Missing parameter: `request`');
@@ -129,7 +126,7 @@ describe('AuthorizationCodeGrantType integration', function() {
       }
     });
 
-    it('should return a token', function() {
+    it('should return a token', async function() {
       const client = { id: 'foobar' };
       const token = {};
       const model = {
@@ -141,11 +138,8 @@ describe('AuthorizationCodeGrantType integration', function() {
       const grantType = new AuthorizationCodeGrantType({ accessTokenLifetime: 123, model: model });
       const request = new Request({ body: { code: 12345 }, headers: {}, method: {}, query: {} });
 
-      return grantType.handle(request, client)
-        .then(function(data) {
-          data.should.equal(token);
-        })
-        .catch(should.fail);
+      const data = await grantType.handle(request, client);
+      data.should.equal(token);
     });
 
     it('should support promises', function() {
@@ -173,23 +167,10 @@ describe('AuthorizationCodeGrantType integration', function() {
 
       grantType.handle(request, client).should.be.an.instanceOf(Promise);
     });
-
-    it('should support callbacks', function() {
-      const client = { id: 'foobar' };
-      const model = {
-        getAuthorizationCode: function(code, callback) { callback(null, { authorizationCode: 12345, client: { id: 'foobar' }, expiresAt: new Date(new Date() * 2), user: {} }); },
-        revokeAuthorizationCode: function(code, callback) { callback(null, { authorizationCode: 12345, client: { id: 'foobar' }, expiresAt: new Date(new Date() / 2), user: {} }); },
-        saveToken: function(tokenToSave, client, user, callback) { callback(null, tokenToSave); }
-      };
-      const grantType = new AuthorizationCodeGrantType({ accessTokenLifetime: 123, model: model });
-      const request = new Request({ body: { code: 12345 }, headers: {}, method: {}, query: {} });
-
-      grantType.handle(request, client).should.be.an.instanceOf(Promise);
-    });
   });
 
   describe('getAuthorizationCode()', function() {
-    it('should throw an error if the request body does not contain `code`', function() {
+    it('should throw an error if the request body does not contain `code`', async function() {
       const client = {};
       const model = {
         getAuthorizationCode: function() {},
@@ -200,16 +181,14 @@ describe('AuthorizationCodeGrantType integration', function() {
       const request = new Request({ body: {}, headers: {}, method: {}, query: {} });
 
       try {
-        grantType.getAuthorizationCode(request, client);
-
-        should.fail();
+        await grantType.getAuthorizationCode(request, client);
       } catch (e) {
         e.should.be.an.instanceOf(InvalidRequestError);
         e.message.should.equal('Missing parameter: `code`');
       }
     });
 
-    it('should throw an error if `code` is invalid', function() {
+    it('should throw an error if `code` is invalid', async function() {
       const client = {};
       const model = {
         getAuthorizationCode: function() {},
@@ -220,8 +199,7 @@ describe('AuthorizationCodeGrantType integration', function() {
       const request = new Request({ body: { code: 'øå€£‰' }, headers: {}, method: {}, query: {} });
 
       try {
-        grantType.getAuthorizationCode(request, client);
-
+        await grantType.getAuthorizationCode(request, client);
         should.fail();
       } catch (e) {
         e.should.be.an.instanceOf(InvalidRequestError);
@@ -229,7 +207,7 @@ describe('AuthorizationCodeGrantType integration', function() {
       }
     });
 
-    it('should throw an error if `authorizationCode` is missing', function() {
+    it('should throw an error if `authorizationCode` is missing', async function() {
       const client = {};
       const model = {
         getAuthorizationCode: function() {},
@@ -239,12 +217,13 @@ describe('AuthorizationCodeGrantType integration', function() {
       const grantType = new AuthorizationCodeGrantType({ accessTokenLifetime: 123, model: model });
       const request = new Request({ body: { code: 12345 }, headers: {}, method: {}, query: {} });
 
-      return grantType.getAuthorizationCode(request, client)
-        .then(should.fail)
-        .catch(function(e) {
-          e.should.be.an.instanceOf(InvalidGrantError);
-          e.message.should.equal('Invalid grant: authorization code is invalid');
-        });
+      try {
+        await grantType.getAuthorizationCode(request, client);
+        should.fail();
+      } catch (e) {
+        e.should.be.an.instanceOf(InvalidGrantError);
+        e.message.should.equal('Invalid grant: authorization code is invalid');
+      }
     });
 
     it('should throw an error if `authorizationCode.client` is missing', function() {
@@ -406,20 +385,6 @@ describe('AuthorizationCodeGrantType integration', function() {
 
       grantType.getAuthorizationCode(request, client).should.be.an.instanceOf(Promise);
     });
-
-    it('should support callbacks', function() {
-      const authorizationCode = { authorizationCode: 12345, client: { id: 'foobar' }, expiresAt: new Date(new Date() * 2), user: {} };
-      const client = { id: 'foobar' };
-      const model = {
-        getAuthorizationCode: function(code, callback) { callback(null, authorizationCode); },
-        revokeAuthorizationCode: function() {},
-        saveToken: function() {}
-      };
-      const grantType = new AuthorizationCodeGrantType({ accessTokenLifetime: 123, model: model });
-      const request = new Request({ body: { code: 12345 }, headers: {}, method: {}, query: {} });
-
-      grantType.getAuthorizationCode(request, client).should.be.an.instanceOf(Promise);
-    });
   });
 
   describe('validateRedirectUri()', function() {
@@ -523,18 +488,6 @@ describe('AuthorizationCodeGrantType integration', function() {
 
       grantType.revokeAuthorizationCode(authorizationCode).should.be.an.instanceOf(Promise);
     });
-
-    it('should support callbacks', function() {
-      const authorizationCode = { authorizationCode: 12345, client: {}, expiresAt: new Date(new Date() / 2), user: {} };
-      const model = {
-        getAuthorizationCode: function() {},
-        revokeAuthorizationCode: function(code, callback) { callback(null, authorizationCode); },
-        saveToken: function() {}
-      };
-      const grantType = new AuthorizationCodeGrantType({ accessTokenLifetime: 123, model: model });
-
-      grantType.revokeAuthorizationCode(authorizationCode).should.be.an.instanceOf(Promise);
-    });
   });
 
   describe('saveToken()', function() {
@@ -578,17 +531,5 @@ describe('AuthorizationCodeGrantType integration', function() {
 
       grantType.saveToken(token).should.be.an.instanceOf(Promise);
     });
-
-    it('should support callbacks', function() {
-      const token = {};
-      const model = {
-        getAuthorizationCode: function() {},
-        revokeAuthorizationCode: function() {},
-        saveToken: function(tokenToSave, client, user, callback) { callback(null, token); }
-      };
-      const grantType = new AuthorizationCodeGrantType({ accessTokenLifetime: 123, model: model });
-
-      grantType.saveToken(token).should.be.an.instanceOf(Promise);
-    });
   });
 });
diff --git a/test/integration/grant-types/client-credentials-grant-type_test.js b/test/integration/grant-types/client-credentials-grant-type_test.js
index b13df08..20c2da4 100644
--- a/test/integration/grant-types/client-credentials-grant-type_test.js
+++ b/test/integration/grant-types/client-credentials-grant-type_test.js
@@ -7,7 +7,6 @@
 const ClientCredentialsGrantType = require('../../../lib/grant-types/client-credentials-grant-type');
 const InvalidArgumentError = require('../../../lib/errors/invalid-argument-error');
 const InvalidGrantError = require('../../../lib/errors/invalid-grant-error');
-const Promise = require('bluebird');
 const Request = require('../../../lib/request');
 const should = require('chai').should();
 
@@ -56,7 +55,7 @@ describe('ClientCredentialsGrantType integration', function() {
   });
 
   describe('handle()', function() {
-    it('should throw an error if `request` is missing', function() {
+    it('should throw an error if `request` is missing', async function() {
       const model = {
         getUserFromClient: function() {},
         saveToken: function() {}
@@ -64,7 +63,7 @@ describe('ClientCredentialsGrantType integration', function() {
       const grantType = new ClientCredentialsGrantType({ accessTokenLifetime: 120, model: model });
 
       try {
-        grantType.handle();
+        await grantType.handle();
 
         should.fail();
       } catch (e) {
@@ -73,7 +72,7 @@ describe('ClientCredentialsGrantType integration', function() {
       }
     });
 
-    it('should throw an error if `client` is missing', function() {
+    it('should throw an error if `client` is missing', async function() {
       const model = {
         getUserFromClient: function() {},
         saveToken: function() {}
@@ -82,7 +81,7 @@ describe('ClientCredentialsGrantType integration', function() {
       const request = new Request({ body: {}, headers: {}, method: {}, query: {} });
 
       try {
-        grantType.handle(request);
+        await grantType.handle(request);
 
         should.fail();
       } catch (e) {
@@ -189,18 +188,6 @@ describe('ClientCredentialsGrantType integration', function() {
 
       grantType.getUserFromClient(request, {}).should.be.an.instanceOf(Promise);
     });
-
-    it('should support callbacks', function() {
-      const user = { email: 'foo@bar.com' };
-      const model = {
-        getUserFromClient: function(userId, callback) { callback(null, user); },
-        saveToken: function() {}
-      };
-      const grantType = new ClientCredentialsGrantType({ accessTokenLifetime: 120, model: model });
-      const request = new Request({ body: {}, headers: {}, method: {}, query: {} });
-
-      grantType.getUserFromClient(request, {}).should.be.an.instanceOf(Promise);
-    });
   });
 
   describe('saveToken()', function() {
@@ -241,16 +228,5 @@ describe('ClientCredentialsGrantType integration', function() {
 
       grantType.saveToken(token).should.be.an.instanceOf(Promise);
     });
-
-    it('should support callbacks', function() {
-      const token = {};
-      const model = {
-        getUserFromClient: function() {},
-        saveToken: function(tokenToSave, client, user, callback) { callback(null, token); }
-      };
-      const grantType = new ClientCredentialsGrantType({ accessTokenLifetime: 123, model: model });
-
-      grantType.saveToken(token).should.be.an.instanceOf(Promise);
-    });
   });
 });
diff --git a/test/integration/grant-types/password-grant-type_test.js b/test/integration/grant-types/password-grant-type_test.js
index a8c4cda..11dc3a3 100644
--- a/test/integration/grant-types/password-grant-type_test.js
+++ b/test/integration/grant-types/password-grant-type_test.js
@@ -8,7 +8,6 @@ const InvalidArgumentError = require('../../../lib/errors/invalid-argument-error
 const InvalidGrantError = require('../../../lib/errors/invalid-grant-error');
 const InvalidRequestError = require('../../../lib/errors/invalid-request-error');
 const PasswordGrantType = require('../../../lib/grant-types/password-grant-type');
-const Promise = require('bluebird');
 const Request = require('../../../lib/request');
 const should = require('chai').should();
 
@@ -57,7 +56,7 @@ describe('PasswordGrantType integration', function() {
   });
 
   describe('handle()', function() {
-    it('should throw an error if `request` is missing', function() {
+    it('should throw an error if `request` is missing', async function() {
       const model = {
         getUser: function() {},
         saveToken: function() {}
@@ -65,7 +64,7 @@ describe('PasswordGrantType integration', function() {
       const grantType = new PasswordGrantType({ accessTokenLifetime: 123, model: model });
 
       try {
-        grantType.handle();
+        await grantType.handle();
 
         should.fail();
       } catch (e) {
@@ -74,7 +73,7 @@ describe('PasswordGrantType integration', function() {
       }
     });
 
-    it('should throw an error if `client` is missing', function() {
+    it('should throw an error if `client` is missing', async function() {
       const model = {
         getUser: function() {},
         saveToken: function() {}
@@ -82,7 +81,7 @@ describe('PasswordGrantType integration', function() {
       const grantType = new PasswordGrantType({ accessTokenLifetime: 123, model: model });
 
       try {
-        grantType.handle({});
+        await grantType.handle({});
 
         should.fail();
       } catch (e) {
@@ -109,7 +108,7 @@ describe('PasswordGrantType integration', function() {
         .catch(should.fail);
     });
 
-    it('should support promises', function() {
+    it('should support promises', async function() {
       const client = { id: 'foobar' };
       const token = {};
       const model = {
@@ -119,10 +118,11 @@ describe('PasswordGrantType integration', function() {
       const grantType = new PasswordGrantType({ accessTokenLifetime: 123, model: model });
       const request = new Request({ body: { username: 'foo', password: 'bar' }, headers: {}, method: {}, query: {} });
 
-      grantType.handle(request, client).should.be.an.instanceOf(Promise);
+      const result = await grantType.handle(request, client);
+      result.should.deep.equal({});
     });
 
-    it('should support non-promises', function() {
+    it('should support non-promises', async function() {
       const client = { id: 'foobar' };
       const token = {};
       const model = {
@@ -132,25 +132,13 @@ describe('PasswordGrantType integration', function() {
       const grantType = new PasswordGrantType({ accessTokenLifetime: 123, model: model });
       const request = new Request({ body: { username: 'foo', password: 'bar' }, headers: {}, method: {}, query: {} });
 
-      grantType.handle(request, client).should.be.an.instanceOf(Promise);
-    });
-
-    it('should support callbacks', function() {
-      const client = { id: 'foobar' };
-      const token = {};
-      const model = {
-        getUser: function(username, password, callback) { callback(null, {}); },
-        saveToken: function(tokenToSave, client, user, callback) { callback(null, token); }
-      };
-      const grantType = new PasswordGrantType({ accessTokenLifetime: 123, model: model });
-      const request = new Request({ body: { username: 'foo', password: 'bar' }, headers: {}, method: {}, query: {} });
-
-      grantType.handle(request, client).should.be.an.instanceOf(Promise);
+      const result = await grantType.handle(request, client);
+      result.should.deep.equal({});
     });
   });
 
   describe('getUser()', function() {
-    it('should throw an error if the request body does not contain `username`', function() {
+    it('should throw an error if the request body does not contain `username`', async function() {
       const model = {
         getUser: function() {},
         saveToken: function() {}
@@ -159,7 +147,7 @@ describe('PasswordGrantType integration', function() {
       const request = new Request({ body: {}, headers: {}, method: {}, query: {} });
 
       try {
-        grantType.getUser(request);
+        await grantType.getUser(request);
 
         should.fail();
       } catch (e) {
@@ -168,7 +156,7 @@ describe('PasswordGrantType integration', function() {
       }
     });
 
-    it('should throw an error if the request body does not contain `password`', function() {
+    it('should throw an error if the request body does not contain `password`', async function() {
       const model = {
         getUser: function() {},
         saveToken: function() {}
@@ -177,7 +165,7 @@ describe('PasswordGrantType integration', function() {
       const request = new Request({ body: { username: 'foo' }, headers: {}, method: {}, query: {} });
 
       try {
-        grantType.getUser(request);
+        await grantType.getUser(request);
 
         should.fail();
       } catch (e) {
@@ -186,7 +174,7 @@ describe('PasswordGrantType integration', function() {
       }
     });
 
-    it('should throw an error if `username` is invalid', function() {
+    it('should throw an error if `username` is invalid', async function() {
       const model = {
         getUser: function() {},
         saveToken: function() {}
@@ -195,7 +183,7 @@ describe('PasswordGrantType integration', function() {
       const request = new Request({ body: { username: '\r\n', password: 'foobar' }, headers: {}, method: {}, query: {} });
 
       try {
-        grantType.getUser(request);
+        await grantType.getUser(request);
 
         should.fail();
       } catch (e) {
@@ -204,7 +192,7 @@ describe('PasswordGrantType integration', function() {
       }
     });
 
-    it('should throw an error if `password` is invalid', function() {
+    it('should throw an error if `password` is invalid', async function() {
       const model = {
         getUser: function() {},
         saveToken: function() {}
@@ -213,7 +201,7 @@ describe('PasswordGrantType integration', function() {
       const request = new Request({ body: { username: 'foobar', password: '\r\n' }, headers: {}, method: {}, query: {} });
 
       try {
-        grantType.getUser(request);
+        await grantType.getUser(request);
 
         should.fail();
       } catch (e) {
@@ -277,18 +265,6 @@ describe('PasswordGrantType integration', function() {
 
       grantType.getUser(request).should.be.an.instanceOf(Promise);
     });
-
-    it('should support callbacks', function() {
-      const user = { email: 'foo@bar.com' };
-      const model = {
-        getUser: function(username, password, callback) { callback(null, user); },
-        saveToken: function() {}
-      };
-      const grantType = new PasswordGrantType({ accessTokenLifetime: 123, model: model });
-      const request = new Request({ body: { username: 'foo', password: 'bar' }, headers: {}, method: {}, query: {} });
-
-      grantType.getUser(request).should.be.an.instanceOf(Promise);
-    });
   });
 
   describe('saveToken()', function() {
@@ -329,16 +305,5 @@ describe('PasswordGrantType integration', function() {
 
       grantType.saveToken(token).should.be.an.instanceOf(Promise);
     });
-
-    it('should support callbacks', function() {
-      const token = {};
-      const model = {
-        getUser: function() {},
-        saveToken: function(tokenToSave, client, user, callback) { callback(null, token); }
-      };
-      const grantType = new PasswordGrantType({ accessTokenLifetime: 123, model: model });
-
-      grantType.saveToken(token).should.be.an.instanceOf(Promise);
-    });
   });
 });
diff --git a/test/integration/grant-types/refresh-token-grant-type_test.js b/test/integration/grant-types/refresh-token-grant-type_test.js
index 83c7489..3273606 100644
--- a/test/integration/grant-types/refresh-token-grant-type_test.js
+++ b/test/integration/grant-types/refresh-token-grant-type_test.js
@@ -7,7 +7,6 @@
 const InvalidArgumentError = require('../../../lib/errors/invalid-argument-error');
 const InvalidGrantError = require('../../../lib/errors/invalid-grant-error');
 const InvalidRequestError = require('../../../lib/errors/invalid-request-error');
-const Promise = require('bluebird');
 const RefreshTokenGrantType = require('../../../lib/grant-types/refresh-token-grant-type');
 const Request = require('../../../lib/request');
 const ServerError = require('../../../lib/errors/server-error');
@@ -74,7 +73,7 @@ describe('RefreshTokenGrantType integration', function() {
   });
 
   describe('handle()', function() {
-    it('should throw an error if `request` is missing', function() {
+    it('should throw an error if `request` is missing', async function() {
       const model = {
         getRefreshToken: function() {},
         revokeToken: function() {},
@@ -83,7 +82,7 @@ describe('RefreshTokenGrantType integration', function() {
       const grantType = new RefreshTokenGrantType({ accessTokenLifetime: 120, model: model });
 
       try {
-        grantType.handle();
+        await grantType.handle();
 
         should.fail();
       } catch (e) {
@@ -92,7 +91,7 @@ describe('RefreshTokenGrantType integration', function() {
       }
     });
 
-    it('should throw an error if `client` is missing', function() {
+    it('should throw an error if `client` is missing', async function() {
       const model = {
         getRefreshToken: function() {},
         revokeToken: function() {},
@@ -102,7 +101,7 @@ describe('RefreshTokenGrantType integration', function() {
       const request = new Request({ body: {}, headers: {}, method: {}, query: {} });
 
       try {
-        grantType.handle(request);
+        await grantType.handle(request);
 
         should.fail();
       } catch (e) {
@@ -154,23 +153,10 @@ describe('RefreshTokenGrantType integration', function() {
 
       grantType.handle(request, client).should.be.an.instanceOf(Promise);
     });
-
-    it('should support callbacks', function() {
-      const client = { id: 123 };
-      const model = {
-        getRefreshToken: function(refreshToken, callback) { callback(null, { accessToken: 'foo', client: { id: 123 }, user: {} }); },
-        revokeToken: function(refreshToken, callback) { callback(null, { accessToken: 'foo', client: {}, refreshTokenExpiresAt: new Date(new Date() / 2), user: {} }); },
-        saveToken: function(tokenToSave, client, user, callback) { callback(null,{ accessToken: 'foo', client: {}, user: {} }); }
-      };
-      const grantType = new RefreshTokenGrantType({ accessTokenLifetime: 123, model: model });
-      const request = new Request({ body: { refresh_token: 'foobar' }, headers: {}, method: {}, query: {} });
-
-      grantType.handle(request, client).should.be.an.instanceOf(Promise);
-    });
   });
 
   describe('getRefreshToken()', function() {
-    it('should throw an error if the `refreshToken` parameter is missing from the request body', function() {
+    it('should throw an error if the `refreshToken` parameter is missing from the request body', async function() {
       const client = {};
       const model = {
         getRefreshToken: function() {},
@@ -181,7 +167,7 @@ describe('RefreshTokenGrantType integration', function() {
       const request = new Request({ body: {}, headers: {}, method: {}, query: {} });
 
       try {
-        grantType.getRefreshToken(request, client);
+        await grantType.getRefreshToken(request, client);
 
         should.fail();
       } catch (e) {
@@ -266,7 +252,7 @@ describe('RefreshTokenGrantType integration', function() {
         });
     });
 
-    it('should throw an error if `refresh_token` contains invalid characters', function() {
+    it('should throw an error if `refresh_token` contains invalid characters', async function() {
       const client = {};
       const model = {
         getRefreshToken: function() {
@@ -279,7 +265,7 @@ describe('RefreshTokenGrantType integration', function() {
       const request = new Request({ body: { refresh_token: 'øå€£‰' }, headers: {}, method: {}, query: {} });
 
       try {
-        grantType.getRefreshToken(request, client);
+        await grantType.getRefreshToken(request, client);
 
         should.fail();
       } catch (e) {
@@ -394,20 +380,6 @@ describe('RefreshTokenGrantType integration', function() {
 
       grantType.getRefreshToken(request, client).should.be.an.instanceOf(Promise);
     });
-
-    it('should support callbacks', function() {
-      const client = { id: 123 };
-      const token = { accessToken: 'foo', client: { id: 123 }, user: {} };
-      const model = {
-        getRefreshToken: function(refreshToken, callback) { callback(null, token); },
-        revokeToken: function() {},
-        saveToken: function() {}
-      };
-      const grantType = new RefreshTokenGrantType({ accessTokenLifetime: 123, model: model });
-      const request = new Request({ body: { refresh_token: 'foobar' }, headers: {}, method: {}, query: {} });
-
-      grantType.getRefreshToken(request, client).should.be.an.instanceOf(Promise);
-    });
   });
 
   describe('revokeToken()', function() {
@@ -466,18 +438,6 @@ describe('RefreshTokenGrantType integration', function() {
 
       grantType.revokeToken(token).should.be.an.instanceOf(Promise);
     });
-
-    it('should support callbacks', function() {
-      const token = { accessToken: 'foo', client: {}, refreshTokenExpiresAt: new Date(new Date() / 2), user: {} };
-      const model = {
-        getRefreshToken: function() {},
-        revokeToken: function(refreshToken, callback) { callback(null, token); },
-        saveToken: function() {}
-      };
-      const grantType = new RefreshTokenGrantType({ accessTokenLifetime: 123, model: model });
-
-      grantType.revokeToken(token).should.be.an.instanceOf(Promise);
-    });
   });
 
   describe('saveToken()', function() {
@@ -520,17 +480,5 @@ describe('RefreshTokenGrantType integration', function() {
 
       grantType.saveToken(token).should.be.an.instanceOf(Promise);
     });
-
-    it('should support callbacks', function() {
-      const token = {};
-      const model = {
-        getRefreshToken: function() {},
-        revokeToken: function() {},
-        saveToken: function(tokenToSave, client, user, callback) { callback(null, token); }
-      };
-      const grantType = new RefreshTokenGrantType({ accessTokenLifetime: 123, model: model });
-
-      grantType.saveToken(token).should.be.an.instanceOf(Promise);
-    });
   });
 });
diff --git a/test/integration/handlers/authenticate-handler_test.js b/test/integration/handlers/authenticate-handler_test.js
index 151ada3..d8bf728 100644
--- a/test/integration/handlers/authenticate-handler_test.js
+++ b/test/integration/handlers/authenticate-handler_test.js
@@ -10,7 +10,6 @@ const InvalidArgumentError = require('../../../lib/errors/invalid-argument-error
 const InvalidRequestError = require('../../../lib/errors/invalid-request-error');
 const InsufficientScopeError = require('../../../lib/errors/insufficient-scope-error');
 const InvalidTokenError = require('../../../lib/errors/invalid-token-error');
-const Promise = require('bluebird');
 const Request = require('../../../lib/request');
 const Response = require('../../../lib/response');
 const ServerError = require('../../../lib/errors/server-error');
@@ -102,11 +101,11 @@ describe('AuthenticateHandler integration', function() {
   });
 
   describe('handle()', function() {
-    it('should throw an error if `request` is missing', function() {
+    it('should throw an error if `request` is missing', async function() {
       const handler = new AuthenticateHandler({ model: { getAccessToken: function() {} } });
 
       try {
-        handler.handle();
+        await handler.handle();
 
         should.fail();
       } catch (e) {
@@ -115,7 +114,7 @@ describe('AuthenticateHandler integration', function() {
       }
     });
 
-    it('should set the `WWW-Authenticate` header if an unauthorized request error is thrown', function() {
+    it('should set the `WWW-Authenticate` header if an unauthorized request error is thrown', async function() {
       const model = {
         getAccessToken: function() {
           throw new UnauthorizedRequestError();
@@ -125,11 +124,12 @@ describe('AuthenticateHandler integration', function() {
       const request = new Request({ body: {}, headers: { 'Authorization': 'Bearer foo' }, method: {}, query: {} });
       const response = new Response({ body: {}, headers: {} });
 
-      return handler.handle(request, response)
-        .then(should.fail)
-        .catch(function() {
-          response.get('WWW-Authenticate').should.equal('Bearer realm="Service"');
-        });
+      try {
+        await handler.handle(request, response);
+        should.fail();
+      } catch (e) {
+        response.get('WWW-Authenticate').should.equal('Bearer realm="Service"');
+      }
     });
 
     it('should set the `WWW-Authenticate` header if an InvalidRequestError is thrown', function() {
@@ -250,7 +250,7 @@ describe('AuthenticateHandler integration', function() {
   });
 
   describe('getTokenFromRequest()', function() {
-    it('should throw an error if more than one authentication method is used', function() {
+    it('should throw an error if more than one authentication method is used', async function() {
       const handler = new AuthenticateHandler({ model: { getAccessToken: function() {} } });
       const request = new Request({
         body: {},
@@ -260,7 +260,7 @@ describe('AuthenticateHandler integration', function() {
       });
 
       try {
-        handler.getTokenFromRequest(request);
+        await handler.getTokenFromRequest(request);
 
         should.fail();
       } catch (e) {
@@ -269,12 +269,12 @@ describe('AuthenticateHandler integration', function() {
       }
     });
 
-    it('should throw an error if `accessToken` is missing', function() {
+    it('should throw an error if `accessToken` is missing', async function() {
       const handler = new AuthenticateHandler({ model: { getAccessToken: function() {} } });
       const request = new Request({ body: {}, headers: {}, method: {}, query: {} });
 
       try {
-        handler.getTokenFromRequest(request);
+        await handler.getTokenFromRequest(request);
 
         should.fail();
       } catch (e) {
@@ -285,7 +285,7 @@ describe('AuthenticateHandler integration', function() {
   });
 
   describe('getTokenFromRequestHeader()', function() {
-    it('should throw an error if the token is malformed', function() {
+    it('should throw an error if the token is malformed', async function() {
       const handler = new AuthenticateHandler({ model: { getAccessToken: function() {} } });
       const request = new Request({
         body: {},
@@ -297,7 +297,7 @@ describe('AuthenticateHandler integration', function() {
       });
 
       try {
-        handler.getTokenFromRequestHeader(request);
+        await handler.getTokenFromRequestHeader(request);
 
         should.fail();
       } catch (e) {
@@ -324,11 +324,11 @@ describe('AuthenticateHandler integration', function() {
   });
 
   describe('getTokenFromRequestQuery()', function() {
-    it('should throw an error if the query contains a token', function() {
+    it('should throw an error if the query contains a token', async function() {
       const handler = new AuthenticateHandler({ model: { getAccessToken: function() {} } });
 
       try {
-        handler.getTokenFromRequestQuery();
+        await handler.getTokenFromRequestQuery();
 
         should.fail();
       } catch (e) {
@@ -345,7 +345,7 @@ describe('AuthenticateHandler integration', function() {
   });
 
   describe('getTokenFromRequestBody()', function() {
-    it('should throw an error if the method is `GET`', function() {
+    it('should throw an error if the method is `GET`', async function() {
       const handler = new AuthenticateHandler({ model: { getAccessToken: function() {} } });
       const request = new Request({
         body: { access_token: 'foo' },
@@ -355,7 +355,7 @@ describe('AuthenticateHandler integration', function() {
       });
 
       try {
-        handler.getTokenFromRequestBody(request);
+        await handler.getTokenFromRequestBody(request);
 
         should.fail();
       } catch (e) {
@@ -364,7 +364,7 @@ describe('AuthenticateHandler integration', function() {
       }
     });
 
-    it('should throw an error if the media type is not `application/x-www-form-urlencoded`', function() {
+    it('should throw an error if the media type is not `application/x-www-form-urlencoded`', async function() {
       const handler = new AuthenticateHandler({ model: { getAccessToken: function() {} } });
       const request = new Request({
         body: { access_token: 'foo' },
@@ -374,7 +374,7 @@ describe('AuthenticateHandler integration', function() {
       });
 
       try {
-        handler.getTokenFromRequestBody(request);
+        await handler.getTokenFromRequestBody(request);
 
         should.fail();
       } catch (e) {
@@ -464,26 +464,15 @@ describe('AuthenticateHandler integration', function() {
 
       handler.getAccessToken('foo').should.be.an.instanceOf(Promise);
     });
-
-    it('should support callbacks', function() {
-      const model = {
-        getAccessToken: function(token, callback) {
-          callback(null, { user: {} });
-        }
-      };
-      const handler = new AuthenticateHandler({ model: model });
-
-      handler.getAccessToken('foo').should.be.an.instanceOf(Promise);
-    });
   });
 
   describe('validateAccessToken()', function() {
-    it('should throw an error if `accessToken` is expired', function() {
+    it('should throw an error if `accessToken` is expired', async function() {
       const accessToken = { accessTokenExpiresAt: new Date(new Date() / 2) };
       const handler = new AuthenticateHandler({ model: { getAccessToken: function() {} } });
 
       try {
-        handler.validateAccessToken(accessToken);
+        await handler.validateAccessToken(accessToken);
 
         should.fail();
       } catch (e) {
@@ -544,18 +533,6 @@ describe('AuthenticateHandler integration', function() {
 
       handler.verifyScope('foo').should.be.an.instanceOf(Promise);
     });
-
-    it('should support callbacks', function() {
-      const model = {
-        getAccessToken: function() {},
-        verifyScope: function(token, scope, callback) {
-          callback(null, true);
-        }
-      };
-      const handler = new AuthenticateHandler({ addAcceptedScopesHeader: true, addAuthorizedScopesHeader: true, model: model, scope: 'foo' });
-
-      handler.verifyScope('foo').should.be.an.instanceOf(Promise);
-    });
   });
 
   describe('updateResponse()', function() {
diff --git a/test/integration/handlers/authorize-handler_test.js b/test/integration/handlers/authorize-handler_test.js
index b91408a..8a5eb65 100644
--- a/test/integration/handlers/authorize-handler_test.js
+++ b/test/integration/handlers/authorize-handler_test.js
@@ -13,7 +13,6 @@ const InvalidClientError = require('../../../lib/errors/invalid-client-error');
 const InvalidRequestError = require('../../../lib/errors/invalid-request-error');
 const InvalidScopeError = require('../../../lib/errors/invalid-scope-error');
 const UnsupportedResponseTypeError = require('../../../lib/errors/unsupported-response-type-error');
-const Promise = require('bluebird');
 const Request = require('../../../lib/request');
 const Response = require('../../../lib/response');
 const ServerError = require('../../../lib/errors/server-error');
@@ -122,7 +121,7 @@ describe('AuthorizeHandler integration', function() {
   });
 
   describe('handle()', function() {
-    it('should throw an error if `request` is missing', function() {
+    it('should throw an error if `request` is missing', async function() {
       const model = {
         getAccessToken: function() {},
         getClient: function() {},
@@ -131,7 +130,7 @@ describe('AuthorizeHandler integration', function() {
       const handler = new AuthorizeHandler({ authorizationCodeLifetime: 120, model: model });
 
       try {
-        handler.handle();
+        await handler.handle();
 
         should.fail();
       } catch (e) {
@@ -140,7 +139,7 @@ describe('AuthorizeHandler integration', function() {
       }
     });
 
-    it('should throw an error if `response` is missing', function() {
+    it('should throw an error if `response` is missing', async function() {
       const model = {
         getAccessToken: function() {},
         getClient: function() {},
@@ -150,7 +149,7 @@ describe('AuthorizeHandler integration', function() {
       const request = new Request({ body: {}, headers: {}, method: {}, query: {} });
 
       try {
-        handler.handle(request);
+        await handler.handle(request);
 
         should.fail();
       } catch (e) {
@@ -695,25 +694,10 @@ describe('AuthorizeHandler integration', function() {
 
       handler.validateRedirectUri('http://example.com/a', { }).should.be.an.instanceOf(Promise);
     });
-
-    it('should support callbacks', function() {
-      const model = {
-        getAccessToken: function() {},
-        getClient: function() {},
-        saveAuthorizationCode: function() {},
-        validateRedirectUri: function(redirectUri, client, callback) {
-          callback(null, false);
-        }
-      };
-
-      const handler = new AuthorizeHandler({ authorizationCodeLifetime: 120, model: model });
-
-      handler.validateRedirectUri('http://example.com/a', { }).should.be.an.instanceOf(Promise);
-    });
   });
 
   describe('getClient()', function() {
-    it('should throw an error if `client_id` is missing', function() {
+    it('should throw an error if `client_id` is missing', async function() {
       const model = {
         getAccessToken: function() {},
         getClient: function() {},
@@ -723,7 +707,7 @@ describe('AuthorizeHandler integration', function() {
       const request = new Request({ body: { response_type: 'code' }, headers: {}, method: {}, query: {} });
 
       try {
-        handler.getClient(request);
+        await handler.getClient(request);
 
         should.fail();
       } catch (e) {
@@ -732,7 +716,7 @@ describe('AuthorizeHandler integration', function() {
       }
     });
 
-    it('should throw an error if `client_id` is invalid', function() {
+    it('should throw an error if `client_id` is invalid', async function() {
       const model = {
         getAccessToken: function() {},
         getClient: function() {},
@@ -742,7 +726,7 @@ describe('AuthorizeHandler integration', function() {
       const request = new Request({ body: { client_id: 'øå€£‰', response_type: 'code' }, headers: {}, method: {}, query: {} });
 
       try {
-        handler.getClient(request);
+        await handler.getClient(request);
 
         should.fail();
       } catch (e) {
@@ -751,7 +735,7 @@ describe('AuthorizeHandler integration', function() {
       }
     });
 
-    it('should throw an error if `client.redirectUri` is invalid', function() {
+    it('should throw an error if `client.redirectUri` is invalid', async function() {
       const model = {
         getAccessToken: function() {},
         getClient: function() {},
@@ -761,7 +745,7 @@ describe('AuthorizeHandler integration', function() {
       const request = new Request({ body: { client_id: 12345, response_type: 'code', redirect_uri: 'foobar' }, headers: {}, method: {}, query: {} });
 
       try {
-        handler.getClient(request);
+        await handler.getClient(request);
 
         should.fail();
       } catch (e) {
@@ -899,26 +883,6 @@ describe('AuthorizeHandler integration', function() {
       handler.getClient(request).should.be.an.instanceOf(Promise);
     });
 
-    it('should support callbacks', function() {
-      const model = {
-        getAccessToken: function() {},
-        getClient: function(clientId, clientSecret, callback) {
-          should.equal(clientSecret, null);
-          callback(null, { grants: ['authorization_code'], redirectUris: ['http://example.com/cb'] });
-        },
-        saveAuthorizationCode: function() {}
-      };
-      const handler = new AuthorizeHandler({ authorizationCodeLifetime: 120, model: model });
-      const request = new Request({
-        body: { client_id: 12345 },
-        headers: {},
-        method: {},
-        query: {}
-      });
-
-      handler.getClient(request).should.be.an.instanceOf(Promise);
-    });
-
     describe('with `client_id` in the request query', function() {
       it('should return a client', function() {
         const client = { grants: ['authorization_code'], redirectUris: ['http://example.com/cb'] };
@@ -942,7 +906,7 @@ describe('AuthorizeHandler integration', function() {
   });
 
   describe('getScope()', function() {
-    it('should throw an error if `scope` is invalid', function() {
+    it('should throw an error if `scope` is invalid', async function() {
       const model = {
         getAccessToken: function() {},
         getClient: function() {},
@@ -952,7 +916,7 @@ describe('AuthorizeHandler integration', function() {
       const request = new Request({ body: { scope: 'øå€£‰' }, headers: {}, method: {}, query: {} });
 
       try {
-        handler.getScope(request);
+        await handler.getScope(request);
 
         should.fail();
       } catch (e) {
@@ -991,7 +955,7 @@ describe('AuthorizeHandler integration', function() {
   });
 
   describe('getState()', function() {
-    it('should throw an error if `allowEmptyState` is false and `state` is missing', function() {
+    it('should throw an error if `allowEmptyState` is false and `state` is missing', async function() {
       const model = {
         getAccessToken: function() {},
         getClient: function() {},
@@ -1001,7 +965,7 @@ describe('AuthorizeHandler integration', function() {
       const request = new Request({ body: {}, headers: {}, method: {}, query: {} });
 
       try {
-        handler.getState(request);
+        await handler.getState(request);
 
         should.fail();
       } catch (e) {
@@ -1022,7 +986,7 @@ describe('AuthorizeHandler integration', function() {
       should.equal(state, undefined);
     });
 
-    it('should throw an error if `state` is invalid', function() {
+    it('should throw an error if `state` is invalid', async function() {
       const model = {
         getAccessToken: function() {},
         getClient: function() {},
@@ -1032,7 +996,7 @@ describe('AuthorizeHandler integration', function() {
       const request = new Request({ body: {}, headers: {}, method: {}, query: { state: 'øå€£‰' } });
 
       try {
-        handler.getState(request);
+        await handler.getState(request);
 
         should.fail();
       } catch (e) {
@@ -1157,23 +1121,10 @@ describe('AuthorizeHandler integration', function() {
 
       handler.saveAuthorizationCode('foo', 'bar', 'biz', 'baz').should.be.an.instanceOf(Promise);
     });
-
-    it('should support callbacks when calling `model.saveAuthorizationCode()`', function() {
-      const model = {
-        getAccessToken: function() {},
-        getClient: function() {},
-        saveAuthorizationCode: function(code, client, user, callback) {
-          return callback(null, true);
-        }
-      };
-      const handler = new AuthorizeHandler({ authorizationCodeLifetime: 120, model: model });
-
-      handler.saveAuthorizationCode('foo', 'bar', 'biz', 'baz').should.be.an.instanceOf(Promise);
-    });
   });
 
   describe('getResponseType()', function() {
-    it('should throw an error if `response_type` is missing', function() {
+    it('should throw an error if `response_type` is missing', async function() {
       const model = {
         getAccessToken: function() {},
         getClient: function() {},
@@ -1183,7 +1134,7 @@ describe('AuthorizeHandler integration', function() {
       const request = new Request({ body: {}, headers: {}, method: {}, query: {} });
 
       try {
-        handler.getResponseType(request);
+        await handler.getResponseType(request);
 
         should.fail();
       } catch (e) {
@@ -1192,7 +1143,7 @@ describe('AuthorizeHandler integration', function() {
       }
     });
 
-    it('should throw an error if `response_type` is not `code`', function() {
+    it('should throw an error if `response_type` is not `code`', async function() {
       const model = {
         getAccessToken: function() {},
         getClient: function() {},
@@ -1202,7 +1153,7 @@ describe('AuthorizeHandler integration', function() {
       const request = new Request({ body: { response_type: 'foobar' }, headers: {}, method: {}, query: {} });
 
       try {
-        handler.getResponseType(request);
+        await handler.getResponseType(request);
 
         should.fail();
       } catch (e) {
@@ -1326,7 +1277,7 @@ describe('AuthorizeHandler integration', function() {
       const request = new Request({ body: {code_challenge_method: 'foo'}, headers: {}, method: {}, query: {} });
 
       try {
-        handler.getCodeChallengeMethod(request);
+        await handler.getCodeChallengeMethod(request);
 
         should.fail();
       } catch (e) {
diff --git a/test/integration/handlers/token-handler_test.js b/test/integration/handlers/token-handler_test.js
index 0cb60c3..fd8f769 100644
--- a/test/integration/handlers/token-handler_test.js
+++ b/test/integration/handlers/token-handler_test.js
@@ -11,7 +11,6 @@ const InvalidClientError = require('../../../lib/errors/invalid-client-error');
 const InvalidGrantError = require('../../../lib/errors/invalid-grant-error');
 const InvalidRequestError = require('../../../lib/errors/invalid-request-error');
 const PasswordGrantType = require('../../../lib/grant-types/password-grant-type');
-const Promise = require('bluebird');
 const Request = require('../../../lib/request');
 const Response = require('../../../lib/response');
 const ServerError = require('../../../lib/errors/server-error');
@@ -149,7 +148,7 @@ describe('TokenHandler integration', function() {
   });
 
   describe('handle()', function() {
-    it('should throw an error if `request` is missing', function() {
+    it('should throw an error if `request` is missing', async function() {
       const model = {
         getClient: function() {},
         saveToken: function() {}
@@ -157,7 +156,7 @@ describe('TokenHandler integration', function() {
       const handler = new TokenHandler({ accessTokenLifetime: 120, model: model, refreshTokenLifetime: 120 });
 
       try {
-        handler.handle();
+        await handler.handle();
 
         should.fail();
       } catch (e) {
@@ -166,7 +165,7 @@ describe('TokenHandler integration', function() {
       }
     });
 
-    it('should throw an error if `response` is missing', function() {
+    it('should throw an error if `response` is missing', async function() {
       const model = {
         getClient: function() {},
         saveToken: function() {}
@@ -175,7 +174,7 @@ describe('TokenHandler integration', function() {
       const request = new Request({ body: {}, headers: {}, method: {}, query: {} });
 
       try {
-        handler.handle(request);
+        await handler.handle(request);
 
         should.fail();
       } catch (e) {
@@ -402,7 +401,7 @@ describe('TokenHandler integration', function() {
 
 
   describe('getClient()', function() {
-    it('should throw an error if `clientId` is invalid', function() {
+    it('should throw an error if `clientId` is invalid', async function() {
       const model = {
         getClient: function() {},
         saveToken: function() {}
@@ -411,7 +410,7 @@ describe('TokenHandler integration', function() {
       const request = new Request({ body: { client_id: 'øå€£‰', client_secret: 'foo' }, headers: {}, method: {}, query: {} });
 
       try {
-        handler.getClient(request);
+        await handler.getClient(request);
 
         should.fail();
       } catch (e) {
@@ -420,7 +419,7 @@ describe('TokenHandler integration', function() {
       }
     });
 
-    it('should throw an error if `clientSecret` is invalid', function() {
+    it('should throw an error if `clientSecret` is invalid', async function() {
       const model = {
         getClient: function() {},
         saveToken: function() {}
@@ -429,7 +428,7 @@ describe('TokenHandler integration', function() {
       const request = new Request({ body: { client_id: 'foo', client_secret: 'øå€£‰' }, headers: {}, method: {}, query: {} });
 
       try {
-        handler.getClient(request);
+        await handler.getClient(request);
 
         should.fail();
       } catch (e) {
@@ -607,21 +606,10 @@ describe('TokenHandler integration', function() {
 
       handler.getClient(request).should.be.an.instanceOf(Promise);
     });
-
-    it('should support callbacks', function() {
-      const model = {
-        getClient: function(clientId, clientSecret, callback) { callback(null, { grants: [] }); },
-        saveToken: function() {}
-      };
-      const handler = new TokenHandler({ accessTokenLifetime: 120, model: model, refreshTokenLifetime: 120 });
-      const request = new Request({ body: { client_id: 12345, client_secret: 'secret' }, headers: {}, method: {}, query: {} });
-
-      handler.getClient(request).should.be.an.instanceOf(Promise);
-    });
   });
 
   describe('getClientCredentials()', function() {
-    it('should throw an error if `client_id` is missing', function() {
+    it('should throw an error if `client_id` is missing', async function() {
       const model = {
         getClient: function() {},
         saveToken: function() {}
@@ -630,7 +618,7 @@ describe('TokenHandler integration', function() {
       const request = new Request({ body: { client_secret: 'foo' }, headers: {}, method: {}, query: {} });
 
       try {
-        handler.getClientCredentials(request);
+        await handler.getClientCredentials(request);
 
         should.fail();
       } catch (e) {
@@ -639,7 +627,7 @@ describe('TokenHandler integration', function() {
       }
     });
 
-    it('should throw an error if `client_secret` is missing', function() {
+    it('should throw an error if `client_secret` is missing', async function() {
       const model = {
         getClient: function() {},
         saveToken: function() {}
@@ -648,7 +636,7 @@ describe('TokenHandler integration', function() {
       const request = new Request({ body: { client_id: 'foo' }, headers: {}, method: {}, query: {} });
 
       try {
-        handler.getClientCredentials(request);
+        await handler.getClientCredentials(request);
 
         should.fail();
       } catch (e) {
@@ -708,7 +696,7 @@ describe('TokenHandler integration', function() {
   });
 
   describe('handleGrantType()', function() {
-    it('should throw an error if `grant_type` is missing', function() {
+    it('should throw an error if `grant_type` is missing', async function() {
       const model = {
         getClient: function() {},
         saveToken: function() {}
@@ -717,7 +705,7 @@ describe('TokenHandler integration', function() {
       const request = new Request({ body: {}, headers: {}, method: {}, query: {} });
 
       try {
-        handler.handleGrantType(request);
+        await handler.handleGrantType(request);
 
         should.fail();
       } catch (e) {
@@ -726,7 +714,7 @@ describe('TokenHandler integration', function() {
       }
     });
 
-    it('should throw an error if `grant_type` is invalid', function() {
+    it('should throw an error if `grant_type` is invalid', async function() {
       const model = {
         getClient: function() {},
         saveToken: function() {}
@@ -735,7 +723,7 @@ describe('TokenHandler integration', function() {
       const request = new Request({ body: { grant_type: '~foo~' }, headers: {}, method: {}, query: {} });
 
       try {
-        handler.handleGrantType(request);
+        await handler.handleGrantType(request);
 
         should.fail();
       } catch (e) {
@@ -744,7 +732,7 @@ describe('TokenHandler integration', function() {
       }
     });
 
-    it('should throw an error if `grant_type` is unsupported', function() {
+    it('should throw an error if `grant_type` is unsupported', async function() {
       const model = {
         getClient: function() {},
         saveToken: function() {}
@@ -753,7 +741,7 @@ describe('TokenHandler integration', function() {
       const request = new Request({ body: { grant_type: 'foobar' }, headers: {}, method: {}, query: {} });
 
       try {
-        handler.handleGrantType(request);
+        await handler.handleGrantType(request);
 
         should.fail();
       } catch (e) {
@@ -762,7 +750,7 @@ describe('TokenHandler integration', function() {
       }
     });
 
-    it('should throw an error if `grant_type` is unauthorized', function() {
+    it('should throw an error if `grant_type` is unauthorized', async function() {
       const client = { grants: ['client_credentials'] };
       const model = {
         getClient: function() {},
@@ -772,7 +760,7 @@ describe('TokenHandler integration', function() {
       const request = new Request({ body: { grant_type: 'password' }, headers: {}, method: {}, query: {} });
 
       try {
-        handler.handleGrantType(request, client);
+        await handler.handleGrantType(request, client);
 
         should.fail();
       } catch (e) {
@@ -784,8 +772,8 @@ describe('TokenHandler integration', function() {
     it('should throw an invalid grant error if a non-oauth error is thrown', function() {
       const client = { grants: ['password'] };
       const model = {
-        getClient: function(clientId, password, callback) { callback(null, client); },
-        getUser: function(uid, pwd, callback) { callback(); },
+        getClient: function(clientId, password) { return client; },
+        getUser: function(uid, pwd) {},
         saveToken: function() {}
       };
       const handler = new TokenHandler({ accessTokenLifetime: 120, model: model, refreshTokenLifetime: 120 });
diff --git a/test/integration/server_test.js b/test/integration/server_test.js
index db10544..cb717c7 100644
--- a/test/integration/server_test.js
+++ b/test/integration/server_test.js
@@ -5,7 +5,6 @@
  */
 
 const InvalidArgumentError = require('../../lib/errors/invalid-argument-error');
-const Promise = require('bluebird');
 const Request = require('../../lib/request');
 const Response = require('../../lib/response');
 const Server = require('../../lib/server');
@@ -37,7 +36,7 @@ describe('Server integration', function() {
   });
 
   describe('authenticate()', function() {
-    it('should set the default `options`', function() {
+    it('should set the default `options`', async function() {
       const model = {
         getAccessToken: function() {
           return {
@@ -50,35 +49,19 @@ describe('Server integration', function() {
       const request = new Request({ body: {}, headers: { 'Authorization': 'Bearer foo' }, method: {}, query: {} });
       const response = new Response({ body: {}, headers: {} });
 
-      return server.authenticate(request, response)
-        .then(function() {
-          this.addAcceptedScopesHeader.should.be.true;
-          this.addAuthorizedScopesHeader.should.be.true;
-          this.allowBearerTokensInQueryString.should.be.false;
-        })
-        .catch(should.fail);
+      try {
+        await server.authenticate(request, response);
+      } catch (e) {
+        server.addAcceptedScopesHeader.should.be.true;
+        server.addAuthorizedScopesHeader.should.be.true;
+        server.allowBearerTokensInQueryString.should.be.false;
+        should.fail();
+      }
     });
 
     it('should return a promise', function() {
       const model = {
-        getAccessToken: function(token, callback) {
-          callback(null, {
-            user: {},
-            accessTokenExpiresAt: new Date(new Date().getTime() + 10000)
-          });
-        }
-      };
-      const server = new Server({ model: model });
-      const request = new Request({ body: {}, headers: { 'Authorization': 'Bearer foo' }, method: {}, query: {} });
-      const response = new Response({ body: {}, headers: {} });
-      const handler = server.authenticate(request, response);
-
-      handler.should.be.an.instanceOf(Promise);
-    });
-
-    it('should support callbacks', function(next) {
-      const model = {
-        getAccessToken: function() {
+        getAccessToken: async function(token) {
           return {
             user: {},
             accessTokenExpiresAt: new Date(new Date().getTime() + 10000)
@@ -88,13 +71,14 @@ describe('Server integration', function() {
       const server = new Server({ model: model });
       const request = new Request({ body: {}, headers: { 'Authorization': 'Bearer foo' }, method: {}, query: {} });
       const response = new Response({ body: {}, headers: {} });
+      const handler = server.authenticate(request, response);
 
-      server.authenticate(request, response, null, next);
+      handler.should.be.an.instanceOf(Promise);
     });
   });
 
   describe('authorize()', function() {
-    it('should set the default `options`', function() {
+    it('should set the default `options`', async function() {
       const model = {
         getAccessToken: function() {
           return {
@@ -113,12 +97,13 @@ describe('Server integration', function() {
       const request = new Request({ body: { client_id: 1234, client_secret: 'secret', response_type: 'code' }, headers: { 'Authorization': 'Bearer foo' }, method: {}, query: { state: 'foobar' } });
       const response = new Response({ body: {}, headers: {} });
 
-      return server.authorize(request, response)
-        .then(function() {
-          this.allowEmptyState.should.be.false;
-          this.authorizationCodeLifetime.should.equal(300);
-        })
-        .catch(should.fail);
+      try {
+        await server.authorize(request, response);
+      } catch (e) {
+        server.allowEmptyState.should.be.false;
+        server.authorizationCodeLifetime.should.equal(300);
+        should.fail();
+      }
     });
 
     it('should return a promise', function() {
@@ -143,32 +128,10 @@ describe('Server integration', function() {
 
       handler.should.be.an.instanceOf(Promise);
     });
-
-    it('should support callbacks', function(next) {
-      const model = {
-        getAccessToken: function() {
-          return {
-            user: {},
-            accessTokenExpiresAt: new Date(new Date().getTime() + 10000)
-          };
-        },
-        getClient: function() {
-          return { grants: ['authorization_code'], redirectUris: ['http://example.com/cb'] };
-        },
-        saveAuthorizationCode: function() {
-          return { authorizationCode: 123 };
-        }
-      };
-      const server = new Server({ model: model });
-      const request = new Request({ body: { client_id: 1234, client_secret: 'secret', response_type: 'code' }, headers: { 'Authorization': 'Bearer foo' }, method: {}, query: { state: 'foobar' } });
-      const response = new Response({ body: {}, headers: {} });
-
-      server.authorize(request, response, null, next);
-    });
   });
 
   describe('token()', function() {
-    it('should set the default `options`', function() {
+    it('should set the default `options`', async function() {
       const model = {
         getClient: function() {
           return { grants: ['password'] };
@@ -185,12 +148,13 @@ describe('Server integration', function() {
       const request = new Request({ body: { client_id: 1234, client_secret: 'secret', grant_type: 'password', username: 'foo', password: 'pass', scope: 'foo' }, headers: { 'content-type': 'application/x-www-form-urlencoded', 'transfer-encoding': 'chunked' }, method: 'POST', query: {} });
       const response = new Response({ body: {}, headers: {} });
 
-      return server.token(request, response)
-        .then(function() {
-          this.accessTokenLifetime.should.equal(3600);
-          this.refreshTokenLifetime.should.equal(1209600);
-        })
-        .catch(should.fail);
+      try {
+        await server.token(request, response);
+      } catch (e) {
+        server.accessTokenLifetime.should.equal(3600);
+        server.refreshTokenLifetime.should.equal(1209600);
+        should.fail();
+      }
     });
 
     it('should return a promise', function() {
@@ -212,27 +176,5 @@ describe('Server integration', function() {
 
       handler.should.be.an.instanceOf(Promise);
     });
-
-    it('should support callbacks', function(next) {
-      const model = {
-        getClient: function() {
-          return { grants: ['password'] };
-        },
-        getUser: function() {
-          return {};
-        },
-        saveToken: function() {
-          return { accessToken: 1234, client: {}, user: {} };
-        },
-        validateScope: function() {
-          return 'foo';
-        }
-      };
-      const server = new Server({ model: model });
-      const request = new Request({ body: { client_id: 1234, client_secret: 'secret', grant_type: 'password', username: 'foo', password: 'pass', scope: 'foo' }, headers: { 'content-type': 'application/x-www-form-urlencoded', 'transfer-encoding': 'chunked' }, method: 'POST', query: {} });
-      const response = new Response({ body: {}, headers: {} });
-
-      server.token(request, response, null, next);
-    });
   });
 });
diff --git a/test/integration/utils/token-util_test.js b/test/integration/utils/token-util_test.js
index d4f368e..edf9c7e 100644
--- a/test/integration/utils/token-util_test.js
+++ b/test/integration/utils/token-util_test.js
@@ -5,7 +5,6 @@
  */
 
 const TokenUtil = require('../../../lib/utils/token-util');
-const should = require('chai').should();
 
 /**
  * Test `TokenUtil` integration.
@@ -13,12 +12,9 @@ const should = require('chai').should();
 
 describe('TokenUtil integration', function() {
   describe('generateRandomToken()', function() {
-    it('should return a sha-256 token', function() {
-      return TokenUtil.generateRandomToken()
-        .then(function(token) {
-          token.should.be.a.sha256();
-        })
-        .catch(should.fail);
+    it('should return a sha-256 token', async function() {
+      const token = await TokenUtil.generateRandomToken();
+      token.should.be.a.sha256();
     });
   });
 });
diff --git a/test/unit/grant-types/authorization-code-grant-type_test.js b/test/unit/grant-types/authorization-code-grant-type_test.js
index 7672ed4..c3502be 100644
--- a/test/unit/grant-types/authorization-code-grant-type_test.js
+++ b/test/unit/grant-types/authorization-code-grant-type_test.js
@@ -7,7 +7,6 @@
 const AuthorizationCodeGrantType = require('../../../lib/grant-types/authorization-code-grant-type');
 const InvalidGrantError = require('../../../lib/errors/invalid-grant-error');
 const ServerError  = require('../../../lib/errors/server-error');
-const Promise = require('bluebird');
 const Request = require('../../../lib/request');
 const sinon = require('sinon');
 const should = require('chai').should();
diff --git a/test/unit/handlers/authorize-handler_test.js b/test/unit/handlers/authorize-handler_test.js
index 0038c7c..91ab651 100644
--- a/test/unit/handlers/authorize-handler_test.js
+++ b/test/unit/handlers/authorize-handler_test.js
@@ -7,7 +7,6 @@
 const AuthorizeHandler = require('../../../lib/handlers/authorize-handler');
 const Request = require('../../../lib/request');
 const Response = require('../../../lib/response');
-const Promise = require('bluebird');
 const sinon = require('sinon');
 const should = require('chai').should();
 
diff --git a/test/unit/server_test.js b/test/unit/server_test.js
index 3987df7..df68521 100644
--- a/test/unit/server_test.js
+++ b/test/unit/server_test.js
@@ -6,7 +6,6 @@
 
 const AuthenticateHandler = require('../../lib/handlers/authenticate-handler');
 const AuthorizeHandler = require('../../lib/handlers/authorize-handler');
-const Promise = require('bluebird');
 const Server = require('../../lib/server');
 const TokenHandler = require('../../lib/handlers/token-handler');
 const sinon = require('sinon');

From 5454497abd1219b88219dcacaa9c4917fc529b5f Mon Sep 17 00:00:00 2001
From: jankapunkt <jkuester@uni-bremen.de>
Date: Fri, 9 Jun 2023 08:51:09 +0200
Subject: [PATCH 2/7] docs: add 5.0.0 to changelog

---
 CHANGELOG.md | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index f85f845..608cb59 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,13 @@
 ## Changelog
 
+## 5.0.0
+
+- removed `bluebird` and `promisify-any`
+- uses native Promises and `async/await` everywhere
+- drop support for Node 14 (EOL), setting Node 16 as `engine` in `package.json`
+- this is a breaking change, because **it removes callback support** for
+  `OAuthServer` and your model implementation.
+
 ## 4.2.0
 ### Fixed
 - fix(core): Bearer regular expression matching in authenticate handler #105

From 085b13d49040646c48af0fbf4b1f8ba21e24e40d Mon Sep 17 00:00:00 2001
From: jankapunkt <jkuester@uni-bremen.de>
Date: Fri, 9 Jun 2023 08:51:22 +0200
Subject: [PATCH 3/7] docs: add 5.x note to readme

---
 README.md | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/README.md b/README.md
index 1b14f03..b60607f 100644
--- a/README.md
+++ b/README.md
@@ -45,6 +45,13 @@ Most users should refer to our [Express (active)](https://github.com/node-oauth/
 
 More examples can be found here: https://github.com/14gasher/oauth-example
 
+## Version 5 notes
+
+Beginning with version `5.x` we removed dual support for callbacks and promises.
+With this version there is only support for Promises / async/await.
+
+With this version we also bumped the `engine` to Node 16 as 14 is now deprecated.
+
 ## Migrating from OAuthJs and 3.x
 
 Version 4.x should not be hard-breaking, however, there were many improvements and fixes that may

From 2627848af9b0b4f3a4f64b47ad0e07bdc19c7db5 Mon Sep 17 00:00:00 2001
From: jankapunkt <jkuester@uni-bremen.de>
Date: Fri, 9 Jun 2023 08:51:36 +0200
Subject: [PATCH 4/7] docs: add 5.x to security policy

---
 SECURITY.md | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/SECURITY.md b/SECURITY.md
index f0cc8ef..5bc4f6a 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -5,11 +5,12 @@
 Use this section to tell people about which versions of your project are
 currently being supported with security updates.
 
-| Version | Supported          |
-| ------- | ------------------ |
-| 4.x.x   | :white_check_mark: |
-| 3.x.x   | :white_check_mark: but only very critical security issues |
-| < 3 | :x: |
+| Version | Supported                                        |
+|---------|--------------------------------------------------|
+| 5.x.x   | :white_check_mark:                               |
+| 4.x.x   | :white_check_mark: but only high severity issues |
+| 3.x.x   | :x:                                              |
+| < 3     | :x:                                              |
 
 ## Reporting a Vulnerability
 

From cf2adbac8f5335b6fa4714a6d13a0cbecb0bcb6f Mon Sep 17 00:00:00 2001
From: jankapunkt <jkuester@uni-bremen.de>
Date: Fri, 9 Jun 2023 08:52:06 +0200
Subject: [PATCH 5/7] build(core): bump node 14 to 16

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 2c26e6b..c3bd5e8 100644
--- a/package.json
+++ b/package.json
@@ -38,7 +38,7 @@
   },
   "license": "MIT",
   "engines": {
-    "node": ">=14.0.0"
+    "node": ">=16.0.0"
   },
   "scripts": {
     "pretest": "./node_modules/.bin/eslint lib test index.js",

From 2563e7b7afe33e21ca06d675ae30b0635b39295c Mon Sep 17 00:00:00 2001
From: jankapunkt <jkuester@uni-bremen.de>
Date: Tue, 13 Jun 2023 15:22:07 +0200
Subject: [PATCH 6/7] fix: replace Promise. calls in async functions with
 native behaviour

---
 lib/grant-types/client-credentials-grant-type.js | 10 +++-------
 lib/handlers/authorize-handler.js                |  2 +-
 lib/handlers/token-handler.js                    |  4 ++--
 3 files changed, 6 insertions(+), 10 deletions(-)

diff --git a/lib/grant-types/client-credentials-grant-type.js b/lib/grant-types/client-credentials-grant-type.js
index a4ae1ed..e2db3f7 100644
--- a/lib/grant-types/client-credentials-grant-type.js
+++ b/lib/grant-types/client-credentials-grant-type.js
@@ -69,13 +69,9 @@ class ClientCredentialsGrantType extends AbstractGrantType {
 	 */
 
   async saveToken(user, client, scope) {
-    const fns = [
-      this.validateScope(user, client, scope),
-      this.generateAccessToken(client, user, scope),
-      this.getAccessTokenExpiresAt(client, user, scope),
-    ];
-
-    const [validatedScope, accessToken, accessTokenExpiresAt] = await Promise.all(fns);
+    const validatedScope = await this.validateScope(user, client, scope);
+    const accessToken = await this.generateAccessToken(client, user, scope);
+    const accessTokenExpiresAt = await this.getAccessTokenExpiresAt(client, user, scope);
     const token = {
       accessToken: accessToken,
       accessTokenExpiresAt: accessTokenExpiresAt,
diff --git a/lib/handlers/authorize-handler.js b/lib/handlers/authorize-handler.js
index 49d5515..75d1b2e 100644
--- a/lib/handlers/authorize-handler.js
+++ b/lib/handlers/authorize-handler.js
@@ -308,7 +308,7 @@ AuthorizeHandler.prototype.validateRedirectUri = async function(redirectUri, cli
     return this.model.validateRedirectUri(redirectUri, client);
   }
 
-  return Promise.resolve(client.redirectUris.includes(redirectUri));
+  return client.redirectUris.includes(redirectUri);
 };
 /**
  * Get response type.
diff --git a/lib/handlers/token-handler.js b/lib/handlers/token-handler.js
index 4630c4f..468d810 100644
--- a/lib/handlers/token-handler.js
+++ b/lib/handlers/token-handler.js
@@ -76,11 +76,11 @@ TokenHandler.prototype.handle = async function(request, response) {
   }
 
   if (request.method !== 'POST') {
-    return Promise.reject(new InvalidRequestError('Invalid request: method must be POST'));
+    throw new InvalidRequestError('Invalid request: method must be POST');
   }
 
   if (!request.is('application/x-www-form-urlencoded')) {
-    return Promise.reject(new InvalidRequestError('Invalid request: content must be application/x-www-form-urlencoded'));
+    throw new InvalidRequestError('Invalid request: content must be application/x-www-form-urlencoded');
   }
 
   try {

From e1fdc23e652dff163cb8d07d5bd272204fc8666b Mon Sep 17 00:00:00 2001
From: jankapunkt <jkuester@uni-bremen.de>
Date: Tue, 13 Jun 2023 15:35:35 +0200
Subject: [PATCH 7/7] fix(tests): replace Promise. calls with native async
 behaviour where applicable

---
 .../grant-types/abstract-grant-type_test.js      |  8 ++++----
 .../authorization-code-grant-type_test.js        |  8 ++++----
 .../client-credentials-grant-type_test.js        |  4 ++--
 .../grant-types/password-grant-type_test.js      |  6 +++---
 .../grant-types/refresh-token-grant-type_test.js | 12 ++++++------
 .../handlers/authenticate-handler_test.js        |  4 ++--
 .../handlers/authorize-handler_test.js           | 16 ++++++++--------
 test/integration/handlers/token-handler_test.js  |  2 +-
 8 files changed, 30 insertions(+), 30 deletions(-)

diff --git a/test/integration/grant-types/abstract-grant-type_test.js b/test/integration/grant-types/abstract-grant-type_test.js
index 5d9aa7a..e874509 100644
--- a/test/integration/grant-types/abstract-grant-type_test.js
+++ b/test/integration/grant-types/abstract-grant-type_test.js
@@ -70,8 +70,8 @@ describe('AbstractGrantType integration', function() {
 
     it('should support promises', function() {
       const model = {
-        generateAccessToken: function() {
-          return Promise.resolve({});
+        generateAccessToken: async function() {
+          return {};
         }
       };
       const handler = new AbstractGrantType({ accessTokenLifetime: 123, model: model, refreshTokenLifetime: 456 });
@@ -104,8 +104,8 @@ describe('AbstractGrantType integration', function() {
 
     it('should support promises', function() {
       const model = {
-        generateRefreshToken: function() {
-          return Promise.resolve({});
+        generateRefreshToken: async function() {
+          return {};
         }
       };
       const handler = new AbstractGrantType({ accessTokenLifetime: 123, model: model, refreshTokenLifetime: 456 });
diff --git a/test/integration/grant-types/authorization-code-grant-type_test.js b/test/integration/grant-types/authorization-code-grant-type_test.js
index 85c007f..a4d69c4 100644
--- a/test/integration/grant-types/authorization-code-grant-type_test.js
+++ b/test/integration/grant-types/authorization-code-grant-type_test.js
@@ -145,7 +145,7 @@ describe('AuthorizationCodeGrantType integration', function() {
     it('should support promises', function() {
       const client = { id: 'foobar' };
       const model = {
-        getAuthorizationCode: function() { return Promise.resolve({ authorizationCode: 12345, client: { id: 'foobar' }, expiresAt: new Date(new Date() * 2), user: {} }); },
+        getAuthorizationCode: function() { return { authorizationCode: 12345, client: { id: 'foobar' }, expiresAt: new Date(new Date() * 2), user: {} }; },
         revokeAuthorizationCode: function() { return true; },
         saveToken: function() {}
       };
@@ -362,7 +362,7 @@ describe('AuthorizationCodeGrantType integration', function() {
       const authorizationCode = { authorizationCode: 12345, client: { id: 'foobar' }, expiresAt: new Date(new Date() * 2), user: {} };
       const client = { id: 'foobar' };
       const model = {
-        getAuthorizationCode: function() { return Promise.resolve(authorizationCode); },
+        getAuthorizationCode: async function() { return authorizationCode; },
         revokeAuthorizationCode: function() {},
         saveToken: function() {}
       };
@@ -469,7 +469,7 @@ describe('AuthorizationCodeGrantType integration', function() {
       const authorizationCode = { authorizationCode: 12345, client: {}, expiresAt: new Date(new Date() / 2), user: {} };
       const model = {
         getAuthorizationCode: function() {},
-        revokeAuthorizationCode: function() { return Promise.resolve(true); },
+        revokeAuthorizationCode: async function() { return true; },
         saveToken: function() {}
       };
       const grantType = new AuthorizationCodeGrantType({ accessTokenLifetime: 123, model: model });
@@ -513,7 +513,7 @@ describe('AuthorizationCodeGrantType integration', function() {
       const model = {
         getAuthorizationCode: function() {},
         revokeAuthorizationCode: function() {},
-        saveToken: function() { return Promise.resolve(token); }
+        saveToken: async function() { return token; }
       };
       const grantType = new AuthorizationCodeGrantType({ accessTokenLifetime: 123, model: model });
 
diff --git a/test/integration/grant-types/client-credentials-grant-type_test.js b/test/integration/grant-types/client-credentials-grant-type_test.js
index 20c2da4..83de9f9 100644
--- a/test/integration/grant-types/client-credentials-grant-type_test.js
+++ b/test/integration/grant-types/client-credentials-grant-type_test.js
@@ -168,7 +168,7 @@ describe('ClientCredentialsGrantType integration', function() {
     it('should support promises', function() {
       const user = { email: 'foo@bar.com' };
       const model = {
-        getUserFromClient: function() { return Promise.resolve(user); },
+        getUserFromClient: async function() { return user; },
         saveToken: function() {}
       };
       const grantType = new ClientCredentialsGrantType({ accessTokenLifetime: 120, model: model });
@@ -211,7 +211,7 @@ describe('ClientCredentialsGrantType integration', function() {
       const token = {};
       const model = {
         getUserFromClient: function() {},
-        saveToken: function() { return Promise.resolve(token); }
+        saveToken: async function() { return token; }
       };
       const grantType = new ClientCredentialsGrantType({ accessTokenLifetime: 123, model: model });
 
diff --git a/test/integration/grant-types/password-grant-type_test.js b/test/integration/grant-types/password-grant-type_test.js
index 11dc3a3..04452ee 100644
--- a/test/integration/grant-types/password-grant-type_test.js
+++ b/test/integration/grant-types/password-grant-type_test.js
@@ -113,7 +113,7 @@ describe('PasswordGrantType integration', function() {
       const token = {};
       const model = {
         getUser: function() { return {}; },
-        saveToken: function() { return Promise.resolve(token); }
+        saveToken: async function() { return token; }
       };
       const grantType = new PasswordGrantType({ accessTokenLifetime: 123, model: model });
       const request = new Request({ body: { username: 'foo', password: 'bar' }, headers: {}, method: {}, query: {} });
@@ -245,7 +245,7 @@ describe('PasswordGrantType integration', function() {
     it('should support promises', function() {
       const user = { email: 'foo@bar.com' };
       const model = {
-        getUser: function() { return Promise.resolve(user); },
+        getUser: async function() { return user; },
         saveToken: function() {}
       };
       const grantType = new PasswordGrantType({ accessTokenLifetime: 123, model: model });
@@ -288,7 +288,7 @@ describe('PasswordGrantType integration', function() {
       const token = {};
       const model = {
         getUser: function() {},
-        saveToken: function() { return Promise.resolve(token); }
+        saveToken: async function() { return token; }
       };
       const grantType = new PasswordGrantType({ accessTokenLifetime: 123, model: model });
 
diff --git a/test/integration/grant-types/refresh-token-grant-type_test.js b/test/integration/grant-types/refresh-token-grant-type_test.js
index 3273606..fede377 100644
--- a/test/integration/grant-types/refresh-token-grant-type_test.js
+++ b/test/integration/grant-types/refresh-token-grant-type_test.js
@@ -131,9 +131,9 @@ describe('RefreshTokenGrantType integration', function() {
     it('should support promises', function() {
       const client = { id: 123 };
       const model = {
-        getRefreshToken: function() { return Promise.resolve({ accessToken: 'foo', client: { id: 123 }, user: {} }); },
-        revokeToken: function() { return Promise.resolve({ accessToken: 'foo', client: {}, refreshTokenExpiresAt: new Date(new Date() / 2), user: {} }); },
-        saveToken: function() { return Promise.resolve({ accessToken: 'foo', client: {}, user: {} }); }
+        getRefreshToken: async function() { return { accessToken: 'foo', client: { id: 123 }, user: {} }; },
+        revokeToken: async function() { return { accessToken: 'foo', client: {}, refreshTokenExpiresAt: new Date(new Date() / 2), user: {} }; },
+        saveToken: async function() { return { accessToken: 'foo', client: {}, user: {} }; }
       };
       const grantType = new RefreshTokenGrantType({ accessTokenLifetime: 123, model: model });
       const request = new Request({ body: { refresh_token: 'foobar' }, headers: {}, method: {}, query: {} });
@@ -357,7 +357,7 @@ describe('RefreshTokenGrantType integration', function() {
       const client = { id: 123 };
       const token = { accessToken: 'foo', client: { id: 123 }, user: {} };
       const model = {
-        getRefreshToken: function() { return Promise.resolve(token); },
+        getRefreshToken: async function() { return token; },
         revokeToken: function() {},
         saveToken: function() {}
       };
@@ -419,7 +419,7 @@ describe('RefreshTokenGrantType integration', function() {
       const token = { accessToken: 'foo', client: {}, refreshTokenExpiresAt: new Date(new Date() / 2), user: {} };
       const model = {
         getRefreshToken: function() {},
-        revokeToken: function() { return Promise.resolve(token); },
+        revokeToken: async function() { return token; },
         saveToken: function() {}
       };
       const grantType = new RefreshTokenGrantType({ accessTokenLifetime: 123, model: model });
@@ -462,7 +462,7 @@ describe('RefreshTokenGrantType integration', function() {
       const model = {
         getRefreshToken: function() {},
         revokeToken: function() {},
-        saveToken: function() { return Promise.resolve(token); }
+        saveToken: async function() { return token; }
       };
       const grantType = new RefreshTokenGrantType({ accessTokenLifetime: 123, model: model });
 
diff --git a/test/integration/handlers/authenticate-handler_test.js b/test/integration/handlers/authenticate-handler_test.js
index d8bf728..c069ed9 100644
--- a/test/integration/handlers/authenticate-handler_test.js
+++ b/test/integration/handlers/authenticate-handler_test.js
@@ -445,8 +445,8 @@ describe('AuthenticateHandler integration', function() {
 
     it('should support promises', function() {
       const model = {
-        getAccessToken: function() {
-          return Promise.resolve({ user: {} });
+        getAccessToken: async function() {
+          return { user: {} };
         }
       };
       const handler = new AuthenticateHandler({ model: model });
diff --git a/test/integration/handlers/authorize-handler_test.js b/test/integration/handlers/authorize-handler_test.js
index 8a5eb65..5da1b39 100644
--- a/test/integration/handlers/authorize-handler_test.js
+++ b/test/integration/handlers/authorize-handler_test.js
@@ -612,8 +612,8 @@ describe('AuthorizeHandler integration', function() {
 
     it('should support promises', function() {
       const model = {
-        generateAuthorizationCode: function() {
-          return Promise.resolve({});
+        generateAuthorizationCode: async function() {
+          return {};
         },
         getAccessToken: function() {},
         getClient: function() {},
@@ -670,8 +670,8 @@ describe('AuthorizeHandler integration', function() {
         getAccessToken: function() {},
         getClient: function() {},
         saveAuthorizationCode: function() {},
-        validateRedirectUri: function() {
-          return Promise.resolve(true);
+        validateRedirectUri: async function() {
+          return true;
         }
       };
 
@@ -848,8 +848,8 @@ describe('AuthorizeHandler integration', function() {
     it('should support promises', function() {
       const model = {
         getAccessToken: function() {},
-        getClient: function() {
-          return Promise.resolve({ grants: ['authorization_code'], redirectUris: ['http://example.com/cb'] });
+        getClient: async function() {
+          return { grants: ['authorization_code'], redirectUris: ['http://example.com/cb'] };
         },
         saveAuthorizationCode: function() {}
       };
@@ -1100,8 +1100,8 @@ describe('AuthorizeHandler integration', function() {
       const model = {
         getAccessToken: function() {},
         getClient: function() {},
-        saveAuthorizationCode: function() {
-          return Promise.resolve({});
+        saveAuthorizationCode: async function() {
+          return {};
         }
       };
       const handler = new AuthorizeHandler({ authorizationCodeLifetime: 120, model: model });
diff --git a/test/integration/handlers/token-handler_test.js b/test/integration/handlers/token-handler_test.js
index fd8f769..4477c7b 100644
--- a/test/integration/handlers/token-handler_test.js
+++ b/test/integration/handlers/token-handler_test.js
@@ -587,7 +587,7 @@ describe('TokenHandler integration', function() {
 
     it('should support promises', function() {
       const model = {
-        getClient: function() { return Promise.resolve({ grants: [] }); },
+        getClient: async function() { return { grants: [] }; },
         saveToken: function() {}
       };
       const handler = new TokenHandler({ accessTokenLifetime: 120, model: model, refreshTokenLifetime: 120 });