Skip to content

Commit 1c4eca0

Browse files
feat: Implement JWT refresh token for authorization (#2230)
1 parent f0d8f01 commit 1c4eca0

File tree

7 files changed

+85
-11
lines changed

7 files changed

+85
-11
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package org.fossasia.openevent.general.auth
2+
3+
import okhttp3.Authenticator
4+
import okhttp3.Request
5+
import okhttp3.Response
6+
import okhttp3.Route
7+
8+
class TokenAuthenticator : Authenticator {
9+
10+
/**
11+
* Authenticator for when the authToken need to be refresh and updated
12+
* everytime we get a 401 error code
13+
*/
14+
15+
override fun authenticate(route: Route?, response: Response): Request? {
16+
17+
val loginResponse = RefreshTokenService.api.refreshToken()
18+
val newToken = "JWT ${loginResponse.refreshToken}"
19+
20+
return response.request.newBuilder()
21+
.addHeader("Authorization", newToken)
22+
.build()
23+
}
24+
}

app/src/main/java/org/fossasia/openevent/general/auth/AuthApi.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ interface AuthApi {
1919
@POST("../auth/session")
2020
fun login(@Body login: Login): Single<LoginResponse>
2121

22+
@POST("/auth/token/refresh")
23+
fun refreshToken(): LoginResponse
24+
2225
@GET("users/{id}")
2326
fun getProfile(@Path("id") id: Long): Single<User>
2427

app/src/main/java/org/fossasia/openevent/general/auth/AuthHolder.kt

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,37 +3,62 @@ package org.fossasia.openevent.general.auth
33
import org.fossasia.openevent.general.data.Preference
44
import org.fossasia.openevent.general.utils.JWTUtils
55

6-
private const val TOKEN_KEY = "TOKEN"
6+
private const val ACCESS_TOKEN = "accessToken"
7+
private const val REFRESH_TOKEN = "refreshToken"
78

89
class AuthHolder(private val preference: Preference) {
910

10-
var token: String? = null
11+
var accessToken: String? = null
1112
get() {
12-
return preference.getString(TOKEN_KEY)
13+
return preference.getString(ACCESS_TOKEN)
1314
}
1415
set(value) {
1516
if (value != null && JWTUtils.isExpired(value))
1617
throw IllegalStateException("Cannot set expired token")
1718
field = value
18-
preference.putString(TOKEN_KEY, value)
19+
preference.putString(ACCESS_TOKEN, value)
1920
}
21+
var refreshToken: String? = null
22+
get() {
23+
return preference.getString(REFRESH_TOKEN)
24+
}
25+
set(value) {
26+
field = if (value != null && JWTUtils.isExpired(value)) {
27+
"expired"
28+
} else {
29+
value
30+
}
31+
preference.putString(REFRESH_TOKEN, value)
32+
}
33+
34+
fun getAccessAuthorization(): String? {
35+
if (!isLoggedIn())
36+
return null
37+
return "JWT $accessToken"
38+
}
39+
40+
fun getRefreshAuthorization(): String? {
41+
if (!isLoggedIn())
42+
return null
43+
return "JWT $refreshToken"
44+
}
2045

2146
fun getAuthorization(): String? {
2247
if (!isLoggedIn())
2348
return null
24-
return "JWT $token"
49+
return "JWT $accessToken"
2550
}
2651

2752
fun isLoggedIn(): Boolean {
28-
if (token == null || JWTUtils.isExpired(token)) {
29-
token = null
53+
if (accessToken == null || JWTUtils.isExpired(accessToken)) {
54+
accessToken = null
3055
return false
3156
}
3257

3358
return true
3459
}
3560

3661
fun getId(): Long {
37-
return if (!isLoggedIn()) -1 else JWTUtils.getIdentity(token)
62+
return if (!isLoggedIn()) -1 else JWTUtils.getIdentity(accessToken)
3863
}
3964
}

app/src/main/java/org/fossasia/openevent/general/auth/AuthService.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ class AuthService(
2929

3030
return authApi.login(Login(username, password))
3131
.map {
32-
authHolder.token = it.accessToken
32+
authHolder.accessToken = it.accessToken
33+
authHolder.refreshToken = it.refreshToken
34+
3335
it
3436
}
3537
}
@@ -66,7 +68,8 @@ class AuthService(
6668

6769
fun logout(): Completable {
6870
return Completable.fromAction {
69-
authHolder.token = null
71+
authHolder.accessToken = null
72+
authHolder.refreshToken = null
7073
userDao.deleteUser(authHolder.getId())
7174
orderDao.deleteAllOrders()
7275
attendeeDao.deleteAllAttendees()

app/src/main/java/org/fossasia/openevent/general/auth/LoginResponse.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,7 @@ import com.fasterxml.jackson.databind.PropertyNamingStrategy
44
import com.fasterxml.jackson.databind.annotation.JsonNaming
55

66
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy::class)
7-
data class LoginResponse(val accessToken: String)
7+
data class LoginResponse(
8+
val accessToken: String,
9+
val refreshToken: String
10+
)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package org.fossasia.openevent.general.auth
2+
3+
import org.fossasia.openevent.general.BuildConfig
4+
import retrofit2.Retrofit
5+
import retrofit2.converter.gson.GsonConverterFactory
6+
7+
object RefreshTokenService {
8+
9+
private val retrofit = Retrofit.Builder()
10+
.addConverterFactory(GsonConverterFactory.create())
11+
.baseUrl(BuildConfig.DEFAULT_BASE_URL)
12+
.build()
13+
val api: AuthApi = retrofit.create(AuthApi::class.java)
14+
}

app/src/main/java/org/fossasia/openevent/general/di/Modules.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ import org.fossasia.openevent.general.search.location.LocationService
7171
import org.fossasia.openevent.general.search.type.SearchTypeViewModel
7272
import org.fossasia.openevent.general.search.location.LocationServiceImpl
7373
import org.fossasia.openevent.general.auth.SmartAuthViewModel
74+
import org.fossasia.openevent.general.auth.TokenAuthenticator
7475
import org.fossasia.openevent.general.connectivity.MutableConnectionLiveData
7576
import org.fossasia.openevent.general.discount.DiscountApi
7677
import org.fossasia.openevent.general.discount.DiscountCode
@@ -296,6 +297,7 @@ val networkModule = module {
296297
.readTimeout(readTimeout.toLong(), TimeUnit.SECONDS)
297298
.addInterceptor(HostSelectionInterceptor(get()))
298299
.addInterceptor(RequestAuthenticator(get()))
300+
.authenticator(TokenAuthenticator())
299301
.addNetworkInterceptor(StethoInterceptor())
300302

301303
if (BuildConfig.DEBUG) {

0 commit comments

Comments
 (0)