Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Google account imrovements #397

Merged
merged 8 commits into from
Jul 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions .github/workflows/release-candidate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
name: Release Candidate
on:
push:
tags:
- 'v*-b*'

jobs:
Linux:
runs-on: ubuntu-latest
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GOOGLE_CLIENT_ID: ${{ secrets.GOOGLE_CLIENT_ID }}
GOOGLE_CLIENT_SECRET: ${{ secrets.GOOGLE_CLIENT_SECRET }}
steps:
- uses: actions/checkout@master
- uses: actions/setup-node@master
with:
node-version: "18"
cache: "yarn"
# See https://github.com/nodejs/node-gyp/blob/main/docs/Force-npm-to-use-global-node-gyp.md
# https://github.com/nodejs/node-gyp/blob/main/docs/Updating-npm-bundled-node-gyp.md
# - name: Update node-gyp
# run: |
# npm install --global [email protected]
# npm config set node_gyp $(npm prefix -g)/lib/node_modules/node-gyp/bin/node-gyp.js
- name: Install dependencies
run: yarn install --immutable
- name: Build and Release
run: yarn release

MacOs:
runs-on: macos-13
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
CSC_LINK: ${{ secrets.CSC_LINK }}
AC_USERNAME: ${{ secrets.AC_USERNAME }}
AC_PASSWORD: ${{ secrets.AC_PASSWORD }}
AC_TEAM_ID: ${{ secrets.AC_TEAM_ID }}
GOOGLE_CLIENT_ID: ${{ secrets.GOOGLE_CLIENT_ID }}
GOOGLE_CLIENT_SECRET: ${{ secrets.GOOGLE_CLIENT_SECRET }}
steps:
- uses: actions/checkout@master
- uses: actions/setup-node@master
with:
node-version: "18"
cache: "yarn"
- name: Configure Node
run: |
# npm v9 doesn't allow custom config parameters (i.e. node_gyp) anymode, so we have to downgrade it to v8
npm install -g npm@8
npm install -g node-gyp@latest
npm config set node_gyp $(npm prefix -g)/lib/node_modules/node-gyp/bin/node-gyp.js
- name: Install dependencies
run: yarn install --immutable
- name: Build and Release
run: yarn release

Windows:
runs-on: windows-2019
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
CSC_LINK: ${{ secrets.CSC_LINK }}
GOOGLE_CLIENT_ID: ${{ secrets.GOOGLE_CLIENT_ID }}
GOOGLE_CLIENT_SECRET: ${{ secrets.GOOGLE_CLIENT_SECRET }}
steps:
- uses: actions/checkout@master
- uses: actions/setup-node@master
with:
node-version: "18"
- name: Configure Node
shell: powershell
run: |
# npm v9 doesn't allow custom config parameters (i.e. node_gyp) anymode, so we have to downgrade it to v8
npm install -g npm@8
npm install -g node-gyp@latest
npm prefix -g | % {npm config set node_gyp "$_\node_modules\node-gyp\bin\node-gyp.js"}
- name: Install dependencies
run: yarn install --immutable
- name: Build and Release
run: yarn release
Binary file added .yarn/install-state.gz
Binary file not shown.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "station-project",
"private": true,
"productName": "Station",
"version": "3.1.0-b1",
"version": "3.1.0-b3",
"description": "Station",
"homepage": "https://getstation.com",
"author": {
Expand Down
2 changes: 1 addition & 1 deletion packages/app/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "station-desktop-app",
"productName": "Station",
"version": "3.1.0-b1",
"version": "3.1.0-b3",
"description": "Station",
"homepage": "https://getstation.com",
"author": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,14 @@ export const JAVASCRIPT_INJECTIONS = {
gmail: ['gmailInjectedScript'],
slack: ['slackInjectedScript'],
'station-support': ['slackInjectedScript'],
'gdrive-mu': ['removeGoogleAccountInjectedScript'],
'gdrive-mu': ['removeGoogleAccountInjectedScript'],
'gcalendar-mu': ['removeGoogleAccountInjectedScript'],
'google-cloud': ['removeGoogleAccountInjectedScript'], //vk: FIXME: doesn't work
'google-keep': ['removeGoogleAccountInjectedScript'],
'meet': ['removeGoogleAccountInjectedScript'],
outlook: ['office365InjectedScript'],
'office-365': ['office365InjectedScript'],
'outlook-pro': ['office365InjectedScript'],
'google-keep': ['removeGoogleAccountInjectedScript'],
'facebook-messenger': ['messengerInjectedScript'],
'whatsapp': ['whatsappInjectedScript'],
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import { Schema$Person } from 'googleapis/build/src/apis/plus/v1';
import { people_v1 } from 'googleapis/build/src/apis/people/v1';
import { ServiceBase } from '../../lib/class';
import { service, timeout } from '../../lib/decorator';
import { RPC } from '../../lib/types';
import { Credentials } from 'google-auth-library/build/src/auth/credentials';
import { Credentials } from 'google-auth-library';

export type ElectronGoogleSignInResponse = {
tokens: Credentials,
profile: people_v1.Schema$Person,
}

@service('electron-google-oauth')
export class ElectronGoogleOAuthService extends ServiceBase implements RPC.Interface<ElectronGoogleOAuthService> {
@timeout(0)
// @ts-ignore
signIn(scopes: string[], forceAddSession?: boolean): Promise<{
tokens: Credentials,
profile: Schema$Person,
}> {}
signIn(scopes: string[], forceAddSession?: boolean): Promise<ElectronGoogleSignInResponse> {}
}
86 changes: 75 additions & 11 deletions packages/app/src/services/services/electron-google-oauth/main.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,95 @@
import { google } from 'googleapis';
import { people_v1 } from 'googleapis/build/src/apis/people/v1';
import { Credentials } from 'google-auth-library';
import ElectronGoogleOAuth2 from '@getstation/electron-google-oauth2';
import log from 'electron-log';

import { RPC } from '../../lib/types';
import { ElectronGoogleOAuthService } from './interface';
import { ElectronGoogleOAuthService, ElectronGoogleSignInResponse } from './interface';

const CLIENT_ID = process.env.GOOGLE_CLIENT_ID!;
const CLIENT_SECRET = process.env.GOOGLE_CLIENT_SECRET!;

export class ElectronGoogleOAuthServiceImpl extends ElectronGoogleOAuthService implements RPC.Interface<ElectronGoogleOAuthService> {
async signIn(scopes: string[], forceAddSession?: boolean) {
async signIn(scopes: string[], forceAddSession?: boolean): Promise<ElectronGoogleSignInResponse> {

const client = new ElectronGoogleOAuth2(CLIENT_ID, CLIENT_SECRET, scopes, { successRedirectURL: 'https://getstation.com/' });
return client.openAuthWindowAndGetTokens(forceAddSession)
.then(async (tokens) => {

const service = google.people({
try {
const service = google.people({
version: 'v1',
auth: client.oauth2Client,
});
});

const response = await service.people.get({
resourceName: 'people/me',
personFields: 'names,emailAddresses,photos',
sources: ['READ_SOURCE_TYPE_PROFILE'],
});
const response = await service.people.get({
resourceName: 'people/me',
personFields: 'names,emailAddresses,photos',
sources: ['READ_SOURCE_TYPE_PROFILE'],
});

return { tokens, profile: response.data as people_v1.Schema$Person };
return { tokens, profile: response.data as people_v1.Schema$Person };
}
catch (err) {
log.error(`Google profile request error ${err}`);
return this.parseToken(tokens);
}
});
}

private parseToken(tokens: Credentials): ElectronGoogleSignInResponse {
try {
//vk: id_token format https://developers.google.com/identity/gsi/web/reference/js-reference#credential
const decodedStr = Buffer.from(tokens.id_token!.split('.')[1], 'base64').toString()
const tokenPayload = JSON.parse(decodedStr);

return {
tokens,
profile: {
names: [
{
metadata: {
source: {
id: tokenPayload.sub,
}
},
displayName: tokenPayload.name,
givenName: tokenPayload.given_name,
familyName: tokenPayload.family_name,
}
],
emailAddresses: [
{
type: '',
value: tokenPayload.email,
}
],
photos: [
{
url: tokenPayload.picture,
}
]
}
};
}
catch (err) {
log.error(`Parse token error ${err}`);
return {
tokens,
profile: {
names: [
{
displayName: 'unknown',
}
],
emailAddresses: [
{
type: '',
value: 'unknown',
}
]
}
};
};
}
}
22 changes: 18 additions & 4 deletions packages/app/src/session.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Session, OnBeforeSendHeadersListenerDetails, BeforeSendResponse } from 'electron';
import { Session, OnBeforeSendHeadersListenerDetails, BeforeSendResponse, OnHeadersReceivedListenerDetails, HeadersReceivedResponse } from 'electron';
import enhanceWebRequest from 'electron-better-web-request';

const orderListeners = (listeners: any) => {
Expand Down Expand Up @@ -78,7 +78,7 @@ const getUserAgentForApp = (url: string, currentUserAgent: string): string => {
return defaultUserAgent;
};

const getHeaderName = (headerName: string, headers?: Record<string, string>): string | undefined => {
const getHeaderName = (headerName: string, headers?: Record<string, string[]>): string | undefined => {
if (headers) {
const lowCaseHeader = headerName.toLowerCase();
for (const key in headers) {
Expand All @@ -90,12 +90,12 @@ const getHeaderName = (headerName: string, headers?: Record<string, string>): st
return undefined;
}

export const getHeader = (headerName: string, headers?: Record<string, any>): any => {
export const getHeader = (headerName: string, headers?: Record<string, string[]>): any => {
const realHeaderName = getHeaderName(headerName, headers);
return headers && realHeaderName ? headers[realHeaderName] : undefined;
}

export const setHeader = (headerName: string, headerValue: any, headers?: Record<string, any>) => {
export const setHeader = (headerName: string, headerValue: any, headers?: Record<string, string[]>): Record<string, string[]> | undefined => {
if (headers) {
const realHeaderName = getHeaderName(headerName, headers);
return {
Expand Down Expand Up @@ -132,4 +132,18 @@ export const enhanceSession = (session: Session) => {
});
}
);

session.webRequest.onHeadersReceived(
(details: OnHeadersReceivedListenerDetails, callback: (headersReceivedResponse: HeadersReceivedResponse) => void) => {
const responseHeaders = details.responseHeaders;

if (responseHeaders) {
delete responseHeaders['content-security-policy']; //vk: causes "This document requires 'TrustedHTML' assignment." error. Does not allow us to modify page CSS.
}

callback({
responseHeaders,
})
}
)
}