Skip to main content

iOS Authentication

AuthenticationService

The AuthenticationService class is responsible for maintaining authentication state in the app. It does this regardless of whether the app is using Native or React Native for login. All native features should rely solely on AuthenticationService for managing authentication state.

Setting a new auth state

Logged In

When a new auth token is acquired, the app needs to call setToken(_:) with the new access token and refresh token. Both tokens must be updated simultaneously. Doing so will store both the access token and refresh token in the device keychain, to be used across future sessions.

This currently happens in the following places in the app:

  • Login - when a user logs in, see Login Feature documentation
  • Token Refresh - when the app requests a refreshed token, see below
  • AuthenticationTokenBridge - whenever the tokens are updated on the React Native side, see AuthenticationTokenBridge documentation

This will soon be updated to also be called from:

  • Sign Up

Logged Out

Whenever the user is logged out, the app needs to call clearToken(). This will remove the stored access and refresh token from the device keychain.

This currently happens in the following places in the app:

  • More Menu - when the user presses log out
  • User Tray - when the user presses log out
  • AuthenticationTokenBridge - whenever the user logs out on the React Native side, see AuthenticationTokenBridge documentation

Listening to updates for a new auth state

The AuthenticationService exposes a tokenUpdatePublisher() method that returns AnyPublisher<Void, Never>. The publisher this returns will publish a new value every time the auth state is updated, whether logged in or out. This can then be listened to, to update any other features in the app that rely on Auth State.

The following code demonstrates how to listen to this publisher, and determine whether the user is currently logged in, with the isLoggedIn property. This property can also be read at any time to determine if the user is currently logged in.

authenticationService.tokenUpdatePublisher()
.sink {
if authenticationService.isLoggedIn {
// user is logged in
} else {
// user is logged out
}
}
.store(in: &cancellables)

Accessing the current Token

Once you've determined the user is currently logged in, if you need access to the authentication token, such as to make an API call, you can access this by calling authententicationService.tokens.accessToken. This will return the stored access token for the current session.

This token is automatically set on the NetworkRESTService and NetworkGraphQLService, and sent for all GraphQL requests, and any REST requests that include isAuthenticated: true, so direct access to this token should be relatively unnecessary.

Refreshing the existing token

The AuthenticationService can also update its access token using the stored Hydra refresh token. This can be kicked off by calling the refreshToken() method. This will make an API call to /api/providers/auth/oauth2/token on the Login API with the refresh token and client ID acquired from App Config (see Login Documentation for detail). This will store the new access and refresh tokens in keychain, then notify any listeners to the tokenUpdatePublisher.

This is currently done by the app in two places:

  • NetworkGraphQLService - If the current accessToken is expired and a GraphQL request is made, the token will be refreshed before making the request
  • NetworkRESTService - If the current accessToken is expired and a REST request is made, where the isAuthenticated value returns true, the token will be refreshed before making the request