|
@@ -0,0 +1,145 @@
|
|
|
+# HG changeset patch
|
|
|
+# User Magnus Melin <mkmelin+mozilla@iki.fi>
|
|
|
+# Date 1694771644 -10800
|
|
|
+# Node ID e8a762f17c72b92cc982e71f1b6f7eefd4de1202
|
|
|
+# Parent 1749f7af16cbf5f31e4d1db5c8658dcccd8d9451
|
|
|
+Bug 1814823 - Handle stale OAuth2 scope. r=leftmostcat a=wsmwk
|
|
|
+
|
|
|
+Add better logging, and always use hardcoded details from the providers we have them for
|
|
|
+(everyone, atm. since we don't yet support dynamic registration).
|
|
|
+
|
|
|
+Differential Revision: https://phabricator.services.mozilla.com/D188241
|
|
|
+
|
|
|
+diff --git a/mail/components/accountcreation/content/createInBackend.js b/mail/components/accountcreation/content/createInBackend.js
|
|
|
+--- a/mail/components/accountcreation/content/createInBackend.js
|
|
|
++++ b/mail/components/accountcreation/content/createInBackend.js
|
|
|
+@@ -24,18 +24,21 @@ function createAccountInBackend(config)
|
|
|
+ sanitize.enum(config.incoming.type, ["pop3", "imap", "nntp"]));
|
|
|
+ inServer.port = config.incoming.port;
|
|
|
+ inServer.authMethod = config.incoming.auth;
|
|
|
+ inServer.password = config.incoming.password;
|
|
|
+ if (config.rememberPassword && config.incoming.password.length)
|
|
|
+ rememberPassword(inServer, config.incoming.password);
|
|
|
+
|
|
|
+ if (inServer.authMethod == Ci.nsMsgAuthMethod.OAuth2) {
|
|
|
+- inServer.setCharValue("oauth2.scope", config.incoming.oauthSettings.scope);
|
|
|
+- inServer.setCharValue(
|
|
|
++ inServer.setUnicharValue(
|
|
|
++ "oauth2.scope",
|
|
|
++ config.incoming.oauthSettings.scope
|
|
|
++ );
|
|
|
++ inServer.setUnicharValue(
|
|
|
+ "oauth2.issuer",
|
|
|
+ config.incoming.oauthSettings.issuer
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ // SSL
|
|
|
+ if (config.incoming.socketType == 1) // plain
|
|
|
+ inServer.socketType = Ci.nsMsgSocketType.plain;
|
|
|
+diff --git a/mailnews/base/src/msgOAuth2Module.js b/mailnews/base/src/msgOAuth2Module.js
|
|
|
+--- a/mailnews/base/src/msgOAuth2Module.js
|
|
|
++++ b/mailnews/base/src/msgOAuth2Module.js
|
|
|
+@@ -14,46 +14,44 @@ ChromeUtils.import("resource://gre/modul
|
|
|
+ * bearer token it can use to authenticate in SASL steps.
|
|
|
+ * It also takes care of persising the refreshToken for later usage.
|
|
|
+ *
|
|
|
+ * @implements {msgIOAuth2Module}
|
|
|
+ */
|
|
|
+ function OAuth2Module() {}
|
|
|
+
|
|
|
+ OAuth2Module.prototype = {
|
|
|
+- // XPCOM registration stuff
|
|
|
+ QueryInterface: XPCOMUtils.generateQI([Ci.msgIOAuth2Module]),
|
|
|
+ classID: Components.ID("{b63d8e4c-bf60-439b-be0e-7c9f67291042}"),
|
|
|
+
|
|
|
+ initFromSmtp(aServer) {
|
|
|
+ return this._initPrefs("mail.smtpserver." + aServer.key + ".",
|
|
|
+ aServer.username, aServer.hostname);
|
|
|
+ },
|
|
|
+ initFromMail(aServer) {
|
|
|
+ return this._initPrefs("mail.server." + aServer.key + ".",
|
|
|
+ aServer.realUsername, aServer.realHostName);
|
|
|
+ },
|
|
|
+ _initPrefs(root, aUsername, aHostname) {
|
|
|
+ // Load all of the parameters from preferences.
|
|
|
+ let issuer = Services.prefs.getStringPref(root + "oauth2.issuer", "");
|
|
|
+ let scope = Services.prefs.getStringPref(root + "oauth2.scope", "");
|
|
|
+
|
|
|
+- // These properties are absolutely essential to OAuth2 support. If we don't
|
|
|
+- // have them, we don't support OAuth2.
|
|
|
++ let details = OAuth2Providers.getHostnameDetails(aHostname);
|
|
|
++ if (details) {
|
|
|
++ // Found in the list of hardcoded providers. Use the hardcoded values.
|
|
|
++ [issuer, scope] = details;
|
|
|
++ // Store them for the future, can be useful once we support
|
|
|
++ // dynamic registration.
|
|
|
++ Services.prefs.setStringPref(root + "oauth2.issuer", issuer);
|
|
|
++ Services.prefs.setStringPref(root + "oauth2.scope", scope);
|
|
|
++ }
|
|
|
+ if (!issuer || !scope) {
|
|
|
+- // Since we currently only support gmail, init values if server matches.
|
|
|
+- let details = OAuth2Providers.getHostnameDetails(aHostname);
|
|
|
+- if (details)
|
|
|
+- {
|
|
|
+- [issuer, scope] = details;
|
|
|
+- Services.prefs.setStringPref(root + "oauth2.issuer", issuer);
|
|
|
+- Services.prefs.setStringPref(root + "oauth2.scope", scope);
|
|
|
+- }
|
|
|
+- else
|
|
|
+- return false;
|
|
|
++ // We need these properties for OAuth2 support.
|
|
|
++ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Find the app key we need for the OAuth2 string. Eventually, this should
|
|
|
+ // be using dynamic client registration, but there are no current
|
|
|
+ // implementations that we can test this with.
|
|
|
+ const issuerDetails = OAuth2Providers.getIssuerDetails(issuer);
|
|
|
+ if (!issuerDetails.clientId) {
|
|
|
+ return false;
|
|
|
+diff --git a/mailnews/base/util/OAuth2.jsm b/mailnews/base/util/OAuth2.jsm
|
|
|
+--- a/mailnews/base/util/OAuth2.jsm
|
|
|
++++ b/mailnews/base/util/OAuth2.jsm
|
|
|
+@@ -180,23 +180,39 @@ OAuth2.prototype = {
|
|
|
+
|
|
|
+ this._browserRequest._active = false;
|
|
|
+ if ("_listener" in this._browserRequest) {
|
|
|
+ this._browserRequest._listener._cleanUp();
|
|
|
+ }
|
|
|
+ delete this._browserRequest;
|
|
|
+ },
|
|
|
+
|
|
|
+- // @see RFC 6749 section 4.1.2: Authorization Response
|
|
|
++ /**
|
|
|
++ * @param {string} aURL - Redirection URI with additional parameters.
|
|
|
++ */
|
|
|
+ onAuthorizationReceived(aURL) {
|
|
|
+- this.log.info("OAuth2 authorization received: url=" + aURL);
|
|
|
++ this.log.info("OAuth2 authorization response received: url=" + aURL);
|
|
|
++ // Services.console.logStringMessage("OAuth2 authorization response received: url=" + aURL);
|
|
|
+ const url = new URL(aURL);
|
|
|
+ if (url.searchParams.has("code")) {
|
|
|
++ // @see RFC 6749 section 4.1.2: Authorization Response
|
|
|
+ this.requestAccessToken(url.searchParams.get("code"), false);
|
|
|
+ } else {
|
|
|
++ // @see RFC 6749 section 4.1.2.1: Error Response
|
|
|
++ if (url.searchParams.has("error")) {
|
|
|
++ let error = url.searchParams.get("error");
|
|
|
++ let errorDescription = url.searchParams.get("error_description") || "";
|
|
|
++ if (error == "invalid_scope") {
|
|
|
++ errorDescription += ` Invalid scope: ${this.scope}.`;
|
|
|
++ }
|
|
|
++ if (url.searchParams.has("error_uri")) {
|
|
|
++ errorDescription += ` See ${url.searchParams.get("error_uri")}.`;
|
|
|
++ }
|
|
|
++ this.log.error(`Authorization error [${error}]: ${errorDescription}`);
|
|
|
++ }
|
|
|
+ this.onAuthorizationFailed(null, aURL);
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ onAuthorizationFailed(aError, aData) {
|
|
|
+ this.connectFailureCallback(aData);
|
|
|
+ },
|
|
|
+
|