Added token refresh.

This commit is contained in:
Stefan Schueller 2020-07-05 20:51:53 +02:00
parent 04ce44ac78
commit b235bbf423
7 changed files with 147 additions and 36 deletions

View File

@ -0,0 +1,49 @@
package net.schueller.peertube.network;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import okhttp3.Authenticator;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.Route;
public class AccessTokenAuthenticator implements Authenticator {
public AccessTokenAuthenticator() {
}
@Nullable
@Override
public Request authenticate(Route route, @NonNull Response response) {
Session session = Session.getInstance();
final String accessToken = session.getToken();
if (!isRequestWithAccessToken(response) || accessToken == null) {
return null;
}
synchronized (this) {
final String newAccessToken = session.getToken();
// Access token is refreshed in another thread.
if (!accessToken.equals(newAccessToken)) {
return newRequestWithAccessToken(response.request(), newAccessToken);
}
// Need to refresh an access token
final String updatedAccessToken = session.refreshAccessToken();
return newRequestWithAccessToken(response.request(), updatedAccessToken);
}
}
private boolean isRequestWithAccessToken(@NonNull Response response) {
String header = response.request().header("Authorization");
return header != null && header.startsWith("Bearer");
}
@NonNull
private Request newRequestWithAccessToken(@NonNull Request request, @NonNull String accessToken) {
return request.newBuilder()
.header("Authorization", accessToken)
.build();
}
}

View File

@ -42,4 +42,14 @@ public interface AuthenticationService {
@Field("password") String password @Field("password") String password
); );
@POST("users/token")
@FormUrlEncoded
Call<Token> refreshToken(
@Field("client_id") String clientId,
@Field("client_secret") String clientSecret,
@Field("grant_type") String grantType,
@Field("response_type") String responseType,
@Field("username") String username,
@Field("refresh_token") String refreshToken
);
} }

View File

@ -20,6 +20,8 @@ package net.schueller.peertube.network;
import android.util.Log; import android.util.Log;
import androidx.annotation.NonNull;
import java.io.IOException; import java.io.IOException;
import okhttp3.Interceptor; import okhttp3.Interceptor;
@ -31,8 +33,7 @@ public class AuthorizationInterceptor implements Interceptor {
public AuthorizationInterceptor() { public AuthorizationInterceptor() {
} }
@NonNull
@Override @Override
public Response intercept(Chain chain) throws IOException { public Response intercept(Chain chain) throws IOException {

View File

@ -33,6 +33,7 @@ public class RetrofitInstance {
OkHttpClient.Builder okhttpClientBuilder = new OkHttpClient.Builder(); OkHttpClient.Builder okhttpClientBuilder = new OkHttpClient.Builder();
okhttpClientBuilder.addInterceptor(new AuthorizationInterceptor()); okhttpClientBuilder.addInterceptor(new AuthorizationInterceptor());
okhttpClientBuilder.authenticator(new AccessTokenAuthenticator());
retrofit = new retrofit2.Retrofit.Builder() retrofit = new retrofit2.Retrofit.Builder()
.client(okhttpClientBuilder.build()) .client(okhttpClientBuilder.build())

View File

@ -20,24 +20,25 @@ package net.schueller.peertube.network;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.util.Log;
import net.schueller.peertube.R; import net.schueller.peertube.R;
import net.schueller.peertube.application.AppApplication; import net.schueller.peertube.application.AppApplication;
import static net.schueller.peertube.service.LoginService.refreshToken;
public class Session { public class Session {
private static volatile Session sSoleInstance; private static volatile Session sSoleInstance;
private static SharedPreferences sharedPreferences; private static SharedPreferences sharedPreferences;
//private constructor. //private constructor.
private Session(){ private Session() {
Context context = AppApplication.getContext(); Context context = AppApplication.getContext();
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
//Prevent form the reflection api. //Prevent form the reflection api.
if (sSoleInstance != null){ if (sSoleInstance != null) {
throw new RuntimeException("Use getInstance() method to get the single instance of this class."); throw new RuntimeException("Use getInstance() method to get the single instance of this class.");
} }
} }
@ -58,7 +59,6 @@ public class Session {
} }
public boolean isLoggedIn() { public boolean isLoggedIn() {
// check if token exist or not // check if token exist or not
// return true if exist otherwise false // return true if exist otherwise false
@ -69,13 +69,6 @@ public class Session {
return getToken() != null; return getToken() != null;
} }
public void saveToken(String token) {
// save the token
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString(AppApplication.getContext().getString(R.string.pref_token_access), token);
editor.commit();
}
public String getToken() { public String getToken() {
// return the token that was saved earlier // return the token that was saved earlier
@ -89,27 +82,19 @@ public class Session {
return null; return null;
} }
public void saveUsername(String username) {
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString(AppApplication.getContext().getString(R.string.pref_auth_username), username);
editor.commit();
}
public String getEmail() {
return sharedPreferences.getString(AppApplication.getContext().getString(R.string.pref_auth_username), null);
}
public void savePassword(String password) {
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString(AppApplication.getContext().getString(R.string.pref_auth_password), password);
editor.commit();
}
public String getPassword() { public String getPassword() {
return sharedPreferences.getString(AppApplication.getContext().getString(R.string.pref_auth_password), null); return sharedPreferences.getString(AppApplication.getContext().getString(R.string.pref_auth_password), null);
} }
public String refreshAccessToken() {
refreshToken();
// refresh token
return this.getToken();
}
public void invalidate() { public void invalidate() {
// get called when user become logged out // get called when user become logged out
// delete token and other user info // delete token and other user info

View File

@ -26,6 +26,7 @@ import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import net.schueller.peertube.R; import net.schueller.peertube.R;
import net.schueller.peertube.application.AppApplication;
import net.schueller.peertube.helper.APIUrlHelper; import net.schueller.peertube.helper.APIUrlHelper;
import net.schueller.peertube.model.OauthClient; import net.schueller.peertube.model.OauthClient;
import net.schueller.peertube.model.Token; import net.schueller.peertube.model.Token;
@ -42,8 +43,7 @@ public class LoginService {
private static final String TAG = "LoginService"; private static final String TAG = "LoginService";
public static void Authenticate(String username, String password) public static void Authenticate(String username, String password) {
{
Context context = getContext(); Context context = getContext();
String apiBaseURL = APIUrlHelper.getUrlWithVersion(context); String apiBaseURL = APIUrlHelper.getUrlWithVersion(context);
@ -62,6 +62,14 @@ public class LoginService {
OauthClient oauthClient = response.body(); OauthClient oauthClient = response.body();
SharedPreferences.Editor editor = sharedPref.edit();
assert oauthClient != null;
editor.putString(context.getString(R.string.pref_client_id), oauthClient.getClientId());
editor.putString(context.getString(R.string.pref_client_secret), oauthClient.getClientSecret());
editor.apply();
Call<Token> call2 = service.getAuthenticationToken( Call<Token> call2 = service.getAuthenticationToken(
oauthClient.getClientId(), oauthClient.getClientId(),
oauthClient.getClientSecret(), oauthClient.getClientSecret(),
@ -79,11 +87,7 @@ public class LoginService {
Token token = response2.body(); Token token = response2.body();
SharedPreferences.Editor editor = sharedPref.edit(); assert token != null;
// TODO: calc expiration
//editor.putInt(getString(R.string.pref_token_expiration), token.getRefreshToken());
editor.putString(context.getString(R.string.pref_token_access), token.getAccessToken()); editor.putString(context.getString(R.string.pref_token_access), token.getAccessToken());
editor.putString(context.getString(R.string.pref_token_refresh), token.getExpiresIn()); editor.putString(context.getString(R.string.pref_token_refresh), token.getExpiresIn());
editor.putString(context.getString(R.string.pref_token_type), token.getTokenType()); editor.putString(context.getString(R.string.pref_token_type), token.getTokenType());
@ -124,4 +128,62 @@ public class LoginService {
} }
}); });
} }
public static void refreshToken() {
Context context = getContext();
String apiBaseURL = APIUrlHelper.getUrlWithVersion(context);
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
AuthenticationService service = RetrofitInstance.getRetrofitInstance(apiBaseURL).create(AuthenticationService.class);
String refreshToken = sharedPreferences.getString(AppApplication.getContext().getString(R.string.pref_token_refresh), null);
String userName = sharedPreferences.getString(AppApplication.getContext().getString(R.string.pref_auth_username), null);
String clientId = sharedPreferences.getString(AppApplication.getContext().getString(R.string.pref_client_id), null);
String clientSecret = sharedPreferences.getString(AppApplication.getContext().getString(R.string.pref_client_secret), null);
Call<Token> call = service.refreshToken(
clientId,
clientSecret,
"refresh_token",
"code",
userName,
refreshToken
);
call.enqueue(new Callback<Token>() {
@Override
public void onResponse(@NonNull Call<Token> call, @NonNull retrofit2.Response<Token> response) {
if (response.isSuccessful()) {
Token token = response.body();
SharedPreferences.Editor editor = sharedPreferences.edit();
assert token != null;
editor.putString(context.getString(R.string.pref_token_access), token.getAccessToken());
editor.putString(context.getString(R.string.pref_token_refresh), token.getExpiresIn());
editor.putString(context.getString(R.string.pref_token_type), token.getTokenType());
editor.apply();
Log.wtf(TAG, "Logged in");
Toast.makeText(context, context.getString(R.string.authentication_login_success), Toast.LENGTH_LONG).show();
} else {
Log.wtf(TAG, response.toString());
Toast.makeText(context, context.getString(R.string.authentication_login_failed), Toast.LENGTH_LONG).show();
}
}
@Override
public void onFailure(@NonNull Call<Token> call2, @NonNull Throwable t2) {
Log.wtf("err", t2.fillInStackTrace());
Toast.makeText(context, context.getString(R.string.authentication_login_failed), Toast.LENGTH_LONG).show();
}
});
}
} }

View File

@ -26,6 +26,8 @@
<string name="pref_token_type" translatable="false">pref_token_type</string> <string name="pref_token_type" translatable="false">pref_token_type</string>
<string name="pref_auth_username" translatable="false">pref_auth_username</string> <string name="pref_auth_username" translatable="false">pref_auth_username</string>
<string name="pref_auth_password" translatable="false">pref_auth_password</string> <string name="pref_auth_password" translatable="false">pref_auth_password</string>
<string name="pref_client_id" translatable="false">pref_client_id</string>
<string name="pref_client_secret" translatable="false">pref_client_secret</string>
<string name="pref_api_base_key" translatable="false">pref_api_base</string> <string name="pref_api_base_key" translatable="false">pref_api_base</string>
<string name="pref_quality_key" translatable="false">pref_quality</string> <string name="pref_quality_key" translatable="false">pref_quality</string>
@ -34,6 +36,7 @@
<string name="pref_background_stop_key" translatable="false">backgroundStop</string> <string name="pref_background_stop_key" translatable="false">backgroundStop</string>
<string name="pref_background_float_key" translatable="false">backgroundFloat</string> <string name="pref_background_float_key" translatable="false">backgroundFloat</string>
<string name="peertube_required_server_version" translatable="false">1.0.0-alpha.7</string> <string name="peertube_required_server_version" translatable="false">1.0.0-alpha.7</string>
<string name="app_background_audio" translatable="false">BACKGROUND_AUDIO</string> <string name="app_background_audio" translatable="false">BACKGROUND_AUDIO</string>