Switch to a “Desktop” Google Client ID to satisfy new security rules

This commit is contained in:
Ben Gotow 2022-08-08 20:30:12 -05:00
parent e24c433b55
commit ad30bcf641
6 changed files with 101 additions and 51 deletions

View file

@ -6,7 +6,7 @@ import http from 'http';
import url from 'url';
import FormErrorMessage from './form-error-message';
import { LOCAL_SERVER_PORT } from './onboarding-helpers';
import { LOCAL_SERVER_PORT } from './onboarding-constants';
import AccountProviders from './account-providers';
interface OAuthSignInPageProps {
@ -14,7 +14,7 @@ interface OAuthSignInPageProps {
buildAccountFromAuthResponse: (rep: any) => Account | Promise<Account>;
onSuccess: (account: Account) => void;
onTryAgain: () => void;
providerConfig: (typeof AccountProviders)[0];
providerConfig: typeof AccountProviders[0];
serviceName: string;
}

View file

@ -0,0 +1,66 @@
import crypto from 'crypto';
import uuidv4 from 'uuid/v4';
export const LOCAL_SERVER_PORT = 12141;
export const GMAIL_CLIENT_ID =
process.env.MS_GMAIL_CLIENT_ID ||
'662287800555-pdiq3r3puob8a44locitndbocua7c30f.apps.googleusercontent.com';
// per https://stackoverflow.com/questions/59416326/safely-distribute-oauth-2-0-client-secret-in-desktop-applications-in-python,
// we really do need to embed this in the application and it's more an extension of the Client ID than a proper Client Secret.
//
// We could run a small web app that receives the code and exchanges it for the refresh token (storing this on the server), but
// that web flow would still hand the resulting client secret to the desktop app, whose authenticity it can't verify.
// (It can verify the connection is secure, but not that the receiving party is /this/ copy of Mailspring.)
//
// Note: This is not a security risk for the end-user -- it just means someone could "fork" Mailspring and re-use it's
// Client ID and Secret. For now, it seems we're on the honor code - Please don't do this.
//
export const GMAIL_CLIENT_SECRET = crypto
.createDecipheriv(
'aes-256-ctr',
"don't-be-ev1l-thanks--mailspring",
Buffer.from('wgvAx+N05nHqhFxJ9I07jw==', 'base64')
)
.update(Buffer.from('1EyEGYVh3NBNIbYEdpdMvOzCH7+vrSciGeYZ1F+W6W+yShk=', 'base64'))
.toString('utf8');
export const GMAIL_SCOPES = [
'https://mail.google.com/', // email
'https://www.googleapis.com/auth/userinfo.email', // email address
'https://www.googleapis.com/auth/userinfo.profile', // G+ profile
'https://www.googleapis.com/auth/contacts', // contacts
'https://www.googleapis.com/auth/calendar', // calendar
];
export const O365_CLIENT_ID =
process.env.MS_O365_CLIENT_ID || '8787a430-6eee-41e1-b914-681d90d35625';
export const O365_SCOPES = [
'user.read', // email address
'offline_access',
'Contacts.ReadWrite', // contacts
'Contacts.ReadWrite.Shared', // contacts
'Calendars.ReadWrite', // calendar
'Calendars.ReadWrite.Shared', // calendar
// Future note: When you exchange the refresh token for an access token, you may
// request these two OR the above set but NOT BOTH, because Microsoft has mapped
// two underlying systems with different tokens onto the single flow and you
// need to get an outlook token and not a Micrsosoft Graph token to use these APIs.
// https://stackoverflow.com/questions/61597263/
'https://outlook.office.com/IMAP.AccessAsUser.All', // email
'https://outlook.office.com/SMTP.Send', // email
];
// Re-created only at onboarding page load / auth session start because storing
// verifier would require additional state refactoring
export const CODE_VERIFIER = uuidv4();
export const CODE_CHALLENGE = crypto
.createHash('sha256')
.update(CODE_VERIFIER, 'utf8')
.digest('base64')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '');

View file

@ -2,56 +2,27 @@
import qs from 'querystring';
import crypto from 'crypto';
import uuidv4 from 'uuid/v4';
import { Account, AccountStore, IdentityStore, MailsyncProcess, localized } from 'mailspring-exports';
import {
Account,
AccountStore,
IdentityStore,
MailsyncProcess,
localized,
} from 'mailspring-exports';
import MailspringProviderSettings from './mailspring-provider-settings.json';
import MailcoreProviderSettings from './mailcore-provider-settings.json';
import dns from 'dns';
import fetch from 'node-fetch';
export const LOCAL_SERVER_PORT = 12141;
const GMAIL_CLIENT_ID =
process.env.MS_GMAIL_CLIENT_ID ||
'662287800555-0a5h4ii0e9hsbpq0mqtul7fja0jhf9uf.apps.googleusercontent.com';
const O365_CLIENT_ID = process.env.MS_O365_CLIENT_ID || '8787a430-6eee-41e1-b914-681d90d35625';
const GMAIL_SCOPES = [
'https://mail.google.com/', // email
'https://www.googleapis.com/auth/userinfo.email', // email address
'https://www.googleapis.com/auth/userinfo.profile', // G+ profile
'https://www.googleapis.com/auth/contacts', // contacts
'https://www.googleapis.com/auth/calendar', // calendar
];
const O365_SCOPES = [
'user.read', // email address
'offline_access',
'Contacts.ReadWrite', // contacts
'Contacts.ReadWrite.Shared', // contacts
'Calendars.ReadWrite', // calendar
'Calendars.ReadWrite.Shared', // calendar
// Future note: When you exchange the refresh token for an access token, you may
// request these two OR the above set but NOT BOTH, because Microsoft has mapped
// two underlying systems with different tokens onto the single flow and you
// need to get an outlook token and not a Micrsosoft Graph token to use these APIs.
// https://stackoverflow.com/questions/61597263/
'https://outlook.office.com/IMAP.AccessAsUser.All', // email
'https://outlook.office.com/SMTP.Send', // email
];
// Re-created only at onboarding page load / auth session start because storing
// verifier would require additional state refactoring
const CODE_VERIFIER = uuidv4();
const CODE_CHALLENGE = crypto
.createHash('sha256')
.update(CODE_VERIFIER, 'utf8')
.digest('base64')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '');
import {
GMAIL_CLIENT_ID,
GMAIL_CLIENT_SECRET,
LOCAL_SERVER_PORT,
O365_SCOPES,
O365_CLIENT_ID,
CODE_VERIFIER,
GMAIL_SCOPES,
CODE_CHALLENGE,
} from './onboarding-constants';
interface TokenResponse {
access_token: string;
@ -206,7 +177,11 @@ export async function expandAccountWithCommonSettings(account: Account) {
// https://protonmail.com/support/knowledge-base/creating-folders/#comment-10460
// on protonmail by default Folders set as container folder
const containerFolderDefault = AccountStore.containerFolderDefaultGetter();
if (containerFolderDefault !== 'Mailspring' && (populated.settings.container_folder === '' || populated.settings.container_folder === undefined)) {
if (
containerFolderDefault !== 'Mailspring' &&
(populated.settings.container_folder === '' ||
populated.settings.container_folder === undefined)
) {
populated.settings.container_folder = containerFolderDefault;
}
return populated;
@ -219,6 +194,7 @@ export async function buildGmailAccountFromAuthResponse(code: string) {
{
code: code,
client_id: GMAIL_CLIENT_ID,
client_secret: GMAIL_CLIENT_SECRET,
redirect_uri: `http://127.0.0.1:${LOCAL_SERVER_PORT}`,
grant_type: 'authorization_code',
}

View file

@ -12,6 +12,11 @@ import fs from 'fs';
import { localized } from './intl';
import { IIdentity, Account } from 'mailspring-exports';
import {
GMAIL_CLIENT_ID,
GMAIL_CLIENT_SECRET,
} from '../internal_packages/onboarding/lib/onboarding-constants';
let Utils = null;
export interface MailsyncProcessExit {
@ -137,6 +142,8 @@ export class MailsyncProcess extends EventEmitter {
_spawnProcess(mode) {
const env = {
CONFIG_DIR_PATH: this.configDirPath,
GMAIL_CLIENT_ID: GMAIL_CLIENT_ID,
GMAIL_CLIENT_SECRET: GMAIL_CLIENT_SECRET,
IDENTITY_SERVER: 'unknown',
};
if (process.type === 'renderer') {

View file

@ -61,7 +61,8 @@ global.finishWithWindowCapture = (previewPath, startedAt = Date.now()) => {
window.requestAnimationFrame(() => {
window.requestAnimationFrame(() => {
window.requestAnimationFrame(() => {
const win = const { BrowserWindow } = require('@electron/remote').getCurrentWindow();
const { BrowserWindow } = require('@electron/remote');
const win = BrowserWindow.getCurrentWindow();
win.capturePage(img => {
fs.writeFileSync(previewPath, img.toPNG());
document.title = 'Finished';

@ -1 +1 @@
Subproject commit 59366b938347b0ce67a43237cd164eb6e6e68d3a
Subproject commit fb0bcb32decaf523ab8547dc7546b32d6d1962c0