Implement the mechanics of fxa login on android, but don't show ui fo… (#1000)

* Implement the mechanics of fxa login on android, but don't show ui for it yet. Also, scopedKeys are not yet implemented.

* Hopefully fix the package-lock conflict?

* WIP on android scoped keys

* Finish implementing login.

* created android/user.js to handle android logins
This commit is contained in:
Donovan Preston 2018-11-08 16:35:19 -05:00 committed by GitHub
parent ffac4ae5b1
commit cab6f1bafb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 592 additions and 304 deletions

View File

@ -1,13 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id="android" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="java-gradle" name="Java-Gradle">
<configuration>
<option name="BUILD_FOLDER_PATH" value="$MODULE_DIR$/build" />
<option name="BUILDABLE" value="false" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">

View File

@ -1,4 +1,4 @@
/* global window */
/* global window, navigator */
window.LIMITS = {
ANON: {
@ -27,14 +27,18 @@ const locale = require('../common/locales');
const home = require('../app/ui/home');
const app = choo();
if (navigator.userAgent === 'Send Android') {
assets.setPrefix('/android_asset');
}
function body(main) {
return function(state, emit) {
return html`<body class="flex flex-col items-center font-sans bg-blue-lightest md:h-screen md:bg-grey-lightest">
${header(state, emit)}
<a id="hamburger" class="absolute pin-t pin-r z-50" href="#" onclick=${clickPreferences}>
<img src=${assets.get('preferences.png')} />
</a>
${main(state, emit)}
<a id="hamburger" class="absolute pin-t pin-r z-50" href="#" onclick=${clickPreferences}>
<img src=${assets.get('preferences.png')} />
</a>
${header(state, emit)}
${main(state, emit)}
</body>`;
function clickPreferences(event) {
@ -44,15 +48,22 @@ function body(main) {
};
}
app.use(require('./stores/state').default);
app.use((state, emitter) => {
state.translate = locale.getTranslator();
state.capabilities = {}; //TODO
state.capabilities = {
account: true
}; //TODO
window.finishLogin = async function(accountInfo) {
await state.user.finishLogin(accountInfo);
emitter.emit('render');
};
// for debugging
window.appState = state;
window.appEmit = emitter.emit.bind(emitter);
});
app.use(require('./stores/state').default);
app.use(require('../app/fileManager').default);
app.use(require('./stores/intents').default);
app.route('/', body(home));

View File

@ -51,19 +51,19 @@
</facet>
</component>
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7">
<output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
<output-test url="file://$MODULE_DIR$/build/intermediates/classes/test/debug" />
<output url="file://$MODULE_DIR$/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes" />
<output-test url="file://$MODULE_DIR$/build/intermediates/javac/debugUnitTest/compileDebugUnitTestJavaWithJavac/classes" />
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/not_namespaced_r_class_sources/debug/processDebugResources/r" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/not_namespaced_r_class_sources/debugAndroidTest/processDebugAndroidTestResources/r" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" />
@ -112,31 +112,40 @@
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/shaders" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/build/.DS_Store" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/annotation_processor_list" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/apk_list" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/blame" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/build-info" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/builds" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/check-libraries" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/check-manifest" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/checkDebugClasspath" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/compatible_screen_manifest" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-runtime-classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-verifier" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant-run-apk" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant-run-main-apk-res" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaPrecompile" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant_run_main_apk_resources" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant_run_merged_manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant_run_split_apk_resources" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/javac" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jniLibs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint_jar" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifest-checker" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/merged_assets" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/merged_manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/prebuild" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/processed_res" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/reload-dex" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/resources" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/shader_assets" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/shaders" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/split-apk" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/split_list" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/splits-support" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/transforms" />
@ -146,9 +155,11 @@
</content>
<orderEntry type="jdk" jdkName="Android API 27 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Gradle: net.java.dev.jna:jna-4.5.2" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: com.android.support.test:runner-1.0.2" level="project" />
<orderEntry type="library" name="Gradle: android.arch.lifecycle:common:1.1.0@jar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-annotations:27.1.1@jar" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.2.61@jar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:animated-vector-drawable-27.1.1" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-compat-27.1.1" level="project" />
<orderEntry type="library" name="Gradle: android.arch.lifecycle:viewmodel-1.1.0" level="project" />
@ -162,21 +173,26 @@
<orderEntry type="library" name="Gradle: com.github.delight-im:Android-AdvancedWebView-v3.0.0" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: com.android.support.test.espresso:espresso-core-3.0.2" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: javax.inject:javax.inject:1@jar" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlinx:kotlinx-coroutines-android:0.23.4@jar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-fragment-27.1.1" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: junit:junit:4.12@jar" level="project" />
<orderEntry type="library" name="Gradle: android.arch.core:runtime-1.1.0" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.61@jar" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: org.hamcrest:hamcrest-core:1.3@jar" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: com.android.support.test:monitor-1.0.2" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlinx:kotlinx-coroutines-core:0.23.4@jar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:appcompat-v7-27.1.1" level="project" />
<orderEntry type="library" name="Gradle: org.mozilla.components:service-firefox-accounts-0.26.0" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: com.android.support.test.espresso:espresso-idling-resource-3.0.2" level="project" />
<orderEntry type="library" name="Gradle: com.android.support.constraint:constraint-layout-solver:1.1.2@jar" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.2.60@jar" level="project" />
<orderEntry type="library" name="Gradle: org.mozilla.fxa_client:fxa_client-0.5.1" level="project" />
<orderEntry type="library" name="Gradle: android.arch.lifecycle:livedata-core-1.1.0" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlinx:kotlinx-coroutines-core-common:0.23.4@jar" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: org.hamcrest:hamcrest-library:1.3@jar" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: org.hamcrest:hamcrest-integration:1.3@jar" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.2.60@jar" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlinx:atomicfu-common:0.10.3@jar" level="project" />
<orderEntry type="library" name="Gradle: android.arch.core:common:1.1.0@jar" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.60@jar" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.2.61@jar" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: net.sf.kxml:kxml2:2.3.0@jar" level="project" />
<orderEntry type="library" name="Gradle: android.arch.lifecycle:runtime-1.1.0" level="project" />
</component>

View File

@ -31,6 +31,7 @@ dependencies {
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation 'com.github.delight-im:Android-AdvancedWebView:v3.0.0'
implementation "org.mozilla.components:service-firefox-accounts:${rootProject.ext.android_components_version}"
}
task generateAndLinkBundle(type: Exec, description: 'Generate the android.js bundle and link it into the assets directory') {

View File

@ -8,7 +8,6 @@
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">

View File

@ -5,6 +5,7 @@ import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import im.delight.android.webview.AdvancedWebView
import android.graphics.Bitmap
import android.content.Context
import android.content.Intent
import android.annotation.SuppressLint
import android.net.Uri
@ -13,7 +14,13 @@ import android.webkit.WebMessage
import android.util.Log
import android.util.Base64
import android.webkit.ConsoleMessage
import android.webkit.JavascriptInterface
import android.webkit.WebChromeClient
import mozilla.components.service.fxa.Config
import mozilla.components.service.fxa.FirefoxAccount
import mozilla.components.service.fxa.OAuthInfo
import mozilla.components.service.fxa.Profile
import mozilla.components.service.fxa.FxaResult
internal class LoggingWebChromeClient : WebChromeClient() {
override fun onConsoleMessage(cm: ConsoleMessage): Boolean {
@ -23,9 +30,18 @@ internal class LoggingWebChromeClient : WebChromeClient() {
}
}
class WebAppInterface(private val mContext: MainActivity) {
@JavascriptInterface
fun beginOAuthFlow() {
mContext.beginOAuthFlow();
}
}
class MainActivity : AppCompatActivity(), AdvancedWebView.Listener {
private var mWebView: AdvancedWebView? = null
private var mToShare: String? = null
private var mToCall: String? = null
private var mAccount: FirefoxAccount? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -34,14 +50,17 @@ class MainActivity : AppCompatActivity(), AdvancedWebView.Listener {
mWebView = findViewById<WebView>(R.id.webview) as AdvancedWebView
mWebView!!.setListener(this, this)
mWebView!!.setWebChromeClient(LoggingWebChromeClient())
mWebView!!.addJavascriptInterface(WebAppInterface(this), "Android")
val webSettings = mWebView!!.getSettings()
webSettings.setUserAgentString("Send Android")
webSettings.setAllowUniversalAccessFromFileURLs(true)
webSettings.setJavaScriptEnabled(true)
val intent = getIntent()
val action = intent.getAction()
val type = intent.getType()
if (Intent.ACTION_SEND.equals(action) && type != null) {
if (type.equals("text/plain")) {
val sharedText = intent.getStringExtra(Intent.EXTRA_TEXT)
@ -51,12 +70,25 @@ class MainActivity : AppCompatActivity(), AdvancedWebView.Listener {
val imageUri = intent.getParcelableExtra(Intent.EXTRA_STREAM) as Uri
Log.w("INTENT", "image/ " + imageUri)
mToShare = "data:text/plain;base64," + Base64.encodeToString(imageUri.path.toByteArray(), 16).trim()
// TODO Currently this causes a Permission Denied error
// val stream = contentResolver.openInputStream(imageUri)
}
}
mWebView!!.loadUrl("file:///android_asset/android.html")
}
fun beginOAuthFlow() {
Config.custom("https://send-fxa.dev.lcip.org").then(fun (value: Config): FxaResult<Unit> {
mAccount = FirefoxAccount(value, "12cc4070a481bc73", "fxaclient://android.redirect")
mAccount?.beginOAuthFlow(arrayOf("profile", "https://identity.mozilla.com/apps/send"), true)?.then(fun (url: String): FxaResult<Unit> {
Log.w("CONFIG", "GOT A URL " + url)
this@MainActivity.runOnUiThread({
mWebView!!.loadUrl(url)
})
return FxaResult.fromValue(Unit)
})
Log.w("CONFIG", "CREATED FIREFOXACCOUNT")
return FxaResult.fromValue(Unit)
})
}
@SuppressLint("NewApi")
@ -94,7 +126,48 @@ class MainActivity : AppCompatActivity(), AdvancedWebView.Listener {
}
override fun onPageStarted(url: String, favicon: Bitmap?) {
if (url.startsWith("fxaclient")) {
// We load this here so the user doesn't see an ugly screen that says "can't handle fxaclient urls"...
mWebView!!.loadUrl("file:///android_asset/android.html")
val parsed = Uri.parse(url)
val code = parsed.getQueryParameter("code")
val state = parsed.getQueryParameter("state")
code?.let { code ->
state?.let { state ->
mAccount?.completeOAuthFlow(code, state)?.whenComplete { info ->
//displayAndPersistProfile(code, state)
val profile = mAccount?.getProfile(false)?.then(fun (profile: Profile): FxaResult<Unit> {
val accessToken = info.accessToken
val keys = info.keys
val avatar = profile.avatar
val displayName = profile.displayName
val email = profile.email
val uid = profile.uid
val toPass = "{\"accessToken\": \"${accessToken}}\", \"keys\": '${keys}', \"avatar\": \"${avatar}\", \"displayName\": \"${displayName}\", \"email\": \"${email}\", \"uid\": \"${uid}\"}"
mToCall = "finishLogin(${toPass})"
this@MainActivity.runOnUiThread({
// But then we also reload this here because we need to make sure onPageFinished runs after mToCall has been set.
// We can't guarantee that onPageFinished has already been called at this point.
mWebView!!.loadUrl("file:///android_asset/android.html")
})
return FxaResult.fromValue(Unit)
})
// TODO get k from it.keys
// TODO get profile from mAccount.getProfile
// TODO get access_token
//mToShare = "data:text/plain;base64," + Base64.encodeToString(toSend.toByteArray(), 16).trim()
}
}
}
}
Log.w("MAIN", "onPageStarted");
// account.completeOAuthFlow()
}
override fun onPageFinished(url: String) {
@ -102,14 +175,22 @@ class MainActivity : AppCompatActivity(), AdvancedWebView.Listener {
if (mToShare != null) {
Log.w("INTENT", mToShare)
val webView = findViewById<WebView>(R.id.webview) as AdvancedWebView
webView.postWebMessage(WebMessage(mToShare), Uri.EMPTY)
mWebView?.postWebMessage(WebMessage(mToShare), Uri.EMPTY)
mToShare = null
}
if (mToCall != null) {
this@MainActivity.runOnUiThread({
mWebView?.evaluateJavascript(mToCall, fun (value: String) {
// noop
})
})
mToCall = null
}
}
override fun onPageError(errorCode: Int, description: String, failingUrl: String) {
Log.w("MAIN", "onPageError")
Log.w("MAIN", "onPageError " + description)
}
override fun onDownloadRequested(url: String, suggestedFilename: String, mimeType: String, contentLength: Long, contentDisposition: String, userAgent: String) {

View File

@ -1,7 +1,7 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>

View File

@ -2,12 +2,13 @@
buildscript {
ext.kotlin_version = '1.2.60'
ext.android_components_version = '0.26.0'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.4'
classpath 'com.android.tools.build:gradle:3.2.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.2.60"
// NOTE: Do not place your application dependencies here; they belong
@ -18,6 +19,9 @@ buildscript {
allprojects {
repositories {
google()
maven {
url "https://maven.mozilla.org/maven2"
}
jcenter()
maven { url "https://jitpack.io" }
}

View File

@ -3,7 +3,7 @@ const path = require('path');
child_process.execSync('npm run build');
child_process.execSync(
`cp -R ${path.resolve(__dirname, '../dist')} ${path.resolve(
`cp -R ${path.resolve(__dirname, '../dist/*')} ${path.resolve(
__dirname,
'app/src/main/assets'
)}`

View File

@ -1,6 +1,6 @@
#Wed May 23 21:14:24 EDT 2018
#Thu Oct 11 12:20:52 EDT 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip

View File

@ -1,6 +1,6 @@
/* eslint-disable no-console */
import User from '../../app/user';
import User from '../user';
import storage from '../../app/storage';
export default function initialState(state, emitter) {

26
android/user.js Normal file
View File

@ -0,0 +1,26 @@
/* global Android */
import User from '../app/user';
import { deriveFileListKey } from '../app/fxa';
export default class AndroidUser extends User {
constructor(storage) {
super(storage);
}
async login() {
Android.beginOAuthFlow();
}
async finishLogin(accountInfo) {
const jwks = JSON.parse(accountInfo.keys);
const ikm = jwks['https://identity.mozilla.com/apps/send'].k;
const profile = {
displayName: accountInfo.displayName,
email: accountInfo.email,
avatar: accountInfo.avatar,
access_token: accountInfo.accessToken
};
profile.fileListKey = await deriveFileListKey(ikm);
this.info = profile;
}
}

View File

@ -146,12 +146,10 @@ export async function preparePkce(storage) {
return arrayToB64(new Uint8Array(challenge));
}
export async function getFileListKey(storage, bundle) {
const jwks = await decryptBundle(storage, bundle);
const jwk = jwks['https://identity.mozilla.com/apps/send'];
export async function deriveFileListKey(ikm) {
const baseKey = await crypto.subtle.importKey(
'raw',
b64ToArray(jwk.k),
b64ToArray(ikm),
{ name: 'HKDF' },
false,
['deriveKey']
@ -174,3 +172,9 @@ export async function getFileListKey(storage, bundle) {
const rawFileListKey = await crypto.subtle.exportKey('raw', fileListKey);
return arrayToB64(new Uint8Array(rawFileListKey));
}
export async function getFileListKey(storage, bundle) {
const jwks = await decryptBundle(storage, bundle);
const jwk = jwks['https://identity.mozilla.com/apps/send'];
return deriveFileListKey(jwk.k);
}

View File

@ -16,28 +16,37 @@ export default class User {
this.data = storage.user || {};
}
get info() {
return this.data || this.storage.user || {};
}
set info(data) {
this.data = data;
this.storage.user = data;
}
get avatar() {
const defaultAvatar = assets.get('user.svg');
if (this.data.avatarDefault) {
if (this.info.avatarDefault) {
return assets.get('firefox_logo-only.svg');
}
return this.data.avatar || defaultAvatar;
return this.info.avatar || defaultAvatar;
}
get name() {
return this.data.displayName;
return this.info.displayName;
}
get email() {
return this.data.email;
return this.info.email;
}
get loggedIn() {
return !!this.data.access_token;
return !!this.info.access_token;
}
get bearerToken() {
return this.data.access_token;
return this.info.access_token;
}
get maxSize() {
@ -104,15 +113,13 @@ export default class User {
const userInfo = await infoResponse.json();
userInfo.access_token = auth.access_token;
userInfo.fileListKey = await getFileListKey(this.storage, auth.keys_jwe);
this.storage.user = userInfo;
this.data = userInfo;
this.info = userInfo;
this.storage.remove('pkceVerifier');
}
logout() {
this.storage.user = null;
this.storage.clearLocalFiles();
this.data = {};
this.info = {};
}
async syncFileList() {
@ -124,7 +131,7 @@ export default class User {
try {
const encrypted = await getFileList(this.bearerToken);
const decrypted = await streamToArrayBuffer(
decryptStream(encrypted, b64ToArray(this.data.fileListKey))
decryptStream(encrypted, b64ToArray(this.info.fileListKey))
);
list = JSON.parse(textDecoder.decode(decrypted));
} catch (e) {
@ -142,7 +149,7 @@ export default class User {
textEncoder.encode(JSON.stringify(this.storage.files))
]);
const encrypted = await streamToArrayBuffer(
encryptStream(blobStream(blob), b64ToArray(this.data.fileListKey))
encryptStream(blobStream(blob), b64ToArray(this.info.fileListKey))
);
await setFileList(this.bearerToken, encrypted);
} catch (e) {
@ -152,6 +159,6 @@ export default class User {
}
toJSON() {
return this.data;
return this.info;
}
}

View File

@ -1,6 +1,6 @@
const genmap = require('../build/generate_asset_map');
const isServer = typeof genmap === 'function';
const prefix = '';
let prefix = '';
let manifest = {};
try {
//eslint-disable-next-line node/no-missing-require
@ -15,6 +15,10 @@ function getAsset(name) {
return prefix + assets[name];
}
function setPrefix(name) {
prefix = name;
}
function getMatches(match) {
return Object.keys(assets)
.filter(k => match.test(k))
@ -22,6 +26,7 @@ function getMatches(match) {
}
const instance = {
setPrefix: setPrefix,
get: getAsset,
match: getMatches,
setMiddleware: function(middleware) {

630
package-lock.json generated

File diff suppressed because it is too large Load Diff