On the Wealthfront iOS team, developer productivity is a top priority. Our infrastructure engineers develop new components and APIs to enable our product engineers to ship features faster, while our product engineers are empowered to identify and eliminate toil independently. Recently, we targeted one source of toil in the development workflow by automating login within dev builds of the Wealthfront iOS app. iOS engineers login to the app many times a day to test new features, and automating this process has paid significant dividends.
The need for simpler authentication
During feature development, iOS developers follow an iterative build process to manually test their code:
This feedback loop represents the majority of the time necessary to ship a new feature, so tightening the loop is an effective way to boost developer productivity. We observed one such opportunity in the app login process. Just as our clients must authenticate to access their finances, our developers must do the same with their test account credentials to verify new features. Without biometric authentication, user sessions are sustained for only 15 minutes. This leads to a significant amount of time wasted on manually entering an email and password combination between incremental builds. This inefficiency motivated us to seek a faster yet secure way for developers to access the authenticated experience.
Ideally, developers need only tap a single button to log in to the dev build. Yet such a simple goal presents two challenges:
- If the account credentials are not entered, from which secure source could we retrieve them?
- Once we have the credentials, how might we access them from within the dev build?
Secure credential storage
A better developer experience cannot come at the cost of security. Even though our test accounts lack the ability to perform sensitive actions such as transferring funds, storing any Wealthfront account credentials in an insecure way would create an unnecessary vulnerability. An acceptable solution must never commit plaintext credentials to disk.
We recalled that iOS engineers already store their Jenkins CI password in a macOS keychain, the same container the OS uses to secure passwords for Safari. This password, combined with the username of the active user, could easily be mapped to test account credentials.
The contents of the system’s keychains can be viewed through the Keychain Access app and be accessed programmatically with the macOS Security framework. At Wealthfront, we aim for scalable solutions by automating as much as possible, so the latter choice suited our needs much better. Within a build run script, we could write something such as
as the beginning of a full solution.
Priming the dev build
With the account credentials now securely accessible, we needed a way to inject them into the app. Our first choice was the Launch Arguments within the build’s Xcode scheme. However, we quickly realized that Launch Arguments are statically defined, whereas the credentials we needed to pass to the app were constructed during build time. We asked: if we could not pass the credentials to the dev build, why not create a new app to ingest them? An auxiliary app could be built as part of the above script using
xcodebuild and launched with the credentials as command line arguments. However, this raised yet another problem: how might this new app communicate with the dev app to hand off the credentials?
Enter keychain access groups. iOS supports keychains just like macOS, and keychain access groups allow a collection of apps from a single development team to share secret items between one another. This provided a secure repository to which the helper app could write and from which the dev app could read.
Enabling keychain access groups is as simple as enabling the Keychain Sharing capability in the Xcode target of each app and defining a shared group identifier in the Keychain Groups list. Apple recommends that this identifier look like a bundle ID, which in our case was
Building the credential helper app
Developers who have worked with iOS keychains before know that the API can be unwieldy, in part because of frequent bridging between Core Foundation and Swift types. Our codebase already included a keychain-interface, used to store Wealthfront client session keys, but we decided to pull in the SwiftKeychainWrapper library as a dependency. This clever library wraps the keychain API in a more familiar interface, closer to that of UserDefaults.
Below is the
UIApplicationDelegate source of the helper app we built.
Between launch and self termination, the helper app:
- constructs a
KeychainWrapperto access a keychain identified by service name and access group,
- reads the username and password passed as command line arguments, and
- stores the credentials within the shared keychain.
With the helper app now functional, we updated our script to build, install, and launch it prior to running the dev app. Below is our abbreviated build script.
Accessing the shared credentials
With our build process augmented, we simply needed to pull the credentials from within the dev app and render a new login button. Our dev app has a special collection of tools which make it easy to create test accounts in other environments and enable feature flags, among other capabilities. This section was a natural place to insert the one-click login option.
Compared to manually entering an email and password, this new feature was a vast improvement. While measuring the actual impact is difficult, we are confident that the incremental gains will add up across the entire iOS team over many development cycles.
Overall, we learned how to improve the developer experience without compromising security. Going forward, we may extend this functionality to on-device builds. If this type of project sounds interesting to you, or if you have ideas on how we might improve it, check out our careers page.
The information contained in this communication is provided for general informational purposes only, and should not be construed as investment or tax advice. Nothing in this communication should be construed as a solicitation or offer, or recommendation, to buy or sell any security. Any links provided to other server sites are offered as a matter of convenience and are not intended to imply that Wealthfront Advisers or its affiliates endorses, sponsors, promotes and/or is affiliated with the owners of or participants in those sites, or endorses any information contained on those sites, unless expressly stated otherwise.
All investing involves risk, including the possible loss of money you invest, and past performance does not guarantee future performance. Please see our Full Disclosure for important details.
Wealthfront offers a free software-based financial advice engine that delivers automated financial planning tools to help users achieve better outcomes. Investment management and advisory services are provided by Wealthfront Advisers LLC, an SEC registered investment adviser, and brokerage related products are provided by Wealthfront Brokerage LLC, a member of FINRA/SIPC.
Wealthfront, Wealthfront Advisers and Wealthfront Brokerage are wholly owned subsidiaries of Wealthfront Corporation.
© 2021 Wealthfront Corporation. All rights reserved.