diff --git a/lib/grant-types/abstract-grant-type.js b/lib/grant-types/abstract-grant-type.js index 4fd02437..e351d940 100644 --- a/lib/grant-types/abstract-grant-type.js +++ b/lib/grant-types/abstract-grant-type.js @@ -9,97 +9,92 @@ const InvalidScopeError = require('../errors/invalid-scope-error'); const isFormat = require('@node-oauth/formats'); const tokenUtil = require('../utils/token-util'); -/** - * Constructor. - */ +class AbstractGrantType { + constructor (options) { + options = options || {}; -function AbstractGrantType(options) { - options = options || {}; + if (!options.accessTokenLifetime) { + throw new InvalidArgumentError('Missing parameter: `accessTokenLifetime`'); + } - if (!options.accessTokenLifetime) { - throw new InvalidArgumentError('Missing parameter: `accessTokenLifetime`'); - } + if (!options.model) { + throw new InvalidArgumentError('Missing parameter: `model`'); + } - if (!options.model) { - throw new InvalidArgumentError('Missing parameter: `model`'); + this.accessTokenLifetime = options.accessTokenLifetime; + this.model = options.model; + this.refreshTokenLifetime = options.refreshTokenLifetime; + this.alwaysIssueNewRefreshToken = options.alwaysIssueNewRefreshToken; } - this.accessTokenLifetime = options.accessTokenLifetime; - this.model = options.model; - this.refreshTokenLifetime = options.refreshTokenLifetime; - this.alwaysIssueNewRefreshToken = options.alwaysIssueNewRefreshToken; -} - -/** - * Generate access token. - */ + /** + * Generate access token. + */ + async generateAccessToken (client, user, scope) { + if (this.model.generateAccessToken) { + const accessToken = await this.model.generateAccessToken(client, user, scope); + return accessToken || tokenUtil.generateRandomToken(); + } -AbstractGrantType.prototype.generateAccessToken = async function(client, user, scope) { - if (this.model.generateAccessToken) { - const accessToken = await this.model.generateAccessToken(client, user, scope); - return accessToken || tokenUtil.generateRandomToken(); + return tokenUtil.generateRandomToken(); } - return tokenUtil.generateRandomToken(); -}; - -/** + /** * Generate refresh token. */ + async generateRefreshToken (client, user, scope) { + if (this.model.generateRefreshToken) { + const refreshToken = await this.model.generateRefreshToken(client, user, scope); + return refreshToken || tokenUtil.generateRandomToken(); + } -AbstractGrantType.prototype.generateRefreshToken = async function(client, user, scope) { - if (this.model.generateRefreshToken) { - const refreshToken = await this.model.generateRefreshToken(client, user, scope); - return refreshToken || tokenUtil.generateRandomToken(); + return tokenUtil.generateRandomToken(); } - return tokenUtil.generateRandomToken(); -}; - -/** + /** * Get access token expiration date. */ + getAccessTokenExpiresAt() { + return new Date(Date.now() + this.accessTokenLifetime * 1000); + } -AbstractGrantType.prototype.getAccessTokenExpiresAt = function() { - return new Date(Date.now() + this.accessTokenLifetime * 1000); -}; -/** - * Get refresh token expiration date. - */ -AbstractGrantType.prototype.getRefreshTokenExpiresAt = function() { - return new Date(Date.now() + this.refreshTokenLifetime * 1000); -}; + /** + * Get refresh token expiration date. + */ + getRefreshTokenExpiresAt () { + return new Date(Date.now() + this.refreshTokenLifetime * 1000); + } -/** - * Get scope from the request body. - */ + /** + * Get scope from the request body. + */ + getScope (request) { + if (!isFormat.nqschar(request.body.scope)) { + throw new InvalidArgumentError('Invalid parameter: `scope`'); + } -AbstractGrantType.prototype.getScope = function(request) { - if (!isFormat.nqschar(request.body.scope)) { - throw new InvalidArgumentError('Invalid parameter: `scope`'); + return request.body.scope; } - return request.body.scope; -}; + /** + * Validate requested scope. + */ + async validateScope (user, client, scope) { + if (this.model.validateScope) { + const validatedScope = await this.model.validateScope(user, client, scope); -/** - * Validate requested scope. - */ -AbstractGrantType.prototype.validateScope = async function(user, client, scope) { - if (this.model.validateScope) { - const validatedScope = await this.model.validateScope(user, client, scope); + if (!validatedScope) { + throw new InvalidScopeError('Invalid scope: Requested scope is invalid'); + } - if (!validatedScope) { - throw new InvalidScopeError('Invalid scope: Requested scope is invalid'); + return validatedScope; + } else { + return scope; } - - return validatedScope; - } else { - return scope; } -}; +} /** * Export constructor. diff --git a/lib/handlers/authenticate-handler.js b/lib/handlers/authenticate-handler.js index 1da50f95..54945285 100644 --- a/lib/handlers/authenticate-handler.js +++ b/lib/handlers/authenticate-handler.js @@ -18,240 +18,241 @@ const UnauthorizedRequestError = require('../errors/unauthorized-request-error') * Constructor. */ -function AuthenticateHandler(options) { - options = options || {}; +class AuthenticateHandler { + constructor (options) { + options = options || {}; - if (!options.model) { - throw new InvalidArgumentError('Missing parameter: `model`'); - } - - if (!options.model.getAccessToken) { - throw new InvalidArgumentError('Invalid argument: model does not implement `getAccessToken()`'); - } - - if (options.scope && undefined === options.addAcceptedScopesHeader) { - throw new InvalidArgumentError('Missing parameter: `addAcceptedScopesHeader`'); - } - - if (options.scope && undefined === options.addAuthorizedScopesHeader) { - throw new InvalidArgumentError('Missing parameter: `addAuthorizedScopesHeader`'); - } + if (!options.model) { + throw new InvalidArgumentError('Missing parameter: `model`'); + } - if (options.scope && !options.model.verifyScope) { - throw new InvalidArgumentError('Invalid argument: model does not implement `verifyScope()`'); - } + if (!options.model.getAccessToken) { + throw new InvalidArgumentError('Invalid argument: model does not implement `getAccessToken()`'); + } - this.addAcceptedScopesHeader = options.addAcceptedScopesHeader; - this.addAuthorizedScopesHeader = options.addAuthorizedScopesHeader; - this.allowBearerTokensInQueryString = options.allowBearerTokensInQueryString; - this.model = options.model; - this.scope = options.scope; -} + if (options.scope && undefined === options.addAcceptedScopesHeader) { + throw new InvalidArgumentError('Missing parameter: `addAcceptedScopesHeader`'); + } -/** - * Authenticate Handler. - */ + if (options.scope && undefined === options.addAuthorizedScopesHeader) { + throw new InvalidArgumentError('Missing parameter: `addAuthorizedScopesHeader`'); + } -AuthenticateHandler.prototype.handle = async function(request, response) { - if (!(request instanceof Request)) { - throw new InvalidArgumentError('Invalid argument: `request` must be an instance of Request'); - } + if (options.scope && !options.model.verifyScope) { + throw new InvalidArgumentError('Invalid argument: model does not implement `verifyScope()`'); + } - if (!(response instanceof Response)) { - throw new InvalidArgumentError('Invalid argument: `response` must be an instance of Response'); + this.addAcceptedScopesHeader = options.addAcceptedScopesHeader; + this.addAuthorizedScopesHeader = options.addAuthorizedScopesHeader; + this.allowBearerTokensInQueryString = options.allowBearerTokensInQueryString; + this.model = options.model; + this.scope = options.scope; } - try { - const requestToken = await this.getTokenFromRequest(request); + /** + * Authenticate Handler. + */ - let accessToken; - accessToken = await this.getAccessToken(requestToken); - accessToken = await this.validateAccessToken(accessToken); - - if (this.scope) { - await this.verifyScope(accessToken); + async handle (request, response) { + if (!(request instanceof Request)) { + throw new InvalidArgumentError('Invalid argument: `request` must be an instance of Request'); } - 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 (!(response instanceof Response)) { + throw new InvalidArgumentError('Invalid argument: `response` must be an instance of Response'); } - if (!(e instanceof OAuthError)) { - throw new ServerError(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; } - - throw e; } -}; -/** - * Get the token from the header or body, depending on the request. - * - * "Clients MUST NOT use more than one method to transmit the token in each request." - * - * @see https://tools.ietf.org/html/rfc6750#section-2 - */ - -AuthenticateHandler.prototype.getTokenFromRequest = function(request) { - const headerToken = request.get('Authorization'); - const queryToken = request.query.access_token; - const bodyToken = request.body.access_token; + /** + * Get the token from the header or body, depending on the request. + * + * "Clients MUST NOT use more than one method to transmit the token in each request." + * + * @see https://tools.ietf.org/html/rfc6750#section-2 + */ + + getTokenFromRequest (request) { + const headerToken = request.get('Authorization'); + const queryToken = request.query.access_token; + const bodyToken = request.body.access_token; + + if (!!headerToken + !!queryToken + !!bodyToken > 1) { + throw new InvalidRequestError('Invalid request: only one authentication method is allowed'); + } - if (!!headerToken + !!queryToken + !!bodyToken > 1) { - throw new InvalidRequestError('Invalid request: only one authentication method is allowed'); - } + if (headerToken) { + return this.getTokenFromRequestHeader(request); + } - if (headerToken) { - return this.getTokenFromRequestHeader(request); - } + if (queryToken) { + return this.getTokenFromRequestQuery(request); + } - if (queryToken) { - return this.getTokenFromRequestQuery(request); - } + if (bodyToken) { + return this.getTokenFromRequestBody(request); + } - if (bodyToken) { - return this.getTokenFromRequestBody(request); + throw new UnauthorizedRequestError('Unauthorized request: no authentication given'); } - throw new UnauthorizedRequestError('Unauthorized request: no authentication given'); -}; + /** + * Get the token from the request header. + * + * @see http://tools.ietf.org/html/rfc6750#section-2.1 + */ -/** - * Get the token from the request header. - * - * @see http://tools.ietf.org/html/rfc6750#section-2.1 - */ + getTokenFromRequestHeader (request) { + const token = request.get('Authorization'); + const matches = token.match(/^Bearer\s(\S+)/); -AuthenticateHandler.prototype.getTokenFromRequestHeader = function(request) { - const token = request.get('Authorization'); - const matches = token.match(/^Bearer\s(\S+)/); + if (!matches) { + throw new InvalidRequestError('Invalid request: malformed authorization header'); + } - if (!matches) { - throw new InvalidRequestError('Invalid request: malformed authorization header'); + return matches[1]; } - return matches[1]; -}; - -/** - * Get the token from the request query. - * - * "Don't pass bearer tokens in page URLs: Bearer tokens SHOULD NOT be passed in page - * URLs (for example, as query string parameters). Instead, bearer tokens SHOULD be - * passed in HTTP message headers or message bodies for which confidentiality measures - * are taken. Browsers, web servers, and other software may not adequately secure URLs - * in the browser history, web server logs, and other data structures. If bearer tokens - * are passed in page URLs, attackers might be able to steal them from the history data, - * logs, or other unsecured locations." - * - * @see http://tools.ietf.org/html/rfc6750#section-2.3 - */ + /** + * Get the token from the request query. + * + * "Don't pass bearer tokens in page URLs: Bearer tokens SHOULD NOT be passed in page + * URLs (for example, as query string parameters). Instead, bearer tokens SHOULD be + * passed in HTTP message headers or message bodies for which confidentiality measures + * are taken. Browsers, web servers, and other software may not adequately secure URLs + * in the browser history, web server logs, and other data structures. If bearer tokens + * are passed in page URLs, attackers might be able to steal them from the history data, + * logs, or other unsecured locations." + * + * @see http://tools.ietf.org/html/rfc6750#section-2.3 + */ + + getTokenFromRequestQuery (request) { + if (!this.allowBearerTokensInQueryString) { + throw new InvalidRequestError('Invalid request: do not send bearer tokens in query URLs'); + } -AuthenticateHandler.prototype.getTokenFromRequestQuery = function(request) { - if (!this.allowBearerTokensInQueryString) { - throw new InvalidRequestError('Invalid request: do not send bearer tokens in query URLs'); + return request.query.access_token; } - return request.query.access_token; -}; - -/** - * Get the token from the request body. - * - * "The HTTP request method is one for which the request-body has defined semantics. - * In particular, this means that the "GET" method MUST NOT be used." - * - * @see http://tools.ietf.org/html/rfc6750#section-2.2 - */ + /** + * Get the token from the request body. + * + * "The HTTP request method is one for which the request-body has defined semantics. + * In particular, this means that the "GET" method MUST NOT be used." + * + * @see http://tools.ietf.org/html/rfc6750#section-2.2 + */ + + getTokenFromRequestBody (request) { + if (request.method === 'GET') { + throw new InvalidRequestError('Invalid request: token may not be passed in the body when using the GET verb'); + } -AuthenticateHandler.prototype.getTokenFromRequestBody = function(request) { - if (request.method === 'GET') { - throw new InvalidRequestError('Invalid request: token may not be passed in the body when using the GET verb'); - } + if (!request.is('application/x-www-form-urlencoded')) { + throw new InvalidRequestError('Invalid request: content must be application/x-www-form-urlencoded'); + } - if (!request.is('application/x-www-form-urlencoded')) { - throw new InvalidRequestError('Invalid request: content must be application/x-www-form-urlencoded'); + return request.body.access_token; } - return request.body.access_token; -}; + /** + * Get the access token from the model. + */ -/** - * Get the access token from the model. - */ + async getAccessToken (token) { + const accessToken = await this.model.getAccessToken(token); -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) { - 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; -}; + /** + * Validate access token. + */ -/** - * Validate access token. - */ + validateAccessToken (accessToken) { + if (!(accessToken.accessTokenExpiresAt instanceof Date)) { + throw new ServerError('Server error: `accessTokenExpiresAt` must be a Date instance'); + } -AuthenticateHandler.prototype.validateAccessToken = function(accessToken) { - if (!(accessToken.accessTokenExpiresAt instanceof Date)) { - throw new ServerError('Server error: `accessTokenExpiresAt` must be a Date instance'); - } + if (accessToken.accessTokenExpiresAt < new Date()) { + throw new InvalidTokenError('Invalid token: access token has expired'); + } - if (accessToken.accessTokenExpiresAt < new Date()) { - throw new InvalidTokenError('Invalid token: access token has expired'); + return accessToken; } - return accessToken; -}; + /** + * Verify scope. + */ -/** - * Verify scope. - */ + async verifyScope (accessToken) { + const scope = await this.model.verifyScope(accessToken, this.scope); -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'); + } - if (!scope) { - throw new InsufficientScopeError('Insufficient scope: authorized scope is insufficient'); + return scope; } - return scope; -}; + /** + * Update response. + */ -/** - * Update response. - */ - -AuthenticateHandler.prototype.updateResponse = function(response, accessToken) { - if (this.scope && this.addAcceptedScopesHeader) { - response.set('X-Accepted-OAuth-Scopes', this.scope); - } + updateResponse (response, accessToken) { + if (this.scope && this.addAcceptedScopesHeader) { + response.set('X-Accepted-OAuth-Scopes', this.scope); + } - if (this.scope && this.addAuthorizedScopesHeader) { - response.set('X-OAuth-Scopes', accessToken.scope); + if (this.scope && this.addAuthorizedScopesHeader) { + response.set('X-OAuth-Scopes', accessToken.scope); + } } -}; - +} /** * Export constructor. */ diff --git a/lib/handlers/authorize-handler.js b/lib/handlers/authorize-handler.js index 75d1b2e6..a98a037a 100644 --- a/lib/handlers/authorize-handler.js +++ b/lib/handlers/authorize-handler.js @@ -34,361 +34,362 @@ const responseTypes = { * Constructor. */ -function AuthorizeHandler(options) { - options = options || {}; +class AuthorizeHandler { + constructor (options) { + options = options || {}; - if (options.authenticateHandler && !options.authenticateHandler.handle) { - throw new InvalidArgumentError('Invalid argument: authenticateHandler does not implement `handle()`'); - } - - if (!options.authorizationCodeLifetime) { - throw new InvalidArgumentError('Missing parameter: `authorizationCodeLifetime`'); - } - - if (!options.model) { - throw new InvalidArgumentError('Missing parameter: `model`'); - } - - if (!options.model.getClient) { - throw new InvalidArgumentError('Invalid argument: model does not implement `getClient()`'); - } + if (options.authenticateHandler && !options.authenticateHandler.handle) { + throw new InvalidArgumentError('Invalid argument: authenticateHandler does not implement `handle()`'); + } - if (!options.model.saveAuthorizationCode) { - throw new InvalidArgumentError('Invalid argument: model does not implement `saveAuthorizationCode()`'); - } + if (!options.authorizationCodeLifetime) { + throw new InvalidArgumentError('Missing parameter: `authorizationCodeLifetime`'); + } - this.allowEmptyState = options.allowEmptyState; - this.authenticateHandler = options.authenticateHandler || new AuthenticateHandler(options); - this.authorizationCodeLifetime = options.authorizationCodeLifetime; - this.model = options.model; -} + if (!options.model) { + throw new InvalidArgumentError('Missing parameter: `model`'); + } -/** - * Authorize Handler. - */ + if (!options.model.getClient) { + throw new InvalidArgumentError('Invalid argument: model does not implement `getClient()`'); + } -AuthorizeHandler.prototype.handle = async function(request, response) { - if (!(request instanceof Request)) { - throw new InvalidArgumentError('Invalid argument: `request` must be an instance of Request'); - } + if (!options.model.saveAuthorizationCode) { + throw new InvalidArgumentError('Invalid argument: model does not implement `saveAuthorizationCode()`'); + } - if (!(response instanceof Response)) { - throw new InvalidArgumentError('Invalid argument: `response` must be an instance of Response'); + this.allowEmptyState = options.allowEmptyState; + this.authenticateHandler = options.authenticateHandler || new AuthenticateHandler(options); + this.authorizationCodeLifetime = options.authorizationCodeLifetime; + this.model = options.model; } - const expiresAt = await this.getAuthorizationCodeLifetime(); - const client = await this.getClient(request); - const user = await this.getUser(request, response); + /** + * Authorize Handler. + */ - 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'); + async handle (request, response) { + if (!(request instanceof Request)) { + throw new InvalidArgumentError('Invalid argument: `request` must be an instance of Request'); } - 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); + if (!(response instanceof Response)) { + throw new InvalidArgumentError('Invalid argument: `response` must be an instance of Response'); } - 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. - */ + /** + * Generate authorization code. + */ -AuthorizeHandler.prototype.generateAuthorizationCode = async function(client, user, scope) { - if (this.model.generateAuthorizationCode) { - return this.model.generateAuthorizationCode(client, user, scope); + async generateAuthorizationCode (client, user, scope) { + if (this.model.generateAuthorizationCode) { + return this.model.generateAuthorizationCode(client, user, scope); + } + return tokenUtil.generateRandomToken(); } - return tokenUtil.generateRandomToken(); -}; -/** - * Get authorization code lifetime. - */ + /** + * Get authorization code lifetime. + */ -AuthorizeHandler.prototype.getAuthorizationCodeLifetime = function() { - const expires = new Date(); + getAuthorizationCodeLifetime () { + const expires = new Date(); - expires.setSeconds(expires.getSeconds() + this.authorizationCodeLifetime); - return expires; -}; + expires.setSeconds(expires.getSeconds() + this.authorizationCodeLifetime); + return expires; + } -/** - * Get the client from the model. - */ + /** + * Get the client from the model. + */ -AuthorizeHandler.prototype.getClient = async function(request) { - const self = this; - const clientId = request.body.client_id || request.query.client_id; + async getClient (request) { + const self = this; + const clientId = request.body.client_id || request.query.client_id; - if (!clientId) { - throw new InvalidRequestError('Missing parameter: `client_id`'); - } + if (!clientId) { + throw new InvalidRequestError('Missing parameter: `client_id`'); + } - if (!isFormat.vschar(clientId)) { - throw new InvalidRequestError('Invalid parameter: `client_id`'); - } + if (!isFormat.vschar(clientId)) { + throw new InvalidRequestError('Invalid parameter: `client_id`'); + } - const redirectUri = request.body.redirect_uri || request.query.redirect_uri; + const redirectUri = request.body.redirect_uri || request.query.redirect_uri; - if (redirectUri && !isFormat.uri(redirectUri)) { - throw new InvalidRequestError('Invalid request: `redirect_uri` is not a valid URI'); - } + if (redirectUri && !isFormat.uri(redirectUri)) { + throw new InvalidRequestError('Invalid request: `redirect_uri` is not a valid URI'); + } - const client = await this.model.getClient(clientId, null); + const client = await this.model.getClient(clientId, null); - if (!client) { - throw new InvalidClientError('Invalid client: client credentials are invalid'); - } + if (!client) { + throw new InvalidClientError('Invalid client: client credentials are invalid'); + } - if (!client.grants) { - throw new InvalidClientError('Invalid client: missing client `grants`'); - } + 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 (!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 (!client.redirectUris || 0 === client.redirectUris.length) { + throw new InvalidClientError('Invalid client: missing client `redirectUri`'); + } - if (redirectUri) { - const valid = await self.validateRedirectUri(redirectUri, client); + if (redirectUri) { + const valid = await self.validateRedirectUri(redirectUri, client); - if (!valid) { - throw new InvalidClientError('Invalid client: `redirect_uri` does not match client value'); + if (!valid) { + throw new InvalidClientError('Invalid client: `redirect_uri` does not match client value'); + } } + + return client; } - return client; -}; + /** + * Validate requested scope. + */ + async validateScope (user, client, scope) { + if (this.model.validateScope) { + const validatedScope = await this.model.validateScope(user, client, scope); -/** - * Validate requested scope. - */ -AuthorizeHandler.prototype.validateScope = async function(user, client, scope) { - if (this.model.validateScope) { - const validatedScope = await this.model.validateScope(user, client, scope); + if (!validatedScope) { + throw new InvalidScopeError('Invalid scope: Requested scope is invalid'); + } - if (!validatedScope) { - throw new InvalidScopeError('Invalid scope: Requested scope is invalid'); + return validatedScope; } - return validatedScope; + return scope; } - return scope; -}; + /** + * Get scope from the request. + */ -/** - * Get scope from the request. - */ + getScope (request) { + const scope = request.body.scope || request.query.scope; -AuthorizeHandler.prototype.getScope = function(request) { - const scope = request.body.scope || request.query.scope; + if (!isFormat.nqschar(scope)) { + throw new InvalidScopeError('Invalid parameter: `scope`'); + } - if (!isFormat.nqschar(scope)) { - throw new InvalidScopeError('Invalid parameter: `scope`'); + return scope; } - return scope; -}; + /** + * Get state from the request. + */ -/** - * Get state from the request. - */ + getState (request) { + const state = request.body.state || request.query.state; + const stateExists = state && state.length > 0; + const stateIsValid = stateExists + ? isFormat.vschar(state) + : this.allowEmptyState; -AuthorizeHandler.prototype.getState = function(request) { - const state = request.body.state || request.query.state; - const stateExists = state && state.length > 0; - const stateIsValid = stateExists - ? isFormat.vschar(state) - : this.allowEmptyState; + if (!stateIsValid) { + const message = (!stateExists) ? 'Missing' : 'Invalid'; + throw new InvalidRequestError(`${message} parameter: \`state\``); + } - if (!stateIsValid) { - const message = (!stateExists) ? 'Missing' : 'Invalid'; - throw new InvalidRequestError(`${message} parameter: \`state\``); + return state; } - return state; -}; + /** + * Get user by calling the authenticate middleware. + */ -/** - * Get user by calling the authenticate middleware. - */ + async getUser (request, response) { + if (this.authenticateHandler instanceof AuthenticateHandler) { + const handled = await this.authenticateHandler.handle(request, response); + return handled + ? handled.user + : undefined; + } -AuthorizeHandler.prototype.getUser = async function(request, response) { - if (this.authenticateHandler instanceof AuthenticateHandler) { - const handled = await this.authenticateHandler.handle(request, response); - return handled - ? handled.user - : undefined; - } + const user = await this.authenticateHandler.handle(request, response); - const user = await this.authenticateHandler.handle(request, response); + if (!user) { + throw new ServerError('Server error: `handle()` did not return a `user` object'); + } - if (!user) { - throw new ServerError('Server error: `handle()` did not return a `user` object'); + return user; } - return user; -}; - -/** - * Get redirect URI. - */ + /** + * Get redirect URI. + */ -AuthorizeHandler.prototype.getRedirectUri = function(request, client) { - return request.body.redirect_uri || request.query.redirect_uri || client.redirectUris[0]; -}; + getRedirectUri (request, client) { + return request.body.redirect_uri || request.query.redirect_uri || client.redirectUris[0]; + } -/** - * Save authorization code. - */ + /** + * Save authorization code. + */ + + async saveAuthorizationCode (authorizationCode, expiresAt, scope, client, redirectUri, user, codeChallenge, codeChallengeMethod) { + let code = { + authorizationCode: authorizationCode, + expiresAt: expiresAt, + redirectUri: redirectUri, + scope: scope + }; + + if(codeChallenge && codeChallengeMethod){ + code = Object.assign({ + codeChallenge: codeChallenge, + codeChallengeMethod: codeChallengeMethod + }, code); + } -AuthorizeHandler.prototype.saveAuthorizationCode = async function(authorizationCode, expiresAt, scope, client, redirectUri, user, codeChallenge, codeChallengeMethod) { - let code = { - authorizationCode: authorizationCode, - expiresAt: expiresAt, - redirectUri: redirectUri, - scope: scope - }; - - if(codeChallenge && codeChallengeMethod){ - code = Object.assign({ - codeChallenge: codeChallenge, - codeChallengeMethod: codeChallengeMethod - }, code); + return this.model.saveAuthorizationCode(code, client, user); } - return this.model.saveAuthorizationCode(code, client, user); -}; + async validateRedirectUri (redirectUri, client) { + if (this.model.validateRedirectUri) { + return this.model.validateRedirectUri(redirectUri, client); + } -AuthorizeHandler.prototype.validateRedirectUri = async function(redirectUri, client) { - if (this.model.validateRedirectUri) { - return this.model.validateRedirectUri(redirectUri, client); + return client.redirectUris.includes(redirectUri); } + /** + * Get response type. + */ - return client.redirectUris.includes(redirectUri); -}; -/** - * Get response type. - */ + getResponseType (request) { + const responseType = request.body.response_type || request.query.response_type; -AuthorizeHandler.prototype.getResponseType = function(request) { - const responseType = request.body.response_type || request.query.response_type; + if (!responseType) { + throw new InvalidRequestError('Missing parameter: `response_type`'); + } - if (!responseType) { - throw new InvalidRequestError('Missing parameter: `response_type`'); - } + if (!Object.prototype.hasOwnProperty.call(responseTypes, responseType)) { + throw new UnsupportedResponseTypeError('Unsupported response type: `response_type` is not supported'); + } - if (!Object.prototype.hasOwnProperty.call(responseTypes, responseType)) { - throw new UnsupportedResponseTypeError('Unsupported response type: `response_type` is not supported'); + return responseTypes[responseType]; } - return responseTypes[responseType]; -}; + /** + * Build a successful response that redirects the user-agent to the client-provided url. + */ -/** - * Build a successful response that redirects the user-agent to the client-provided url. - */ + buildSuccessRedirectUri (redirectUri, responseType) { + return responseType.buildRedirectUri(redirectUri); + } -AuthorizeHandler.prototype.buildSuccessRedirectUri = function(redirectUri, responseType) { - return responseType.buildRedirectUri(redirectUri); -}; + /** + * Build an error response that redirects the user-agent to the client-provided url. + */ -/** - * Build an error response that redirects the user-agent to the client-provided url. - */ + buildErrorRedirectUri (redirectUri, error) { + const uri = url.parse(redirectUri); -AuthorizeHandler.prototype.buildErrorRedirectUri = function(redirectUri, error) { - const uri = url.parse(redirectUri); + uri.query = { + error: error.name + }; - uri.query = { - error: error.name - }; + if (error.message) { + uri.query.error_description = error.message; + } - if (error.message) { - uri.query.error_description = error.message; + return uri; } - return uri; -}; + /** + * Update response with the redirect uri and the state parameter, if available. + */ -/** - * Update response with the redirect uri and the state parameter, if available. - */ + updateResponse (response, redirectUri, state) { + redirectUri.query = redirectUri.query || {}; -AuthorizeHandler.prototype.updateResponse = function(response, redirectUri, state) { - redirectUri.query = redirectUri.query || {}; + if (state) { + redirectUri.query.state = state; + } - if (state) { - redirectUri.query.state = state; + response.redirect(url.format(redirectUri)); } - response.redirect(url.format(redirectUri)); -}; - -AuthorizeHandler.prototype.getCodeChallenge = function(request) { - return request.body.code_challenge; -}; - -/** - * Get code challenge method from request or defaults to plain. - * https://www.rfc-editor.org/rfc/rfc7636#section-4.3 - * - * @throws {InvalidRequestError} if request contains unsupported code_challenge_method - * (see https://www.rfc-editor.org/rfc/rfc7636#section-4.4) - */ -AuthorizeHandler.prototype.getCodeChallengeMethod = function(request) { - const algorithm = request.body.code_challenge_method; - - if (algorithm && !pkce.isValidMethod(algorithm)) { - throw new InvalidRequestError(`Invalid request: transform algorithm '${algorithm}' not supported`); + getCodeChallenge (request) { + return request.body.code_challenge; } - return algorithm || 'plain'; -}; + /** + * Get code challenge method from request or defaults to plain. + * https://www.rfc-editor.org/rfc/rfc7636#section-4.3 + * + * @throws {InvalidRequestError} if request contains unsupported code_challenge_method + * (see https://www.rfc-editor.org/rfc/rfc7636#section-4.4) + */ + getCodeChallengeMethod (request) { + const algorithm = request.body.code_challenge_method; + + if (algorithm && !pkce.isValidMethod(algorithm)) { + throw new InvalidRequestError(`Invalid request: transform algorithm '${algorithm}' not supported`); + } + return algorithm || 'plain'; + } +} /** * Export constructor. */ diff --git a/lib/handlers/token-handler.js b/lib/handlers/token-handler.js index 468d8102..6ce6c215 100644 --- a/lib/handlers/token-handler.js +++ b/lib/handlers/token-handler.js @@ -34,264 +34,266 @@ const grantTypes = { * Constructor. */ -function TokenHandler(options) { - options = options || {}; +class TokenHandler { + constructor (options) { + options = options || {}; - if (!options.accessTokenLifetime) { - throw new InvalidArgumentError('Missing parameter: `accessTokenLifetime`'); - } + if (!options.accessTokenLifetime) { + throw new InvalidArgumentError('Missing parameter: `accessTokenLifetime`'); + } - if (!options.model) { - throw new InvalidArgumentError('Missing parameter: `model`'); - } + if (!options.model) { + throw new InvalidArgumentError('Missing parameter: `model`'); + } - if (!options.refreshTokenLifetime) { - throw new InvalidArgumentError('Missing parameter: `refreshTokenLifetime`'); - } + if (!options.refreshTokenLifetime) { + throw new InvalidArgumentError('Missing parameter: `refreshTokenLifetime`'); + } + + if (!options.model.getClient) { + throw new InvalidArgumentError('Invalid argument: model does not implement `getClient()`'); + } - if (!options.model.getClient) { - throw new InvalidArgumentError('Invalid argument: model does not implement `getClient()`'); + this.accessTokenLifetime = options.accessTokenLifetime; + this.grantTypes = Object.assign({}, grantTypes, options.extendedGrantTypes); + this.model = options.model; + this.refreshTokenLifetime = options.refreshTokenLifetime; + this.allowExtendedTokenAttributes = options.allowExtendedTokenAttributes; + this.requireClientAuthentication = options.requireClientAuthentication || {}; + this.alwaysIssueNewRefreshToken = options.alwaysIssueNewRefreshToken !== false; } - this.accessTokenLifetime = options.accessTokenLifetime; - this.grantTypes = Object.assign({}, grantTypes, options.extendedGrantTypes); - this.model = options.model; - this.refreshTokenLifetime = options.refreshTokenLifetime; - this.allowExtendedTokenAttributes = options.allowExtendedTokenAttributes; - this.requireClientAuthentication = options.requireClientAuthentication || {}; - this.alwaysIssueNewRefreshToken = options.alwaysIssueNewRefreshToken !== false; -} + /** + * Token Handler. + */ -/** - * Token Handler. - */ + async handle (request, response) { + if (!(request instanceof Request)) { + throw new InvalidArgumentError('Invalid argument: `request` must be an instance of Request'); + } -TokenHandler.prototype.handle = async function(request, response) { - if (!(request instanceof Request)) { - throw new InvalidArgumentError('Invalid argument: `request` must be an instance of Request'); - } + if (!(response instanceof Response)) { + throw new InvalidArgumentError('Invalid argument: `response` must be an instance of Response'); + } - if (!(response instanceof Response)) { - throw new InvalidArgumentError('Invalid argument: `response` must be an instance of Response'); - } + if (request.method !== 'POST') { + throw new InvalidRequestError('Invalid request: method must be POST'); + } - if (request.method !== 'POST') { - throw new InvalidRequestError('Invalid request: method must be POST'); - } + if (!request.is('application/x-www-form-urlencoded')) { + throw new InvalidRequestError('Invalid request: content must be application/x-www-form-urlencoded'); + } - if (!request.is('application/x-www-form-urlencoded')) { - throw new InvalidRequestError('Invalid request: content must be application/x-www-form-urlencoded'); - } + 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); - 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); - this.updateSuccessResponse(response, tokenType); + return data; + } catch (err) { + let e = err; - return data; - } catch (err) { - let e = err; + if (!(e instanceof OAuthError)) { + e = new ServerError(e); + } - if (!(e instanceof OAuthError)) { - e = new ServerError(e); + this.updateErrorResponse(response, e); + throw e; } - - this.updateErrorResponse(response, e); - throw e; } -}; -/** - * Get the client from the model. - */ + /** + * Get the client from the model. + */ -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 }); + async getClient (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 }); - if (!credentials.clientId) { - throw new InvalidRequestError('Missing parameter: `client_id`'); - } + if (!credentials.clientId) { + throw new InvalidRequestError('Missing parameter: `client_id`'); + } - if (this.isClientAuthenticationRequired(grantType) && !credentials.clientSecret && !isPkce) { - throw new InvalidRequestError('Missing parameter: `client_secret`'); - } + if (this.isClientAuthenticationRequired(grantType) && !credentials.clientSecret && !isPkce) { + throw new InvalidRequestError('Missing parameter: `client_secret`'); + } - if (!isFormat.vschar(credentials.clientId)) { - throw new InvalidRequestError('Invalid parameter: `client_id`'); - } + if (!isFormat.vschar(credentials.clientId)) { + throw new InvalidRequestError('Invalid parameter: `client_id`'); + } - if (credentials.clientSecret && !isFormat.vschar(credentials.clientSecret)) { - throw new InvalidRequestError('Invalid parameter: `client_secret`'); - } + if (credentials.clientSecret && !isFormat.vschar(credentials.clientSecret)) { + throw new InvalidRequestError('Invalid parameter: `client_secret`'); + } - try { - const client = await this.model.getClient(credentials.clientId, credentials.clientSecret); + 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; + } + } - if (!client) { - throw new InvalidClientError('Invalid client: client is invalid'); + /** + * Get client credentials. + * + * The client credentials may be sent using the HTTP Basic authentication scheme or, alternatively, + * the `client_id` and `client_secret` can be embedded in the body. + * + * @see https://tools.ietf.org/html/rfc6749#section-2.3.1 + */ + + getClientCredentials (request) { + const credentials = auth(request); + const grantType = request.body.grant_type; + const codeVerifier = request.body.code_verifier; + + if (credentials) { + return { clientId: credentials.name, clientSecret: credentials.pass }; } - if (!client.grants) { - throw new ServerError('Server error: missing client `grants`'); + if (request.body.client_id && request.body.client_secret) { + return { clientId: request.body.client_id, clientSecret: request.body.client_secret }; } - if (!(client.grants instanceof Array)) { - throw new ServerError('Server error: `grants` must be an array'); + if (pkce.isPKCERequest({ grantType, codeVerifier })) { + if(request.body.client_id) { + return { clientId: request.body.client_id }; + } } - 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 }); + if (!this.isClientAuthenticationRequired(grantType)) { + if(request.body.client_id) { + return { clientId: request.body.client_id }; + } } - throw e; + throw new InvalidClientError('Invalid client: cannot retrieve client credentials'); } -}; - -/** - * Get client credentials. - * - * The client credentials may be sent using the HTTP Basic authentication scheme or, alternatively, - * the `client_id` and `client_secret` can be embedded in the body. - * - * @see https://tools.ietf.org/html/rfc6749#section-2.3.1 - */ -TokenHandler.prototype.getClientCredentials = function(request) { - const credentials = auth(request); - const grantType = request.body.grant_type; - const codeVerifier = request.body.code_verifier; + /** + * Handle grant type. + */ - if (credentials) { - return { clientId: credentials.name, clientSecret: credentials.pass }; - } - - if (request.body.client_id && request.body.client_secret) { - return { clientId: request.body.client_id, clientSecret: request.body.client_secret }; - } + async handleGrantType (request, client) { + const grantType = request.body.grant_type; - if (pkce.isPKCERequest({ grantType, codeVerifier })) { - if(request.body.client_id) { - return { clientId: request.body.client_id }; + if (!grantType) { + throw new InvalidRequestError('Missing parameter: `grant_type`'); } - } - if (!this.isClientAuthenticationRequired(grantType)) { - if(request.body.client_id) { - return { clientId: request.body.client_id }; + if (!isFormat.nchar(grantType) && !isFormat.uri(grantType)) { + throw new InvalidRequestError('Invalid parameter: `grant_type`'); } - } - throw new InvalidClientError('Invalid client: cannot retrieve client credentials'); -}; + if (!Object.prototype.hasOwnProperty.call(this.grantTypes, grantType)) { + throw new UnsupportedGrantTypeError('Unsupported grant type: `grant_type` is invalid'); + } -/** - * Handle grant type. - */ + if (!Array.isArray(client.grants) || !client.grants.includes(grantType)) { + throw new UnauthorizedClientError('Unauthorized client: `grant_type` is invalid'); + } -TokenHandler.prototype.handleGrantType = async function(request, client) { - const grantType = request.body.grant_type; + const accessTokenLifetime = this.getAccessTokenLifetime(client); + const refreshTokenLifetime = this.getRefreshTokenLifetime(client); + const Type = this.grantTypes[grantType]; - if (!grantType) { - throw new InvalidRequestError('Missing parameter: `grant_type`'); - } + const options = { + accessTokenLifetime: accessTokenLifetime, + model: this.model, + refreshTokenLifetime: refreshTokenLifetime, + alwaysIssueNewRefreshToken: this.alwaysIssueNewRefreshToken + }; - if (!isFormat.nchar(grantType) && !isFormat.uri(grantType)) { - throw new InvalidRequestError('Invalid parameter: `grant_type`'); + return new Type(options).handle(request, client); } - if (!Object.prototype.hasOwnProperty.call(this.grantTypes, grantType)) { - throw new UnsupportedGrantTypeError('Unsupported grant type: `grant_type` is invalid'); - } + /** + * Get access token lifetime. + */ - if (!Array.isArray(client.grants) || !client.grants.includes(grantType)) { - throw new UnauthorizedClientError('Unauthorized client: `grant_type` is invalid'); + getAccessTokenLifetime (client) { + return client.accessTokenLifetime || this.accessTokenLifetime; } - const accessTokenLifetime = this.getAccessTokenLifetime(client); - const refreshTokenLifetime = this.getRefreshTokenLifetime(client); - const Type = this.grantTypes[grantType]; - - const options = { - accessTokenLifetime: accessTokenLifetime, - model: this.model, - refreshTokenLifetime: refreshTokenLifetime, - alwaysIssueNewRefreshToken: this.alwaysIssueNewRefreshToken - }; - - return new Type(options).handle(request, client); -}; - -/** - * Get access token lifetime. - */ - -TokenHandler.prototype.getAccessTokenLifetime = function(client) { - return client.accessTokenLifetime || this.accessTokenLifetime; -}; - -/** - * Get refresh token lifetime. - */ + /** + * Get refresh token lifetime. + */ -TokenHandler.prototype.getRefreshTokenLifetime = function(client) { - return client.refreshTokenLifetime || this.refreshTokenLifetime; -}; + getRefreshTokenLifetime (client) { + return client.refreshTokenLifetime || this.refreshTokenLifetime; + } -/** - * Get token type. - */ + /** + * Get token type. + */ -TokenHandler.prototype.getTokenType = function(model) { - return new BearerTokenType(model.accessToken, model.accessTokenLifetime, model.refreshToken, model.scope, model.customAttributes); -}; + getTokenType (model) { + return new BearerTokenType(model.accessToken, model.accessTokenLifetime, model.refreshToken, model.scope, model.customAttributes); + } -/** - * Update response when a token is generated. - */ + /** + * Update response when a token is generated. + */ -TokenHandler.prototype.updateSuccessResponse = function(response, tokenType) { - response.body = tokenType.valueOf(); + updateSuccessResponse (response, tokenType) { + response.body = tokenType.valueOf(); - response.set('Cache-Control', 'no-store'); - response.set('Pragma', 'no-cache'); -}; + response.set('Cache-Control', 'no-store'); + response.set('Pragma', 'no-cache'); + } -/** - * Update response when an error is thrown. - */ + /** + * Update response when an error is thrown. + */ -TokenHandler.prototype.updateErrorResponse = function(response, error) { - response.body = { - error: error.name, - error_description: error.message - }; + updateErrorResponse (response, error) { + response.body = { + error: error.name, + error_description: error.message + }; - response.status = error.code; -}; + response.status = error.code; + } -/** - * Given a grant type, check if client authentication is required - */ -TokenHandler.prototype.isClientAuthenticationRequired = function(grantType) { - if (Object.keys(this.requireClientAuthentication).length > 0) { - return (typeof this.requireClientAuthentication[grantType] !== 'undefined') ? this.requireClientAuthentication[grantType] : true; - } else { - return true; + /** + * Given a grant type, check if client authentication is required + */ + isClientAuthenticationRequired (grantType) { + if (Object.keys(this.requireClientAuthentication).length > 0) { + return (typeof this.requireClientAuthentication[grantType] !== 'undefined') ? this.requireClientAuthentication[grantType] : true; + } else { + return true; + } } -}; +} /** * Export constructor. diff --git a/lib/server.js b/lib/server.js index aca56d2a..a73acd63 100644 --- a/lib/server.js +++ b/lib/server.js @@ -13,62 +13,64 @@ const TokenHandler = require('./handlers/token-handler'); * Constructor. */ -function OAuth2Server(options) { - options = options || {}; +class OAuth2Server { + constructor (options) { + options = options || {}; - if (!options.model) { - throw new InvalidArgumentError('Missing parameter: `model`'); - } + if (!options.model) { + throw new InvalidArgumentError('Missing parameter: `model`'); + } - this.options = options; -} + this.options = options; + } -/** + /** * Authenticate a token. * Note, that callback will soon be deprecated! */ -OAuth2Server.prototype.authenticate = function(request, response, options) { - if (typeof options === 'string') { - options = {scope: options}; - } + authenticate (request, response, options) { + if (typeof options === 'string') { + options = {scope: options}; + } - options = Object.assign({ - addAcceptedScopesHeader: true, - addAuthorizedScopesHeader: true, - allowBearerTokensInQueryString: false - }, this.options, options); + options = Object.assign({ + addAcceptedScopesHeader: true, + addAuthorizedScopesHeader: true, + allowBearerTokensInQueryString: false + }, this.options, options); - return new AuthenticateHandler(options).handle(request, response); -}; + return new AuthenticateHandler(options).handle(request, response); + } -/** + /** * Authorize a request. */ -OAuth2Server.prototype.authorize = function(request, response, options) { - options = Object.assign({ - allowEmptyState: false, - authorizationCodeLifetime: 5 * 60 // 5 minutes. - }, this.options, options); + authorize (request, response, options) { + options = Object.assign({ + allowEmptyState: false, + authorizationCodeLifetime: 5 * 60 // 5 minutes. + }, this.options, options); - return new AuthorizeHandler(options).handle(request, response); -}; + return new AuthorizeHandler(options).handle(request, response); + } -/** + /** * Create a token. */ -OAuth2Server.prototype.token = function(request, response, options) { - options = Object.assign({ - accessTokenLifetime: 60 * 60, // 1 hour. - refreshTokenLifetime: 60 * 60 * 24 * 14, // 2 weeks. - allowExtendedTokenAttributes: false, - requireClientAuthentication: {} // defaults to true for all grant types - }, this.options, options); + token (request, response, options) { + options = Object.assign({ + accessTokenLifetime: 60 * 60, // 1 hour. + refreshTokenLifetime: 60 * 60 * 24 * 14, // 2 weeks. + allowExtendedTokenAttributes: false, + requireClientAuthentication: {} // defaults to true for all grant types + }, this.options, options); - return new TokenHandler(options).handle(request, response); -}; + return new TokenHandler(options).handle(request, response); + } +} /** * Export constructor. diff --git a/lib/token-types/bearer-token-type.js b/lib/token-types/bearer-token-type.js index 0bf526d0..02b73517 100644 --- a/lib/token-types/bearer-token-type.js +++ b/lib/token-types/bearer-token-type.js @@ -10,50 +10,52 @@ const InvalidArgumentError = require('../errors/invalid-argument-error'); * Constructor. */ -function BearerTokenType(accessToken, accessTokenLifetime, refreshToken, scope, customAttributes) { - if (!accessToken) { - throw new InvalidArgumentError('Missing parameter: `accessToken`'); - } +class BearerTokenType { + constructor(accessToken, accessTokenLifetime, refreshToken, scope, customAttributes) { + if (!accessToken) { + throw new InvalidArgumentError('Missing parameter: `accessToken`'); + } - this.accessToken = accessToken; - this.accessTokenLifetime = accessTokenLifetime; - this.refreshToken = refreshToken; - this.scope = scope; + this.accessToken = accessToken; + this.accessTokenLifetime = accessTokenLifetime; + this.refreshToken = refreshToken; + this.scope = scope; - if (customAttributes) { - this.customAttributes = customAttributes; + if (customAttributes) { + this.customAttributes = customAttributes; + } } -} -/** + /** * Retrieve the value representation. */ -BearerTokenType.prototype.valueOf = function() { - const object = { - access_token: this.accessToken, - token_type: 'Bearer' - }; + valueOf () { + const object = { + access_token: this.accessToken, + token_type: 'Bearer' + }; - if (this.accessTokenLifetime) { - object.expires_in = this.accessTokenLifetime; - } + if (this.accessTokenLifetime) { + object.expires_in = this.accessTokenLifetime; + } - if (this.refreshToken) { - object.refresh_token = this.refreshToken; - } + if (this.refreshToken) { + object.refresh_token = this.refreshToken; + } - if (this.scope) { - object.scope = this.scope; - } + if (this.scope) { + object.scope = this.scope; + } - for (const key in this.customAttributes) { - if ( Object.prototype.hasOwnProperty.call(this.customAttributes, key) ) { - object[key] = this.customAttributes[key]; + for (const key in this.customAttributes) { + if ( Object.prototype.hasOwnProperty.call(this.customAttributes, key) ) { + object[key] = this.customAttributes[key]; + } } + return object; } - return object; -}; +} /** * Export constructor. diff --git a/lib/token-types/mac-token-type.js b/lib/token-types/mac-token-type.js index a5dd240a..2d90fbe8 100644 --- a/lib/token-types/mac-token-type.js +++ b/lib/token-types/mac-token-type.js @@ -10,8 +10,10 @@ const ServerError = require('../errors/server-error'); * Constructor. */ -function MacTokenType() { - throw new ServerError('Not implemented.'); +class MacTokenType { + constructor() { + throw new ServerError('Not implemented.'); + } } /**