Android Authentication
AuthRepository
The AuthRepository
class manages the authentication state across the application, whether using Native or React Native for login purposes. It is essential that all native features utilize the AuthRepository to maintain authentication consistency.
Available usecases for interacting with the AuthRepository include:
SetTokenUseCase
LogOutUseCase
RefreshAuthenticationTokenUseCase
GetAuthClientIdUseCase
GetAuthenticationTokenUseCase
GetClientIdUseCase
ObserveLogoutUseCase
ObserveTokenChangesUseCase
Setting a new auth state
Logged In
When the app acquires a new auth token, it must use setToken(_:)/SetTokenUseCase
to update both the access token and refresh token. Additionally, we securely store critical details such as id, type, scope, and expiry on the device using EncryptedSecureSharedPreferences
. These additional fields are crucial for effective token synchronization with React Native through the AuthenticationTokenModule
.
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
- AuthenticationTokenModule - whenever the tokens are updated on the React Native side, see AuthenticationTokenModule 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()/LogOutUseCase
. This will remove the securely stored access and refresh token from the device.
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
- AuthenticationTokenModule - whenever the user logs out on the React Native side, see
AuthenticationTokenModule
documentation
Listening for updates
isLoggedIn
The AuthRepository
exposes a isLoggedIn()
method that returns Flow<Boolean>
. The Flow will emit a boolean change whenever the auth state is updated. For convenience the LoggedInUseCase
can be used to listen to the logged in state change.
// LoggedInUseCase utilizing isLoggedIn() from AuthRepository
loggedInUseCase().distinctUntilChanged().onEach { isLoggedIn ->
// Use isLoggedIn boolean as required
}.launchIn(viewModelScope)
OnTokenChange
The AuthRepository
exposes a onTokenChange()
method that returns Flow<TokenEntity?>
. The Flow will emit a nullable TokenEntity change whenever the auth state is updated by setToken()
/ refreshAccessToken()
/ onLogout
. For convenience the ObserveTokenChangesUseCase
can be used to listen for the token change.
// ObserveTokenChangesUseCase utilizing onTokenChange() from AuthRepository
observeTokenChangesUseCase.invoke().collectLatest { token ->
// Use token as required
}.launchIn(viewModelScope)
OnLogout
The AuthRepository
exposes a onLogout()
method that returns Flow<LogoutEvent>
. The Flow will emit a LogoutEvent change whenever the clearToken()/LogOutUseCase
is called, LogoutEvent
can either be a NativeLogout
or ReactNativeLogout
when invoked from the RN bridge AuthenticationTokenModule
. For convenience the ObserveLogoutUseCase
can be used to listen to the logout change.
// ObserveLogoutUseCase utilizing onLogout() from AuthRepository
observeLogoutUseCase.invoke().collect { logoutEvent ->
if (logoutEvent is LogoutEvent.NativeLogout) {
// Native logout event
}else if (logoutEvent is LogoutEvent.ReactNativeLogout){
// React Native logout event
}
}
Accessing the current Token
To access the authentication token, such as when needed for an API call, utilize the getToken()
method from AuthRepository
, which returns a nullable TokenEntity
. For convenience, its recommend using the GetAuthenticationTokenUseCase
. If the user is not logged in, this method will return a null value. This token is automatically integrated into the headers of both the RestAuthorizationInterceptor
and GraphQLAuthorizationInterceptor
, ensuring it is included in all GraphQL requests and any REST requests using the default OkHttpClient
. For requests that do not require authentication, use the UnAuthenticatedOkHttpClient
. Generally, direct access to this token is not typically necessary.
Refreshing the existing token
To refresh the access token, use the refreshAccessToken()
method from AuthRepository
, which may return a nullable TokenEntity
. It's recommend utilizing the RefreshAuthenticationTokenUseCase
for managing the refresh token logic. This use case handles scenarios where the token is blank or invalid and will log out the user's session if needed. Upon a successful refresh, it saves the new token using setToken(_:)
as part of refreshAccessToken()
The token refresh process is seamlessly integrated into the GraphQLAuthorizationInterceptor
and RestAuthorizationInterceptor
. These interceptors automatically refresh the token upon expiration and trigger updates for onTokenChange
, isLoggedIn
, and onLogout
as necessary.The refresh action involves making an API call to /api/providers/auth/oauth2/token
on the Login API, utilizing the refresh token and client ID from the App Config (refer to the Login Documentation for details).
- GraphQLAuthorizationInterceptor - If the current accessToken is expired and a GraphQL request is made, the token will be refreshed before making the request.
- RestAuthorizationInterceptor - If the current accessToken is expired and a REST request is made, the token will be refreshed before making any request. By default
OkHttpClient
hasRestAuthorizationInterceptor
set as the default interceptor.
Get Client Id
Often times you might require the users client id, for convenience use: GetClientIdUseCase
that utilizes the AuthRepository
getClientId()
which extracts the client ID from the existing JWT token. It returns the client ID as a string, or a null value if it cannot be retrieved.