初始登录时, 只申请基本的权限, 在需要时再申请drive权限

This commit is contained in:
CounterFire2023 2023-09-18 16:25:18 +08:00
parent 259b587bc5
commit dd639ed72c
8 changed files with 180 additions and 16 deletions

View File

@ -2,31 +2,31 @@ const scripts = ['https://apis.google.com/js/api.js', 'https://accounts.google.c
const DISCOVERY_DOC = 'https://www.googleapis.com/discovery/v1/apis/drive/v3/rest';
const CLIENT_ID = '53206975661-ih3r0ubph3rqejdq97b029difbrk2bqj.apps.googleusercontent.com';
// Authorization scopes required by the API; multiple scopes can be
// included, separated by spaces.
const SCOPES = 'openid email profile https://www.googleapis.com/auth/drive.appdata';
// Authorization scopes required by the API; multiple scopes can be included, separated by spaces.
const SCOPES = 'openid email profile';
const SCOPES_DRIVE = 'https://www.googleapis.com/auth/drive.appdata';
export class GoogleClient {
async initGApi() {
return new Promise((resolve, reject) => {
gapi.load('client', async () => {
await gapi.client.init({
discoveryDocs: [DISCOVERY_DOC],
});
await gapi.client.init({ discoveryDocs: [DISCOVERY_DOC] });
resolve();
});
});
}
initTokenClient(scopes, cb) {
this.tokenClient = google.accounts.oauth2.initTokenClient({
client_id: CLIENT_ID,
scope: scopes,
callback: cb // defined later
});
console.log('google client init success');
}
async initGoolgeClient() {
await Promise.all([loadSingleScript([scripts[0]]), loadSingleScript([scripts[1]])]);
await this.initGApi();
this.tokenClient = google.accounts.oauth2.initTokenClient({
client_id: CLIENT_ID,
scope: SCOPES,
callback: '', // defined later
});
console.log('google client init success');
this.initTokenClient(SCOPES);
}
login(funid) {
this.tokenClient.callback = async (resp) => {
@ -51,4 +51,82 @@ export class GoogleClient {
this.tokenClient.requestAccessToken({ prompt: '' });
}
}
generateCfgName(key) {
return `wallet_${key}.json`;
}
hasDriveScope() {
return google.accounts.oauth2.hasGrantedAnyScope(gapi.client.getToken(), SCOPES_DRIVE);
}
async requestDriveScope() {
return new Promise((resolve, reject) => {
this.initTokenClient(SCOPES + " " + SCOPES_DRIVE, (resp) => {
if (resp.error !== undefined) {
throw resp;
}
resolve();
});
this.tokenClient.requestAccessToken({ prompt: 'consent' });
});
}
async listFiles() {
return gapi.client.drive.files.list({
spaces: 'appDataFolder',
fields: 'files(id, name)',
});
}
async downloadCfg(key) {
if (!this.hasDriveScope()) {
await this.requestDriveScope();
}
let fileName = this.generateCfgName(key)
let files = await this.listFiles();
let file = files.result.files.find((file) => file.name.toLowerCase() === fileName.toLowerCase());
if (file) {
let fileId = file.id;
let info = await gapi.client.drive.files.get({
fileId: fileId,
alt: 'media',
});
return info.result;
}
}
async uploadOneFile(content, fileName) {
return new Promise((resolve, reject) => {
var file = new Blob([JSON.stringify(content)], {type: 'application/json'});
var metadata = {
'name': fileName, // Filename at Google Drive
'mimeType': 'application/json', // mimeType at Google Drive
'parents': ['appDataFolder'], // Folder ID at Google Drive
};
var accessToken = gapi.auth.getToken().access_token; // Here gapi is used for retrieving the access token.
var form = new FormData();
form.append('metadata', new Blob([JSON.stringify(metadata)], {type: 'application/json'}));
form.append('file', file);
var xhr = new XMLHttpRequest();
xhr.open('post', 'https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart&fields=id');
xhr.setRequestHeader('Authorization', 'Bearer ' + accessToken);
xhr.responseType = 'json';
xhr.onload = () => {
console.log(xhr.response.id); // Retrieve uploaded file ID.
resolve && resolve(xhr.response);
};
xhr.send(form);
});
}
async uploadCfg(key, pass) {
if (!this.hasDriveScope()) {
await this.requestDriveScope();
}
let fileName = this.generateCfgName(key);
let files = await this.listFiles();
let file = files.result.files.find((file) => file.name.toLowerCase() === fileName.toLowerCase());
if (!file) {
let res = await this.uploadOneFile({pass}, fileName);
return res;
} else {
return file;
}
}
}

View File

@ -6,6 +6,8 @@ import init, {
generate_sec_key,
generate_scrypt_hash,
generate_qr,
encrypt_content,
decrypt_content,
} from '../../wasm/rustwallet.js';
import { QrScanner, showQr } from './qr_reading.js';
import { GoogleClient } from './google.client.js';
@ -131,13 +133,30 @@ window.jsb = {
callNative({ action: 'finishTransaction', funid, transactionId });
},
authGetStoragePass: function (funid, key) {
callNative({ action: 'authGetStoragePass', funid, key });
if (jc.wallet.platform === 'web') {
googleClient.downloadCfg(key).then((data) => {
console.log(data);
let r = decrypt_content(data.pass, key);
let result = {errcode: 0, data: r};
nativeCall(funid, JSON.stringify(result));
});
} else {
callNative({ action: 'authGetStoragePass', funid, key });
}
},
passStorageState: function (funid, key) {
callNative({ action: 'passStorageState', funid, key });
},
storagePass: function (funid, key, pass) {
callNative({ action: 'storagePass', funid, key, pass });
if (jc.wallet.platform === 'web') {
pass = encrypt_content(pass, key);
googleClient.uploadCfg(key, pass).then((data) => {
let result = {errcode: 0, data: data.id};
nativeCall(funid, JSON.stringify(result));
});
} else {
callNative({ action: 'storagePass', funid, key, pass });
}
},
// END:: native method
};

View File

@ -184,6 +184,14 @@ const pages = {
let res = await callMethod('emailInfo');
console.log(res);
},
getPass: async function() {
let res = await callMethod('restorePassLocal', '0x1c2d7640d9510569ef2687b7729be8e1583c2781');
console.log(res);
},
savePassCloud: async function() {
let res = await callMethod('storePassLocal', '0x1c2d7640d9510569ef2687b7729be8e1583c2781', '111111');
console.log(res);
}
};
// 根据上面的事件列表, 在页面上显示按钮

View File

@ -87,6 +87,18 @@ export function wdecrypt(id: string, openid: string, master_key: string, salt: s
* @param {string} key
* @returns {string}
*/
export function encrypt_content(content: string, key: string): string;
/**
* @param {string} content
* @param {string} key
* @returns {string}
*/
export function decrypt_content(content: string, key: string): string;
/**
* @param {string} content
* @param {string} key
* @returns {string}
*/
export function aes_encrypt(content: string, key: string): string;
/**
* @param {string} content
@ -152,6 +164,8 @@ export interface InitOutput {
readonly wget_address: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number) => void;
readonly wencrypt: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number, l: number, m: number) => void;
readonly wdecrypt: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number, l: number, m: number) => void;
readonly encrypt_content: (a: number, b: number, c: number, d: number, e: number) => void;
readonly decrypt_content: (a: number, b: number, c: number, d: number, e: number) => void;
readonly aes_encrypt: (a: number, b: number, c: number, d: number, e: number) => void;
readonly aes_decrypt: (a: number, b: number, c: number, d: number, e: number) => void;
readonly rencrypt: (a: number, b: number, c: number, d: number, e: number) => void;

View File

@ -384,6 +384,50 @@ export function wdecrypt(id, openid, master_key, salt, pass, msg) {
}
}
/**
* @param {string} content
* @param {string} key
* @returns {string}
*/
export function encrypt_content(content, key) {
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
const ptr0 = passStringToWasm0(content, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
const ptr1 = passStringToWasm0(key, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len1 = WASM_VECTOR_LEN;
wasm.encrypt_content(retptr, ptr0, len0, ptr1, len1);
var r0 = getInt32Memory0()[retptr / 4 + 0];
var r1 = getInt32Memory0()[retptr / 4 + 1];
return getStringFromWasm0(r0, r1);
} finally {
wasm.__wbindgen_add_to_stack_pointer(16);
wasm.__wbindgen_free(r0, r1);
}
}
/**
* @param {string} content
* @param {string} key
* @returns {string}
*/
export function decrypt_content(content, key) {
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
const ptr0 = passStringToWasm0(content, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
const ptr1 = passStringToWasm0(key, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len1 = WASM_VECTOR_LEN;
wasm.decrypt_content(retptr, ptr0, len0, ptr1, len1);
var r0 = getInt32Memory0()[retptr / 4 + 0];
var r1 = getInt32Memory0()[retptr / 4 + 1];
return getStringFromWasm0(r0, r1);
} finally {
wasm.__wbindgen_add_to_stack_pointer(16);
wasm.__wbindgen_free(r0, r1);
}
}
/**
* @param {string} content
* @param {string} key

Binary file not shown.

View File

@ -10,6 +10,8 @@ export function generate_scrypt_hash(a: number, b: number, c: number, d: number,
export function wget_address(a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number): void;
export function wencrypt(a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number, l: number, m: number): void;
export function wdecrypt(a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number, l: number, m: number): void;
export function encrypt_content(a: number, b: number, c: number, d: number, e: number): void;
export function decrypt_content(a: number, b: number, c: number, d: number, e: number): void;
export function aes_encrypt(a: number, b: number, c: number, d: number, e: number): void;
export function aes_decrypt(a: number, b: number, c: number, d: number, e: number): void;
export function rencrypt(a: number, b: number, c: number, d: number, e: number): void;

View File

@ -63,7 +63,6 @@
}, false);
document.body.appendChild(s);
})
};
var scripts = [
['assets/scripts/libs/jcwallet.js'],