The next feature you will be adding is user authentication. In this module, you will learn how to authenticate a user with the Amplify CLI and libraries, leveraging Amazon Cognito, a managed user identity provider.
You will also learn how to use the Cognito Hosted User Interface to present an entire user authentication flow, allowing users to sign up, sign in, and reset their password with just few lines of code.
using a "Hosted User Interface" means the application leverages the Cognito web pages for the signin and signup user interface flows. The user of the app is redirected to a web page hosted by Cognito and redirected back to the app after signin. Of course, Cognito and Amplify does support native UI as well, you can follow these workshop instructions to learn more about custom authentication UI.
- Create and deploy an authentication service
- Configure your iOS app to include Cognito Hosted UI authentication
-
Amplify libraries – The Amplify libraries allow you to interact with AWS services from a web or mobile application.
-
Authentication – In software, authentication is the process of verifying and managing the identity of a user using an authentication service or API.
To create the authentication service, open a Terminal and execute this command in your project directory:
amplify add auth
- ? Do you want to use the default authentication and security configuration? Select Default configuration with Social Provider and press enter
- How do you want users to be able to sign in? Select the default Username and press enter
- Do you want to configure advanced settings? Select the default No, I am done and press enter
- What domain name prefix do you want to use? Select the default and press enter
- Enter your redirect signin URI: type
gettingstarted://
and press enter - ? Do you want to add another redirect signin URI? Select the default N and press enter
- Enter your redirect signout URI: type
gettingstarted://
and press enter - ? Do you want to add another redirect signout URI? Select the default N and press enter
- Select the social providers you want to configure for your user pool: do not select any provider and press enter
You know the configuration is successful when you see the message (the exact name of the resource will vary) :
Successfully added resource iosgettingstartedfc5a4717 locally
DO NOT FORGET to enter the two redirect URIs above (gettingstarted://
), this is mandatory to have the redirection to Cognito Hosted UI to work
Now that the authentication service has been configured locally, you can deploy it to the cloud. In a Terminal, execute this command in your project directory:
amplify push
# press Enter (Y) when asked to continue
After a while, you should see the following message:
✔ All resources are updated in the cloud
Hosted UI Endpoint: https://iosgettingstarted-dev.auth.eu-central-1.amazoncognito.com/
Test Your Hosted UI Endpoint: https://iosgettingstarted-dev.auth.eu-central-1.amazoncognito.com/login?response_type=code&client_id=1234567890&redirect_uri=gettingstarted://
Before going to the code, you add the Amplify Authentication Library to the dependencies of your project. Navigate to the General tab of your Target application (Your Project > Targets > General) and click the plus (+) in the Frameworks, Libraries, and Embedded Content section:
Select AWSCognitoAuthPlugin and click Add:
You will now see AWSCognitoAuthPlugin as a dependency for your project:
Back to Xcode, open Backend.swift
file. In the Backend
class,
- add an
import
statement for theAmplifyPlugins
- add a line to the amplify initialization code we added in the previous section.
Complete code block should look like this:
// at the top of the file
import Amplify
import AWSCognitoAuthPlugin
private init () {
// initialize amplify
do {
try Amplify.add(plugin: AWSCognitoAuthPlugin())
try Amplify.configure()
print("Initialized Amplify")
} catch {
print("Could not initialize Amplify: \(error)")
}
}
To verify everything works as expected, build the project. Click Product menu and select Build or type ⌘B. There should be no error.
The remaining code change tracks the status of user (are they signed in or not?) and triggers the SignIn / SignUp user interface when user is not signed in.
-
Add signin and signout code
Anywhere in
Backend
class, add the following three methods:// MARK: - User Authentication // signin with Cognito web user interface public func signIn() { Amplify.Auth.signInWithWebUI(presentationAnchor: UIApplication.shared.windows.first!) { result in switch result { case .success(_): print("Sign in succeeded") case .failure(let error): print("Sign in failed \(error)") } } } // signout public func signOut() { Amplify.Auth.signOut() { (result) in switch result { case .success: print("Successfully signed out") case .failure(let error): print("Sign out failed with error \(error)") } } } // change our internal state, this triggers an UI update on the main thread func updateUserData(withSignInStatus status : Bool) { DispatchQueue.main.async() { let userData : UserData = .shared userData.isSignedIn = status } }
-
Add an authentication hub listener
To track the changes of authentication status, we add code to subscribe to Authentication events sent by Amplify. We initialize the Hub in the
Backend.init()
method.When an authentication event is received, we call the
updateUserData()
method. This method keeps theUserData
object in sync. TheUserData.isSignedIn
property is@Published
, it means the user interface is automatically refreshed when the value changes.We also add code to check previous authentication status at application startup time. When the application starts, it checks if a Cognito session already exists and updates the UI accordingly.
In
Backend.init()
, add the following code after Amplify's initialization:// in private init() function // listen to auth events. // see https://github.com/aws-amplify/amplify-ios/blob/master/Amplify/Categories/Auth/Models/AuthEventName.swift _ = Amplify.Hub.listen(to: .auth) { (payload) in switch payload.eventName { case HubPayload.EventName.Auth.signedIn: print("==HUB== User signed In, update UI") self.updateUserData(withSignInStatus: true) case HubPayload.EventName.Auth.signedOut: print("==HUB== User signed Out, update UI") self.updateUserData(withSignInStatus: false) case HubPayload.EventName.Auth.sessionExpired: print("==HUB== Session expired, show sign in UI") self.updateUserData(withSignInStatus: false) default: //print("==HUB== \(payload)") break } } // let's check if user is signedIn or not Amplify.Auth.fetchAuthSession { (result) in do { let session = try result.get() // let's update UserData and the UI self.updateUserData(withSignInStatus: session.isSignedIn) } catch { print("Fetch auth session failed with error - \(error)") } }
-
Update the User Interface code
The last change in the code is related to the User Interface, we add a
ZStack
to theContentView
. Depending onUserData.isSignedIn
's value, the UI shows either aSigninButton
or the mainList
view.Open
ContentView.swift
and replacebody
inContentView
struct:var body: some View { ZStack { if (userData.isSignedIn) { NavigationView { List { ForEach(userData.notes) { note in ListRow(note: note) } } .navigationBarTitle(Text("Notes")) .navigationBarItems(leading: SignOutButton()) } } else { SignInButton() } } }
In the same file, add a
SignInButton
and aSignOutButton
view:struct SignInButton: View { var body: some View { Button(action: { Backend.shared.signIn() }){ HStack { Image(systemName: "person.fill") .scaleEffect(1.5) .padding() Text("Sign In") .font(.largeTitle) } .padding() .foregroundColor(.white) .background(Color.green) .cornerRadius(30) } } } struct SignOutButton : View { var body: some View { Button(action: { Backend.shared.signOut() }) { Text("Sign Out") } } }
To verify everything works as expected, build the project. Click Product menu and select Build or type ⌘B. There should be no error.
-
Update
Info.plist
Finally, we must ensure our app is launched at the end of the web authentication sequence, provided by Cognito hosted user interface. We add the
gettingstarted
URI scheme to the app'sInfo.plist
file.In Xcode, navigate to the Info.plist file (Project > Targets > Info) and add URL types as a key:
Expand the URL types drop down and add URL Schemes to Item 0 ():
Lastly add the value
gettingstarted
to Item 0 under URL Schemes: -
Build and Test
To verify everything works as expected, build the project. Click Product menu and select Run or type ⌘R. There should be no error. The app starts on the Sign In button.
Here is the full signup flow.
Landing View | Consent to redirect | Cognito Hosted UI |
---|---|---|
Signup flow | Verification Code | Main View |
---|---|---|
Next : Add API & Database.