Getting Started with Auth Connect
Generate the Application
Before we explore the use of Auth Connect, we need to scaffold an application. In this section, we will generate an @ionic/react
tabs based application, perform some basic configuration, and add the iOS
and Android
platforms.
Use the Ionic CLI to generate the application.
Change directory into the newly generated project.
Change the appId
to be something unique. The appId
is used as the bundle ID / application ID. Therefore it should be a string that is unique to your organization and application. We will use io.ionic.gettingstartedac
for this application.
It is best to do this before adding the iOS
and Android
platforms to ensure they are setup properly from the start.
Build the application and install the platforms.
We should do a cap sync
with each build. This ensures our native projects remain up to date. Change the scripts in package.json
to do this.
Modify the vite.config.ts
file to ensure that the development server (npm run dev
) uses port 8100
.
Use the Ionic CLI to generate the application.
Change directory into the newly generated project.
Change the appId
to be something unique. The appId
is used as the bundle ID / application ID. Therefore it should be a string that is unique to your organization and application. We will use io.ionic.gettingstartedac
for this application.
It is best to do this before adding the iOS
and Android
platforms to ensure they are setup properly from the start.
Build the application and install the platforms.
We should do a cap sync
with each build. This ensures our native projects remain up to date. Change the scripts in package.json
to do this.
Modify the vite.config.ts
file to ensure that the development server (npm run dev
) uses port 8100
.
Install Auth Connect
In order to install Auth Connect, you will need to use ionic enterprise register
to register your product key. This will create a .npmrc
file containing the product key.
If you have already performed that step for your production application, you can just copy the .npmrc
file from your production project. Since this application is for learning purposes only, you don't need to obtain another key.
You can now install Auth Connect and sync the platforms:
Create the "Session Store"
When using Auth Connect, the current session is defined by the current AuthResult which contains auth tokens as well as other session information. Auth Connect returns an AuthResult
from the login()
function. We will need a place to store that result. Let's create that now. Create a src/util
directory, then create a file called src/util/session-store.ts
.
Note: React allows for many different state management strategies. Your strategy may differ significantly. State management strategies are outside the scope of this tutorial. Use code that aligns with your chosen strategy.
We know we will need to store the session, and that the session is defined by the AuthResult
.
To sync the store with React, we will eventually use useSyncExternalHook. In preparation, we need some boilerplate code starting with a subscribe()
method.
Add a getSnapshot()
function that just returns the current session.
When the session
changes, we need to inform any subscribed listeners.
We need a function that we can use to set the session
. For now, this is the only function that changes the session
and emits the change.
Export the functions that will need to be used elsewhere in our application.
We know we will need to store the session, and that the session is defined by the AuthResult
.
To sync the store with React, we will eventually use useSyncExternalHook. In preparation, we need some boilerplate code starting with a subscribe()
method.
Add a getSnapshot()
function that just returns the current session.
When the session
changes, we need to inform any subscribed listeners.
We need a function that we can use to set the session
. For now, this is the only function that changes the session
and emits the change.
Export the functions that will need to be used elsewhere in our application.
Create the Authentication
Utility Functions
All interaction with Auth Connect will be abstracted into a set of functions in an authentication
utility module. Create an empty src/utils/authentication.ts
file.
Setup and Initialization
Before we use Auth Connect, we need to make sure that it is properly set up and initialized. We will build a utility function to perform the setup and initialization required by Auth Connect.
Auth Connect needs a slightly different configuration between mobile and web, so we need to know in which context we are currently running.
For this tutorial, we are using Auth0 as the authentication vendor. We need to create an Auth0Provider
to help Auth Connect with the communication with Auth0.
Auth Connect needs to know how to communicate with our authentication vendor. You will likely need to get this information from the team that manages your cloud infrastructure.
We need to perform a one-time setup with Auth Connect. Please refer to the documentation if you have any questions about the individual properties. We will start here with a simple set up that is good for development.
The utility module needs to return the setupAuthConnect
function. It will be called elsewhere.
The utility module needs to return the setupAuthConnect
function. It will be called elsewhere.
Auth Connect needs a slightly different configuration between mobile and web, so we need to know in which context we are currently running.
For this tutorial, we are using Auth0 as the authentication vendor. We need to create an Auth0Provider
to help Auth Connect with the communication with Auth0.
Auth Connect needs to know how to communicate with our authentication vendor. You will likely need to get this information from the team that manages your cloud infrastructure.
We need to perform a one-time setup with Auth Connect. Please refer to the documentation if you have any questions about the individual properties. We will start here with a simple set up that is good for development.
The utility module needs to return the setupAuthConnect
function. It will be called elsewhere.
The utility module needs to return the setupAuthConnect
function. It will be called elsewhere.
Login and Logout
We need to create login()
and logout()
functions.
Import the functions from our session store that are used to get and set the session.
For the login()
, we need to pass both the provider
and the authOptions
we established earlier. It returns an AuthResult
that we need to store. Be sure to export the function so we can use it in the rest of our app.
For the logout()
, when calling Auth Connect we need to pass the provider
as well as the AuthResult
we established with the login()
. If the logout succeeds, the session needs to be cleared.
Import the functions from our session store that are used to get and set the session.
For the login()
, we need to pass both the provider
and the authOptions
we established earlier. It returns an AuthResult
that we need to store. Be sure to export the function so we can use it in the rest of our app.
For the logout()
, when calling Auth Connect we need to pass the provider
as well as the AuthResult
we established with the login()
. If the logout succeeds, the session needs to be cleared.
Handling the Authentication Flow
Now that the utility functions are complete, we will use them to implement the authentication flow of our application. To do this, we will need to:
- Create the
AuthActionCompletePage
and route that is used in our configuration. - Expose the authentication state to our application via the creation of an
AuthenticationProvider
. - Add buttons to the
Tab1
page that will be used to perform thelogin()
andlogout()
operations.
Create the AuthActionCompletePage
The logoutUrl
and redirectUri
properties are using the /auth-action-complete
route. Create a page for the route. The page does not have to do much. We will just display a spinner in case someone sees it momentarily.
We will perform a small bit of styling to make the page look nice.
Be sure to add the route in the router setup. In a production app, this likely would not be put within the IonTabs
structure. Refactoring the routing, however, to avoid this is beyond the scope of this tutorial.
Create the AuthenticationProvider
The Auth Connect related functionality will be exposed to our application via a context provider, which we will now build. Create a src/providers
directory containing a file named src/providers/AuthenticationProvider.tsx
Start with creating the context we want to provide to the application.
Create a shell component for the AuthenticationProvider
. It needs to render the context we created.
Initialize AuthConnect
. The child components should only be able to interact with AuthConnect
after it has completed the initialization process, so display a spinner until this is complete.
Get the session
value from our session store and update isAuthenticated
when its value changes.
Start with creating the context we want to provide to the application.
Create a shell component for the AuthenticationProvider
. It needs to render the context we created.
Initialize AuthConnect
. The child components should only be able to interact with AuthConnect
after it has completed the initialization process, so display a spinner until this is complete.
Get the session
value from our session store and update isAuthenticated
when its value changes.
Now that the AuthenticationProvider
has been created, we can add it to our application, which will allow us to perform the login and logout actions. Modify src/App.tsx
to surround the bulk of our application with the provider.
Hook Up the Login and Logout
We can use the first tab of our application to test the login()
and logout()
functions.
Currently, the Tab1
page contains the default skeleton code.
Use the AuthenticationContext
to get the isAuthenticated
value.
Replace the ExploreContainer
component with a Login or Logout button, depending on the current authentication status.
Perform a login()
or logout()
on click accordingly.
Currently, the Tab1
page contains the default skeleton code.
Use the AuthenticationContext
to get the isAuthenticated
value.
Replace the ExploreContainer
component with a Login or Logout button, depending on the current authentication status.
Perform a login()
or logout()
on click accordingly.
Test this in the web using the following credentials:
- email:
test@ionic.io
- password:
Ion54321
At this point if we press the Login button, a tab should open where we can log in using Auth0. This tab will close after we log in. When we press the logout button a tab will briefly open to perform the logout and then automatically close. The button that is displayed changes based on our current authentication status.
Configure the Native Projects
Login and logout are working in your web browser. Build your application for mobile and try to run them there. You can use an emulator or an actual device for this test.
On Android, you get an error like this one:
_10Manifest merger failed : Attribute data@scheme at AndroidManifest.xml requires a placeholder substitution but no value for <AUTH_URL_SCHEME> is provided.
On iOS, the application runs, but you get an invalid URL error after successfully logging in on Auth0.
The problem is that on mobile we are deep-linking back into our application using io.ionic.acdemo://auth-action-complete
. We have not registered that scheme with the OS so it does not know to deep-link back to our application. We will set that up now.
For Android, modify the android
section of the android/app/build.gradle
file to include the AUTH_URL_SCHEME
:
For iOS, add a CFBundleURLTypes
section to the ios/App/App/Info.plist
file:
Re-run the application from Xcode and Android Studio. You should now be able to perform the authentication properly on the mobile applications.
Persist the AuthResult
The user can perform login and logout operations, but if the browser is refreshed, the application loses the AuthResult
. This value needs to be persisted between sessions of the application. To fix this, we will modify our session store to use the Preferences plugin to persist the AuthResult
. In a production application, we should store the result more securely by using Identity Vault. However, setting up Identity Vault is beyond the scope of this tutorial.
Use the @capacitor/preferences
in our session store to persist the AuthResult
we received when logging in.
Currently, the session information is only stored in the session
variable. We will modify our code to store the session information using the Preferences
plugin.
Import the Preferences
plugin.
Save the session to Preferences
when the session is set.
Create an initialize()
function that gets the value from the Preferences
plugin.
Ensure the authentication information is initialized before the application is mounted.
Currently, the session information is only stored in the session
variable. We will modify our code to store the session information using the Preferences
plugin.
Import the Preferences
plugin.
Save the session to Preferences
when the session is set.
Create an initialize()
function that gets the value from the Preferences
plugin.
Ensure the authentication information is initialized before the application is mounted.
If the user logs in and either refreshes the browser or restarts the application the authentication state is preserved.
Next Steps
Explore the specific topics that are of interest to you at this time. This application is used as the foundation to build upon as those topics are explored.
Happy coding!! 🤓