Name: SC_MarkStruzinski
Owner: raywenderlich
Description: null
Created: 2017-12-13 20:32:41.0
Updated: 2018-05-05 02:20:48.0
Pushed: 2018-05-05 02:20:47.0
Homepage: null
Size: 4794
Language: Swift
GitHub Committers
User | Most Recent Commit | # Commits |
---|
Other Committers
User | Most Recent Commit | # Commits |
---|
Today, we're going to explore Shared Web Credentials. Shared Web Credentials is a technology Apple has introduced which allows seamless login into your iOS app after a user has logged into your website using Safari.
Shared Web Credentials works using iCloud Keychain, and establishes trust between your site and your app via the site association file. This is a file Apple requires you to host on your domain. If your user has opted into iCloud Keychain and has elected to store their credentials for your domain there, you will be able to present UI that allows them to use those same credentials to log into your app without the need to type a username or password.
There are 2 components that come together to ensure Shared Web Credentials can offer login functionality:
From your domain, you must be able to serve up a file named apple-app-site-association
from either the root of the domain or from a directory named .well-known
.
The following criteria must be met for the Shared Web Credentials functionality to work:
application/json
webcredentials
objectwebcredentials
object of this file, there has to be an array of bundle identifiers with the full team prefix. One of these identifiers has to match the one for your app.To easily meet those requirements for this tutorial, we're going to use the free Heroku web service to establish a domain.
Your app's bundle id has to match the one being referenced by the apple-app-site-assocation
file on your web server. Your app must also enable the Associated Domains capability, and you have to add your site domain to the domain list with a webcredentials
prefix.
As you can see, there are some setup steps that need to be satisfied before we can start coding our iOS app, so let's get started!
We'll have to set up 2 components to make Shared Credentials work - the iOS app and a web app. We'll start by configuring the web app, move over and configure the iOS app, then wrap up with some code in the iOS app to make everything work together.
Our first step is to set up our web server. Since Shared Web Credentials requires a functioning domain set up over SSL, we will use a free Heroku account. Heroku offers up to 5 free domains, and will satisfy this Apple requirement.
heroku login
git clone git@github.com:raywenderlich/shared-credentials.git
. This web app is written in Ruby on Rails, but you won't need to know any implementation details to get this demo up and running!apple-app-site-association.rb
file located at shared-credentials/config/initializers/apple-app-site-association.rb
in a plain text editor such as Atom or Visual Studio Code.apps
array, update the bundle identifier to match the one you just created on the Apple Dev Portal. Replace MX49LZU2AV.com.example.test.sharedcredentials
with your team prefix and bundle id and save the file.
git add .
heroku create
to create a new Heroku app. This will create a new remote on your git repo named heroku
. This will take a bit, so I'm going to skip ahead to the end. Make note of the new domain Heroku has assigned for you.git push heroku master
from the terminal. This should make your web app live. Go to the Heroku domain in the previous step and verify your web app is up and running. You should see a blank page with a Log In button on the top right of the nav bar.That's it for this section. On to the iOS app!
The example app was created from the single view Xcode template. It is a single ViewController wrapped inside a NavigationController. All button actions and element outlets are wired up already, and we'll just be implementing the logic to enable the Shared Web Credentials functionality.
First we'll need to configure the app to use the Associated Domains capability:
webcredentials:
. Example: webcredentials:com.test.shared-credentials
This will set up the trust relationship with your new domain. Next we need to set up the web app.
The first thing we'll need to do is open our newly created domain and attempt a login. This will get Safari to prompt us to store the credentials used for the site, and we can use them later to log the user into the app.
First, we'll need to do some brief setup inside the iOS Simulator.
Now, we can add the code that will set up Shared Credentials
LoginViewController.swift
@IBOutlet
declarations, update the websiteURLString
variable to match the homepage of your new domain that you got during the web app setup.Accessing Shared Credentials From Your App
We now have stored credentials for your new domain in iCloud Keychain. The final step will be to set the app up to read them, and we'll be able to use them to log your user in without having to enter them manually
LoginViewController
ivate func attemptLoginFromSharedCredentials() {
Next we'll show some mocked up UI to simulate a network event. Add this to your new method:
teUIForNetworkCall(inProgress: true)
Now we'll call the system API SecRequestSharedWebCredentials
to request permission to the stored iCloud credentials for our site. We could request credentials for only our domain, and even a specific user account, but we won't get that specific here. We'll pass nil
for those 2 parameters. Add this code next:
equestSharedWebCredential(nil, nil) { [weak self] (results, error) in
The first thing we'll do after the request completes is to guard against any errors. If the error parameter on the completion block is not nil, then something has failed. We'll log it and exit. We'll also grab a strong reference to the viewcontroller from the capture list above to prevent any retain cycles. Add this code first right inside the completion block:
d error == nil,
strongSelf = self else {
t("Error encountered: \(error!)")
rn
Next we'll perform a count on the CFArray
parameter returned in the completion block. If the array is not nil, and the count is larger than 0, we got a match back that we can work with. Add this code right below the error guard
:
d let credentials = results,
rayGetCount(credentials) > 0 else {
rn
Next we need to get a pointer to the response object returned in the array. Since we know it's the first object in the array, we grab the one at index 0 and cast it to a CFDictionary
. Add this code next:
unsafeCredentials = CFArrayGetValueAtIndex(credentials, 0)
credentialsDict = unsafeBitCast(unsafeCredentials, to: CFDictionary.self)
CFDictionary
, we use a convenience method to retrieve pointers to the username and password values, and make sure we have valid objects back. Add this under the 2 variables you just created:unsafeLoginValue = strongSelf.unsafeValue(from: credentialsDict, for: kSecAttrAccount)
unsafePasswordValue = strongSelf.unsafeValue(from: credentialsDict, for: kSecSharedPassword)
d let unsafeLogin = unsafeLoginValue,
t unsafePassword = unsafePasswordValue else {
turn
unsafeBitcastToString:
next:username = strongSelf.unsafeBitcastToString(from: unsafeLogin)
password = strongSelf.unsafeBitcastToString(from: unsafePassword)
ngSelf.fillCredentialsAndLogin(withUserName: username,
password: password)
requestSharedCredentialsTapped(_:)
, make a call to attemptLoginFromSharedCredentials()
. Normally you would initiate this immediately when the Login screen comes into view, but since this is a single view app, we needed a way to trigger it manually.UIAlert
that allows you to tap the email address you used on the web login from earlier.Ok, that was a lot, but I think it was worth it! The setup is a bit complicated, but once your domain is set up properly, the code to implement this in an iOS app is pretty minimal, and worth the effort for your users. Having a site association file in place also enables some other cool features, such as Universal Links and NSUserActivity continuation.
At this point, you should understand how to set up your app and your domain to work with Shared Credentials, and how to use Shared Credentials inside your app to log a user in seamlessly.
Thanks for watching, and I hope this helps you get your users logged in a little bit easier!