diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /services/fxaccounts/FxAccountsOAuthGrantClient.jsm | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'services/fxaccounts/FxAccountsOAuthGrantClient.jsm')
-rw-r--r-- | services/fxaccounts/FxAccountsOAuthGrantClient.jsm | 239 |
1 files changed, 239 insertions, 0 deletions
diff --git a/services/fxaccounts/FxAccountsOAuthGrantClient.jsm b/services/fxaccounts/FxAccountsOAuthGrantClient.jsm new file mode 100644 index 0000000000..33795598af --- /dev/null +++ b/services/fxaccounts/FxAccountsOAuthGrantClient.jsm @@ -0,0 +1,239 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Firefox Accounts OAuth Grant Client allows clients to obtain + * an OAuth token from a BrowserID assertion. Only certain client + * IDs support this privilege. + */ + +var EXPORTED_SYMBOLS = [ + "FxAccountsOAuthGrantClient", + "FxAccountsOAuthGrantClientError", +]; + +const { + ERRNO_NETWORK, + ERRNO_PARSE, + ERRNO_UNKNOWN_ERROR, + ERROR_CODE_METHOD_NOT_ALLOWED, + ERROR_MSG_METHOD_NOT_ALLOWED, + ERROR_NETWORK, + ERROR_PARSE, + ERROR_UNKNOWN, + OAUTH_SERVER_ERRNO_OFFSET, + log, +} = ChromeUtils.import("resource://gre/modules/FxAccountsCommon.js"); +const { RESTRequest } = ChromeUtils.import( + "resource://services-common/rest.js" +); +const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); +const { XPCOMUtils } = ChromeUtils.import( + "resource://gre/modules/XPCOMUtils.jsm" +); + +XPCOMUtils.defineLazyGlobalGetters(this, ["URL"]); + +const AUTH_ENDPOINT = "/authorization"; +const HOST_PREF = "identity.fxaccounts.remote.oauth.uri"; + +// This is the same pref that's used by FxAccounts.jsm. +const ALLOW_HTTP_PREF = "identity.fxaccounts.allowHttp"; + +/** + * Create a new FxAccountsOAuthClient for the browser. + * + * @param {Object} options Options + * @param {String} options.client_id + * OAuth client id under which this client is registered + * @param {String} options.parameters.serverURL + * The FxA OAuth server URL + * @constructor + */ +var FxAccountsOAuthGrantClient = function(options = {}) { + this.parameters = { ...options }; + if (!this.parameters.serverURL) { + this.parameters.serverURL = Services.prefs.getCharPref(HOST_PREF); + } + this._validateOptions(this.parameters); + + try { + this.serverURL = new URL(this.parameters.serverURL); + } catch (e) { + throw new Error("Invalid 'serverURL'"); + } + + let forceHTTPS = !Services.prefs.getBoolPref(ALLOW_HTTP_PREF, false); + if (forceHTTPS && this.serverURL.protocol != "https:") { + throw new Error("'serverURL' must be HTTPS"); + } + + log.debug("FxAccountsOAuthGrantClient Initialized"); +}; + +FxAccountsOAuthGrantClient.prototype = { + /** + * Retrieves an OAuth access token for the signed in user + * + * @param {Object} assertion BrowserID assertion + * @param {String} scope OAuth scope + * @param {Number} ttl token time to live + * @return Promise + * Resolves: {Object} Object with access_token property + */ + getTokenFromAssertion(assertion, scope, ttl) { + if (!assertion) { + throw new Error("Missing 'assertion' parameter"); + } + if (!scope) { + throw new Error("Missing 'scope' parameter"); + } + let params = { + scope, + client_id: this.parameters.client_id, + assertion, + response_type: "token", + ttl, + }; + + return this._createRequest(AUTH_ENDPOINT, "POST", params); + }, + + /** + * Validates the required FxA OAuth parameters + * + * @param options {Object} + * OAuth client options + * @private + */ + _validateOptions(options) { + if (!options) { + throw new Error("Missing configuration options"); + } + + ["serverURL", "client_id"].forEach(option => { + if (!options[option]) { + throw new Error("Missing '" + option + "' parameter"); + } + }); + }, + + /** + * Interface for making remote requests. + */ + _Request: RESTRequest, + + /** + * Remote request helper + * + * @param {String} path + * OAuth server path, i.e "/token". + * @param {String} [method] + * Type of request, i.e "GET". + * @return Promise + * Resolves: {Object} Successful response from the Oauth server. + * Rejects: {FxAccountsOAuthGrantClientError} OAuth client error. + * @private + */ + async _createRequest(path, method = "POST", params) { + let requestUrl = this.serverURL + path; + let request = new this._Request(requestUrl); + method = method.toUpperCase(); + + request.setHeader("Accept", "application/json"); + request.setHeader("Content-Type", "application/json"); + + if (method != "POST") { + throw new FxAccountsOAuthGrantClientError({ + error: ERROR_NETWORK, + errno: ERRNO_NETWORK, + code: ERROR_CODE_METHOD_NOT_ALLOWED, + message: ERROR_MSG_METHOD_NOT_ALLOWED, + }); + } + + try { + await request.post(params); + } catch (error) { + throw new FxAccountsOAuthGrantClientError({ + error: ERROR_NETWORK, + errno: ERRNO_NETWORK, + message: error.toString(), + }); + } + + let body = null; + try { + body = JSON.parse(request.response.body); + } catch (e) { + throw new FxAccountsOAuthGrantClientError({ + error: ERROR_PARSE, + errno: ERRNO_PARSE, + code: request.response.status, + message: request.response.body, + }); + } + + if (request.response.success) { + return body; + } + + if (typeof body.errno === "number") { + // Offset oauth server errnos to avoid conflict with other FxA server errnos + body.errno += OAUTH_SERVER_ERRNO_OFFSET; + } else if (body.errno) { + body.errno = ERRNO_UNKNOWN_ERROR; + } + throw new FxAccountsOAuthGrantClientError(body); + }, +}; + +/** + * Normalized oauth client errors + * @param {Object} [details] + * Error details object + * @param {number} [details.code] + * Error code + * @param {number} [details.errno] + * Error number + * @param {String} [details.error] + * Error description + * @param {String|null} [details.message] + * Error message + * @constructor + */ +var FxAccountsOAuthGrantClientError = function(details) { + details = details || {}; + + this.name = "FxAccountsOAuthGrantClientError"; + this.code = details.code || null; + this.errno = details.errno || ERRNO_UNKNOWN_ERROR; + this.error = details.error || ERROR_UNKNOWN; + this.message = details.message || null; +}; + +/** + * Returns error object properties + * + * @returns {{name: *, code: *, errno: *, error: *, message: *}} + * @private + */ +FxAccountsOAuthGrantClientError.prototype._toStringFields = function() { + return { + name: this.name, + code: this.code, + errno: this.errno, + error: this.error, + message: this.message, + }; +}; + +/** + * String representation of a oauth grant client error + * + * @returns {String} + */ +FxAccountsOAuthGrantClientError.prototype.toString = function() { + return this.name + "(" + JSON.stringify(this._toStringFields()) + ")"; +}; |