diff --git a/README.md b/README.md index 52cc778..0025a09 100644 --- a/README.md +++ b/README.md @@ -110,6 +110,8 @@ app.get('/auth/github/callback', function (req, res) { }) ``` +**P.S.** The `getToken` URI parameter can be an object containing `pathname` and `query` properties. + ### [Implicit Grant](http://tools.ietf.org/html/rfc6749#section-4.2) > The implicit grant type is used to obtain access tokens (it does not support the issuance of refresh tokens) and is optimized for public clients known to operate a particular redirection URI. These clients are typically implemented in a browser using a scripting language such as JavaScript. @@ -137,6 +139,8 @@ window.oauth2Callback = function (uri) { window.open(githubAuth.token.getUri()) ``` +**P.S.** The `getToken` URI parameter can be an object containing `pathname`, `query` and `hash` properties. + ### [Resource Owner Password Credentials Grant](http://tools.ietf.org/html/rfc6749#section-4.3) > The resource owner password credentials grant type is suitable in cases where the resource owner has a trust relationship with the client, such as the device operating system or a highly privileged application. The authorization server should take special care when enabling this grant type and only allow it when other flows are not viable. diff --git a/index.d.ts b/index.d.ts index 76f1fa9..96c7834 100644 --- a/index.d.ts +++ b/index.d.ts @@ -48,6 +48,16 @@ declare namespace ClientOAuth2 { }; } + export interface UrlObject { + hash?: string | { + [key: string]: string | string[]; + }; + query?: string | { + [key: string]: string | string[]; + } + pathname?: string; + } + export class Token { client: ClientOAuth2; data: Data; @@ -65,13 +75,13 @@ declare namespace ClientOAuth2 { export class CodeFlow { constructor(client: ClientOAuth2); getUri(options?: Options): string; - getToken(uri: string, options?: Options): Promise; + getToken(uri: string | UrlObject, options?: Options): Promise; } export class TokenFlow { constructor(client: ClientOAuth2); getUri(options?: Options): string; - getToken(uri: string, options?: Options): Promise; + getToken(uri: string | UrlObject, options?: Options): Promise; } export class OwnerFlow { diff --git a/src/client-oauth2.js b/src/client-oauth2.js index 8f12f9d..8292b8e 100644 --- a/src/client-oauth2.js +++ b/src/client-oauth2.js @@ -457,23 +457,25 @@ TokenFlow.prototype.getUri = function (options) { /** * Get the user access token from the uri. * - * @param {string} uri - * @param {Object} [options] + * @param {string|Object} uri + * @param {Object} [options] * @return {Promise} */ TokenFlow.prototype.getToken = function (uri, options) { options = extend(this.client.options, options) - var url = Url.parse(uri) + var url = typeof uri === 'object' ? uri : Url.parse(uri, true) var expectedUrl = Url.parse(options.redirectUri) - if (url.pathname !== expectedUrl.pathname) { - return Promise.reject(new TypeError('Should match redirect uri: ' + uri)) + if (typeof url.pathname === 'string' && url.pathname !== expectedUrl.pathname) { + return Promise.reject( + new TypeError('Redirected path should match configured path, but got: ' + url.pathname) + ) } // If no query string or fragment exists, we won't be able to parse // any useful information from the uri. - if (!url.hash && !url.search) { + if (!url.hash && !url.query) { return Promise.reject(new TypeError('Unable to process uri: ' + uri)) } @@ -481,8 +483,8 @@ TokenFlow.prototype.getToken = function (uri, options) { // important, but the query string is also used because some OAuth 2.0 // implementations (Instagram) have a bug where state is passed via query. var data = extend( - url.query ? Querystring.parse(url.query) : {}, - url.hash ? Querystring.parse(url.hash.substr(1)) : {} + typeof url.query === 'string' ? Querystring.parse(url.query) : (url.query || {}), + typeof url.hash === 'string' ? Querystring.parse(url.hash.substr(1)) : (url.hash || {}) ) var err = getAuthError(data) @@ -571,8 +573,8 @@ CodeFlow.prototype.getUri = function (options) { * Get the code token from the redirected uri and make another request for * the user access token. * - * @param {string} uri - * @param {Object} [options] + * @param {string|Object} uri + * @param {Object} [options] * @return {Promise} */ CodeFlow.prototype.getToken = function (uri, options) { @@ -587,26 +589,28 @@ CodeFlow.prototype.getToken = function (uri, options) { 'accessTokenUri' ]) - var url = Url.parse(uri) + var url = typeof uri === 'object' ? uri : Url.parse(uri, true) var expectedUrl = Url.parse(options.redirectUri) - if (url.pathname !== expectedUrl.pathname) { - return Promise.reject(new TypeError('Should match redirect uri: ' + uri)) + if (typeof url.pathname === 'string' && url.pathname !== expectedUrl.pathname) { + return Promise.reject( + new TypeError('Redirected path should match configured path, but got: ' + url.pathname) + ) } - if (!url.search) { + if (!url.query) { return Promise.reject(new TypeError('Unable to process uri: ' + uri)) } - var data = Querystring.parse(url.query) + var data = typeof url.query === 'string' ? Querystring.parse(url.query) : (url.query || {}) var err = getAuthError(data) if (err) { return Promise.reject(err) } - if (options.state && data.state !== options.state) { - return Promise.reject(new TypeError('Invalid state:' + data.state)) + if (options.state != null && data.state !== options.state) { + return Promise.reject(new TypeError('Invalid state: ' + data.state)) } // Check whether the response code is set.