-
Notifications
You must be signed in to change notification settings - Fork 0
Credentials authentication
namnh-0652 edited this page Mar 9, 2023
·
6 revisions
- SignIn with provided credentials
- Refresh token manually or automatically
- Local check you are logged in or not
- Gets the current authentication token
- Logout current user from local
- Android 23+
From project build.gradle
(or settings.gradle
), add Jitpack maven
repositories {
maven { url 'https://jitpack.io' }
}
Add these dependencies to your app/build.gradle
dependencies {
implementation "com.github.sun-asterisk.tech-standard-android-auth:core:${latest_version}"
implementation "com.github.sun-asterisk.tech-standard-android-auth:credentialsauth:${latest_version}"
}
There are 3 steps for signIn with username & password using CredentialsAuth
module.
Create your token model extends from AuthToken
data class Token(
@Expose @SerializedName("id") val id: String? = null,
@Expose @SerializedName("user_id") val userId: String? = null,
@Expose @SerializedName("token") val accessToken: String? = null,
@Expose @SerializedName("refresh_token") val refreshToken: String? = null,
@Expose @SerializedName("expired_at") val expiresIn: String? = null
) : AuthToken {
override val crAccessToken: String
get() = accessToken.orEmpty() // required
override var crRefreshToken: String? = null
get() = refreshToken ?: field // required
}
Create configuration for CredentialsAuthConfig
From Application
class, call initCredentialsAuth
method
initCredentialsAuth(
signInUrl = "http://10.0.5.78:8001/api/login", // replace YOUR_FULL_LOGIN_URL
authTokenClazz = Token::class.java,
) {
authTokenChanged = object : AuthTokenChanged<Token> {
override fun onTokenUpdate(token: Token?): Request {
return buildRefreshTokenRequest(token)
}
}
}
You can add more configurations, see CredentialsAuthConfig
fun signIn(username: String, password: String) {
viewModelScope.launch(Dispatchers.IO) {
CredentialsAuth.signIn(
requestBody = SignInRequest(username, password), // change to your signin request body
callback = object : AuthCallback<Token> {
override fun onResult(data: Token?, error: Throwable?) {
_credentialsAuthResult.postValue(AuthResult(success = data, error = error))
}
}
)
}
}
sequenceDiagram
autonumber
participant Application
participant CredentialsAuth
participant AuthRepository
note over CredentialsAuth, AuthRepository: CredentialsAuth Module
participant Server
Application->>CredentialsAuth: send SignIn() request
CredentialsAuth->>AuthRepository: forward SignIn() request
AuthRepository->>Server: call SignIn
Server->>Server: Handle Request
alt signIn process fails
rect rgb(0, 0, 255, .1)
break
Server-->>AuthRepository: return error
AuthRepository-->>CredentialsAuth: forward error
CredentialsAuth-->>Application: Callback error
end
end
else signIn Success
Server-->>AuthRepository: return Token
AuthRepository->>AuthRepository: save Token
AuthRepository-->>CredentialsAuth: forward Token
CredentialsAuth-->>Application: Callback Token
end
Refresh token manually with 2 steps:
import okhttp3.Request
// PATCH request body sample
val requestBody = MultipartBody.Builder().addPart(
MultipartBody.Part.createFormData(
"refresh_token",
getToken()?.crRefreshToken.orEmpty() // to retrieve the last saved refresh token
)
).build()
// PUT, POST request body sample
val json = JsonObject().apply { // using gson
addProperty("refresh_token", getToken()?.crRefreshToken.orEmpty())
}
val requestBody = json.toString().toRequestBody(CredentialsAuth.JSON_MEDIA_TYPE)
val refreshTokenRequest = Request.Builder()
.url("https://your.url/api/v1/auth_tokens") // replace YOUR_FULL_REFRESH_TOKEN_URL
.put(requestBody)
//.post(requestBody)
//.patch(requestBody)
.build()
fun refreshToken() {
viewModelScope.launch(Dispatchers.IO) {
CredentialsAuth.refreshToken(
request = refreshTokenRequest,
callback = object : AuthCallback<Token> {
override fun onResult(data: Token?, error: Throwable?) {
_credentialsAuthResult.postValue(AuthResult(success = data, error = error))
}
}
)
}
}
There are 2 steps to achieve this
Build refresh token request when token is updated via CredentialsAuthConfig
, see create configurations
initCredentialsAuth(...) {
...
authTokenChanged = object : AuthTokenChanged<Token> {
override fun onTokenUpdate(token: Token?): Request {
return buildRefreshTokenRequest(token)
}
}
}
fun buildRefreshTokenRequest(token: Token?): Request? {
// See "create refresh token Request using OkHttp" above
}
client = OkHttpClient().newBuilder()
.authenticator(TokenAuthenticator<Token>(REFRESH_TOKEN_EXPIRED_ERROR_CODE))
.addInterceptor(...)
...
.build()
When the refreshToken
is expired, we can not use it anymore, user may need to re-signin again. To listen the event when refreshToken
is expired, register BroadcastReceiver
in your Activity or Fragment
.
private val appIntentFilter = IntentFilter().apply {
addAction(ACTION_REFRESH_TOKEN_EXPIRED) // need to listen this action
// other actions
}
private val appBroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
when (intent?.action ?: return) {
ACTION_REFRESH_TOKEN_EXPIRED -> {
// your logic here, For example: move to Login screen
}
// other actions
}
}
}
override fun onResume() {
super.onResume()
LocalBroadcastManager.getInstance(this).registerReceiver(appBroadcastReceiver, appIntentFilter)
}
override fun onPause() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(appBroadcastReceiver)
super.onPause()
}
sequenceDiagram
autonumber
participant Application
participant TokenAuthenticator
note over TokenAuthenticator: CredentialsAuth Module
participant Server
Application->>Application: Setup OkHttp Client
note over Application: add TokenAuthenticator <br> into OkHttp Client
Application->>Server: send API Request(1)
Server->>Server: Handle Request
critical Token is expired error
Server-->>TokenAuthenticator:
loop Util reaches refresh limit time <br> or refreshToken Success
TokenAuthenticator->>TokenAuthenticator: get saved RefreshToken
TokenAuthenticator->>Server: send refreshToken() request
Server->>Server: Handle Request
alt refreshToken Error
Server-->>TokenAuthenticator: return Error
else refreshToken Success
Server-->>TokenAuthenticator: return new Token
end
end
TokenAuthenticator->>TokenAuthenticator: Save new Token
TokenAuthenticator->>TokenAuthenticator: Update Token to API Request(1)
TokenAuthenticator->>Server: Resend API Request(1)
Server->>Server: Handle Request
Server-->>Application: return response API Request(1)
end
Just simply call this method
fun isSignedIn(): Boolean {
return CredentialsAuth.isSignedIn<Token>()
}
Just simply call this method
fun getToken(): Token? {
return CredentialsAuth.getToken()
}
Just simply call this method
CredentialsAuth.logout {
// Do your job when logout. Ex: clear other local data.
}
classDiagram
class CredentialsAuthConfig {
}
class CredentialsAuth {
-AuthRepository repository
-CredentialsAuthConfig config
}
class AuthRepository {
<<interface>>
}
class AuthRepositoryImpl {
-AuthRemoteDataSource remote
-AuthLocalDataSource local
}
class AuthRemoteDataSource {
-NonAuthApi api
}
class NonAuthApi {
<<interface>>
}
class AuthLocalDataSource {
-SharedPrefApi api
}
class SharedPrefApi {
<<interface>>
}
CredentialsAuthConfig *-- CredentialsAuth
AuthRepository *-- CredentialsAuth
AuthRepositoryImpl --|> AuthRepository
AuthRemoteDataSource *-- AuthRepositoryImpl
AuthLocalDataSource *-- AuthRepositoryImpl
NonAuthApi *-- AuthRemoteDataSource
SharedPrefApi *-- AuthLocalDataSource
SharedPrefApiImpl --|> SharedPrefApi
Method | Purpose |
---|---|
suspend fun signIn(requestBody: Any?, callback: AuthCallback): Unit | SignIn with given credentials |
fun isSignedIn(): Boolean | Local check user is signed in |
fun signOut(doOnSignedOut: () -> Unit): Unit | Local remove token |
fun getToken(): T? | Gets saved token |
suspend fun refreshToken(request: Request, callback: AuthCallback?): Unit | Manually refresh token |
Method/field | Purpose |
---|---|
signInUrl: String | Full signIn URL with scheme & path |
authTokenClazz: Class<*> | Class which implements from AuthToken |
authTokenChanged: AuthTokenChanged<*> | Callback when Token is changed |
basicAuthentication: String | Sets authentication API basic authen |
httpLogLevel: HttpLoggingInterceptor.Level | Sets authentication API log level |
connectTimeout: Long | Sets authentication API connect timeout |
readTimeout: Long | Sets authentication API read timeout |
writeTimeout: Long | Sets authentication API write timeout |
customHeaders: Headers? | Sets authentication API Headers |