Giter Club home page Giter Club logo

application-services's Introduction

Firefox Application Services

Application Services (a-s) is a collection of Rust Components that are used to enable Firefox applications to integrate with Firefox accounts, sync, experimentation, etc. Each component is built using a core of shared code written in Rust, wrapped with native language bindings for different platforms.

Contributing

To contribute, please review the Mozilla Community Participation Guidelines and then visit our how to contribute guide.

Contact

Get in touch with other community members on Matrix, or through issues here on GitHub.

Documentation

High-level docs

The Application Services Book contains high-level documentation about the code in this repository. It's built from the ./docs/ directory.

Package docs

We use rustdoc to document both the public API of the components and the various internal implementation details. View them on https://mozilla.github.io/application-services/book/rust-docs/fxa_client/index.html. Once you have completed the build steps, you can view the docs by running:

cargo doc --no-deps --document-private-items --open

Building

Building the Rust Components

  1. Clone or Download the repository:
  $ git clone https://github.com/mozilla/application-services # (or use the ssh link)
  $ cd application-services
  $ git submodule init
  $ git submodule update --recursive
  1. Follow these instructions to install your system-level dependencies
  2. Run the a-s Rust unit tests
cargo test

Consumer build, integration and testing

The application-services library primary consumers are Fenix (Firefox on Android) and Firefox iOS. Assure you are able to run integration tests (for Android and iOS if using MacOS) by following the instructions to build for Android and iOS integrations.

Android integration builds and helpful tools

Firefox for iOS integration

Firefox Desktop

Rust Components

./components/ contains the source for each component. Note that most components have their FFI generated by the uniffi library.

List of components

  • autofill - for storage and syncing of credit card and address information
  • crashtest - testing-purposes (crashing the Rust code)
  • fxa-client - for applications that need to sign in with FxA, access encryption keys for sync, and more.
  • logins - for storage and syncing of a user's saved login credentials
  • nimbus - for integrating with Mozilla's experimentation platform for Firefox
  • places - for storage and syncing of a user's saved browsing history
  • push - for applications to receive real-time updates via WebPush
  • support - low-level utility libraries
  • sync15 - shared library for accessing data in Firefox Sync
  • sync_manager - integrates multiple sync engines/ stores into a single framework
  • tabs - an in-memory syncing engine for remote browser tabs
  • viaduct - an HTTP request library
  • webext-storage - powers an implementation of the chrome.storage.sync WebExtension API

Note the above list is partial; see the actual list under the components directory.

application-services's People

Contributors

0x00a5 avatar ahal avatar badboy avatar bendk avatar bhearsum avatar brennie avatar dependabot-preview[bot] avatar dependabot[bot] avatar dmose avatar eliserichards avatar eoger avatar garvankeeley avatar glasserc avatar jdragojevic avatar jeddai avatar jhugman avatar jrconlin avatar linabutler avatar lougeniac64 avatar mergify[bot] avatar mhammond avatar ncalexan avatar rfk avatar rvandermeulen avatar skhamis avatar thomcc avatar tiftran avatar travis79 avatar vbudhram avatar vladikoff avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

application-services's Issues

"Global" refresh token

From #15:

I think it makes sense for the app to only ever have one refresh token. It might use that refresh token to grant multiple different access tokens with different combinations of scopes, but it'll be simpler both here and on the server if we can ensure it only has one refresh token that lists all the scopes granted to the application. But that can be for a follow-up issue.

Handling multiple/implied scopes

Case 1:
If a token with the scope "profile https://identity.mozilla.com/apps/oldsync" is cached in FirefoxAccount and if I call get_oauth_token with only profile, I should get the cached token instead of requesting a new one.

Case 2:
If a token with the scope profile is cached in FirefoxAccount and I call get_oauth_token with profile:write, I should get the profile token since it supersedes profile:write.

See related PR: https://github.com/mozilla/fxa-shared/pull/21/files

[logins] Rudimentary overview in Rustdoc

Right now, the initial landing branch doesn't have a Rustdoc "landing page" for consumers. There are enough assumptions in the existing expression, especially about Sync 1.5 and the path to Sync.next, that we should capture them for the benefit of potential consumers.

OpenSSL dependencies list

I believe that to simplify and shrink our Rust libraries builds, we should move away from OpenSSL.
Instead, we should use the ring rust library which doesn't need to be linked against a static library.
However, ring is still incomplete and we are missing some important crypto primitives, so we should consider this issue more as a laundry list.

User Primitives used Solution
reqwest TLS (on linux/android, implemented with openssl) Use the rustls-tls feature of reqwest.
sync15-adapter AEAD(HMAC::sha256/AES-256-CBC) (encrypt/decrypt) Depends on: briansmith/ring#588.

┆Issue is synchronized with this Jira Story

Sync15Service should be more explicit about the sync state machine, fixes #53

At a minimum it needs to handle backoff, but likely a lot more of the sync state machine. iOS's code might be a good model.

Some discussion: #12 (comment)

At a minimum, regardless of how it's implemented, we absolutely need to

  • Handle backoff (both from the tokenserver and the storage server).
  • Handle outdated meta/global, possibly uploading it if we're the first there.
  • Handle outdated crypto/keys, possibly uploading new keys.
  • Handle refreshing the sync token.
  • ...

[logins] Expose local metadata directly to consumers

The initial logins landing goes to a great deal of effort to splice local metadata into Sync 1.5 password records uploaded to the service. However, because Bug 555755 is still open, uploading local metadata changes to the service might actually be bad for a client that really wants to use the metadata changes! With logins based on Mentat, however, we're explicitly modeling the local metadata as local usage events. We already expose metadata about individual records: see

//! credential with [`times_used`], [`time_last_used`], and [`time_last_modified`].
. This ticket tracks exposing local metadata about the collection as a whole directly to consumers -- say, finding the 10 most recent locally-used credentials; or the most frequently locally-used credential.

Simplify travis-ci build

From #6 (comment):

  • We should have a script for building libs/ for x86_64 only (travis ci/local testing). Maybe there's a way to code re-use what we have for iOS, or not.
  • We should have a warm-up cache phase so we don't end up building/caching the same libs/ twice.
  • Unlike what we're doing right now, we should build our dependencies in a build.rs file.

Figure out how sync adapter interfaces with storage

My vague plan, at least recently has been something like:

pub struct RecordChangeset<T> {
    pub changed: Vec<BsoRecord<T>>,
    pub deleted_ids: Vec<String>,
    /// For GETs, the last sync timestamp that should be persisted after
    /// applying the records.
    /// For POSTs, this is the XIUS timestamp.
    pub timestamp: ServerTimestamp,
}

impl Sync15Service {
    pub fn get_changes<T: Sync15Record>(
        &mut self,
        since: ServerTimestamp
    ) -> Result<RecordChangeset<T>>;

    pub fn post_changes<T: Sync15Record>(
        &mut self,
        changes: RecordChangeset<T>,
        // Whether or not oversized and/or server-failed records
        // blow everything up.
        fully_atomic: bool
    ) -> Result<ServerTimestamp>; // Might want to return the Vec of failed ids too?
}

Note that we will likely want to trim down the Result to the set of errors that can actually happen for this.

Also note that the methods might be on something that isn't Sync15Service, but just occupies the role it currently holds.

Implementation note: both methods would encrypt the records immediately to avoid having more code than is necessary monomorphized for each record type.

Pros

  1. API encourages clients to store sync change metadata with the record.
  2. Minimizes callbacks between Swift and Rust. Entirely avoids having passing around FFI function pointers (both in Rust and in swift/java). Not having an event driven system also avoids problems like sync tracker's ignoreAll and such.
  3. The API makes deletions explicit and not dependent on the caller knowing about the tombstone format (unlike using JSON directly).

Cons

  1. Clients might decide that storing any sort of sync change metadata is a PITA and do a sync for each change they want to upload (e.g. Pro number 1 may backfire). I don't think this is that likely for shipping code, though.
  2. Leaves managing sync change timestamps fully up to the app (I think this is going to be more or less necessary for most designs, unfortunately).
  3. Shaped nothing like what is proposed in the lockbox sync google doc (Not sure how much this matters, there's a good chance we can either write some sort of wrapper that does what they want, or we'd have problems with it anyway).
  4. Likely requires that T get exposed over the FFI?

@mhammond I expect you'll have some opinions about this?

FxA dependency build scripts shouldn't rebuild the world every time

We should be able to do incremental builds. All three libs use autotools, so I think this would probably work out of the box if we just used normal vpath builds (e.g. when you invoke configure from a different directory than the directory where it lives).

I think this would look like:

mkdir -p build-$ARCH-$HOST
cd build-$ARCH-$HOST
../configure --host="$HOST" --with-openssl="$OPENSSL_DIR" --with-jansson="$JANSSON_DIR" && 
cd -

around https://github.com/mozilla/application-services/blob/master/libs/build-cjose-ios.sh#L40 or so, (and in the other build scripts ofc) as long as we also update the parts that grab the build artifacts out of the folders so that they look in the right place.

There shouldn't be any need to have a make clean also, because we wouldn't have the build artifacts for the wrong build lying around. In fact, keeping make clean would defeat the whole point. There might be a use case for it so it's possible that we either want to keep a script that does it, maybe.

Sorry in advance if there's a reason we aren't doing this already.

┆Issue is synchronized with this Jira Story

[logins] Ensure that a single Sync 1.5 "flow" results in a single Mentat transaction

Right now, the initial landing branch produces a lot of "empty" Mentat transactions (i.e., ones which only have a :db/txInstant) as part of the Sync 1.5 flow. This ticket tracks the narrow outcome of ensuring that a single "sync" results in a single Mentat transaction.

However, those empty transactions are a sign of a larger problem: it's not natural to build large transactions in Rust, because the transaction datatype (Entities) is awkward to manipulate. In Clojure, the transaction datatype is built of Clojure-native things (vectors, lists, maps) so it's very natural to grow a single structure for transaction. This ticket might point to larger changes in the input structures that Mentat can process.

Investigate a persist() callback.

Instead of having the app doing

fxa.operation_that_mutates_interal_state()
let json = fxa.to_json()
persistInSecureStorage(json)

We could do something like:

fxa.addPersistListener((json) => { persistInSecureStorage(json) });
fxa.operation_that_mutates_interal_state()
// Nothing to do, the callback has been called automatically in operation_that_mutates_interal_state

Sync adapter needs to save it's state

At the moment it is entirely in-memory. We probably want to leave actually persisting the state to the library user, but give them a JSON string to save (More granular updates may be necessary in the future but ATM it doesn't seem worth it).

At a minimum it should be saving: syncID (and version?) for each collection (this comes from meta/global), lastSync timestamp for each collection (which is a remote timestamp), and probably more?

Is there anything from the token we need to save? (I think the answer is no?)

FFI errors

We need to figure out how do we want to expose errors to callers (using an out err parameter), either a struct or simply an error code.

Fix leaks in fxa SDKs

These leak on every call that returns an error. This should be pretty easy to do now that we've basically hammered out the patterns.

I think this is blocked on #83. Or at least, unless there's a reason not to block it on that. (I don't mind taking this, but I'd rather not go through it twice)

Define a minimal FxA client API for oauth apps

In our meeting last week, @eoger @vladikoff and I kicked around some ideas for what API to expose the FxA client apps such as lockbox. It will hopefully be different from (and simpler than) the FxA Client API used by Firefox Desktop, because it's based on OAuth rather than our bespoke login protocol.

Here's some more detailed thoughts on what it might look like.

At a high level, we need the following functionality:

  • The ability to serialize state for storage by the application, and to restore from serialized state.
  • The ability for the application to request an OAuth access_token (and any corresponding keys) for a given set of scopes. This could fail for the following reasons:
    • The user hasn't signed in at all yet.
    • The user has signed in, but didn't grant that particular scope.
    • The user has signed in, but our tokens have expired or been revoked
      • This could happen due to explicit revokation, or password reset.
  • The ability for the application, in response to one of the above errors, to launch an OAuth flow and provide back updated credentials.

We could also provide some additional helpers on top of this basic functionality, e.g. ability to fetch and cache profile data. But the above is the core OAuth functionality on which the rest would be based.

The FxA client's internal state would need to contain:

  • The app's OAuth client_id, and OAuth issuer URL ("https://accounts.firefox.com" in production).
  • Basic info about the user who has logged in, e.g. their uid.
  • The list of scopes on that refresh_token
  • The JWKs for any corresponding keys for the above scopes

A client attempting to access, say, sync data, would begin by loading the FxAClient from serialized state:

fxaClient = FxAClient::from_seralized_state(APP_CLIENT_ID, "....")

It could then ask for an access_token and keys with which to talk to the sync service:

token, keys = fxaClient.get_access_token_and_keys(scope=["https://identity.mozilla.com/apps/oldsync"])

Unfortunately the user hasn't signed in yet, so it will return an error to the app, and the app will need to prompt the user for authorization. It calls into the client to initiate an OAuth flow:

url = fxaClient.begin_oauth_flow(
    scope=["https://identity.mozilla.com/apps/oldsync"]
    redirect_uri=APP_REGISTERED_REDIRECT_URI
)

The client would set up some internal state for an in-progress OAuth flow, such as a keys_jwk and an OAuth state token, and would return a URL for the app to load for the user. When the OAuth dance completes, the app would capture the result from the redirect and pass it back to the client:

fxaClient.complete_oauth_flow(code="ABCD", state="XYZ")

The client would do all the tricky oauth bits, like validating state, exchanging the code for some OAuth tokens, decrypting the keys_jwe, etc. It would store the results into its internal state, so that when the client asks again for tokens to talk to sync:

token, keys = fxaClient.get_access_token_and_keys(scope=["https://identity.mozilla.com/apps/oldsync"])

It can return them successfully, possibly by using its refresh_token to mint a new access_token.

I think that's almost everything that would be required for a minimal client - the token and keys from the above can be passed downstream to a sync client library for actual fetching of sync data.

@eoger @vladikoff does that make sense? How well does the above map onto your "sandwich" model prototype?

Don't hardcode CLIENT_ID

Instead, the app should be expected to provide it, either as a FirefoxAccount constructor argument or in FxAConfig.

Allow fxa-client consumers to specify authorization action

In custom implementations of the OAuth flow, implementers are able to specify an action (one of email, signup, signin, or force_auth) in the request parameters for the version of the OAuth login they would like to use. Ideally, the beginOAuthFlow() method signature could be extended to support the action desired by consumers of the library.

Async-ify the Swift API

Currently we call the rust crate code synchronously, which is very problematic since we make network requests.
I don't really know the Swift conventions, but I assume a callback function has to be passed as the last argument of an asynchronous function.

[logins] Sync 1.5 correctness and robustness

A laundry list of concerns I have with the initial landing of the logins crate:

  • verify that ServerPassword instances with missing usernames are accepted
  • verify that multiple ServerPassword instances with overlapping credentials are not content resolved to the same credential
  • verify that metadata changes are uploaded and correctly mirrored to the store

Minor enhancements:

  • make sync15-adapter manage the sync transaction ID, so that it's not a member Option
  • store the last-synced timestamp as an opaque blob rather than a floating point value
  • implement wiping (excising?) the underlying Mentat store
  • rather than :form/syncPassword, model the relationship correctly, with :sync.password/form

Major enhancements:

  • capture the current device in each login usage
  • support adding forms to the Mentat store, and correctly generate ServerPassword records for them

Design and implement a logins delivery mechanism for Lockbox

We need to work out what delivery vehicle we use for lockbox. It seems ideal if we could do the same thing other android components do (ie, have the api in the android-components repo and built using the same approach as the fxa sandvich), but whatever we come up with it, needs to be documented (including documentation for updating it), agreed to by the lockbox team and a first version released.

Add "client instance metadata" APIs and expose over FFI

We want to add capabilities for:

  • Commands / Send Tab
  • Display Name
  • Push registration and notifications handling

This requires to implement the /device API, however we should learn from our Desktop implementation and try to come up with something that doesn't look like it's been "bolted on".

This issue is to discuss how do we shape that client API to look natural for users, and how do we track all that state inside the crate.

Requires #390

Look into smaller OpenSSL builds

There are some options in the openssl build that can minimize the size of the large lib and solve all of our problems with the lib size!

We need to investigate that 🔍

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.