初始登录时, 只申请基本的权限, 在需要时再申请drive权限
This commit is contained in:
parent
259b587bc5
commit
dd639ed72c
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
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) {
|
||||
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
|
||||
};
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
// 根据上面的事件列表, 在页面上显示按钮
|
||||
|
14
assets/wasm/rustwallet.d.ts
vendored
14
assets/wasm/rustwallet.d.ts
vendored
@ -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;
|
||||
|
@ -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.
2
assets/wasm/rustwallet_bg.wasm.d.ts
vendored
2
assets/wasm/rustwallet_bg.wasm.d.ts
vendored
@ -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;
|
||||
|
@ -63,7 +63,6 @@
|
||||
}, false);
|
||||
document.body.appendChild(s);
|
||||
})
|
||||
|
||||
};
|
||||
var scripts = [
|
||||
['assets/scripts/libs/jcwallet.js'],
|
||||
|
Loading…
x
Reference in New Issue
Block a user