diff --git a/lab04/AuthCodeApp/.gitignore b/lab04/AuthCodeApp/.gitignore new file mode 100644 index 0000000..dc42d53 --- /dev/null +++ b/lab04/AuthCodeApp/.gitignore @@ -0,0 +1,10 @@ +*.iml +.gradle +/local.properties +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +.DS_Store +/build +/captures +.externalNativeBuild diff --git a/lab04/AuthCodeApp/.idea/caches/build_file_checksums.ser b/lab04/AuthCodeApp/.idea/caches/build_file_checksums.ser new file mode 100644 index 0000000..1bcd2ca Binary files /dev/null and b/lab04/AuthCodeApp/.idea/caches/build_file_checksums.ser differ diff --git a/lab04/AuthCodeApp/.idea/codeStyles/Project.xml b/lab04/AuthCodeApp/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..2715a34 --- /dev/null +++ b/lab04/AuthCodeApp/.idea/codeStyles/Project.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lab04/AuthCodeApp/.idea/gradle.xml b/lab04/AuthCodeApp/.idea/gradle.xml new file mode 100644 index 0000000..7ac24c7 --- /dev/null +++ b/lab04/AuthCodeApp/.idea/gradle.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file diff --git a/lab04/AuthCodeApp/.idea/misc.xml b/lab04/AuthCodeApp/.idea/misc.xml new file mode 100644 index 0000000..99202cc --- /dev/null +++ b/lab04/AuthCodeApp/.idea/misc.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/lab04/AuthCodeApp/.idea/runConfigurations.xml b/lab04/AuthCodeApp/.idea/runConfigurations.xml new file mode 100644 index 0000000..7f68460 --- /dev/null +++ b/lab04/AuthCodeApp/.idea/runConfigurations.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/lab04/AuthCodeApp/app/.gitignore b/lab04/AuthCodeApp/app/.gitignore new file mode 100644 index 0000000..3543521 --- /dev/null +++ b/lab04/AuthCodeApp/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/lab04/AuthCodeApp/app/build.gradle b/lab04/AuthCodeApp/app/build.gradle new file mode 100644 index 0000000..f8ec293 --- /dev/null +++ b/lab04/AuthCodeApp/app/build.gradle @@ -0,0 +1,36 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 27 + defaultConfig { + applicationId "spring2go.io.authcodeapp" + minSdkVersion 21 + targetSdkVersion 27 + versionCode 1 + versionName "1.0" + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + + packagingOptions { + exclude 'META-INF/LICENSE' + } + +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation 'com.android.support:appcompat-v7:27.1.1' + implementation 'com.android.support.constraint:constraint-layout:1.0.2' + testImplementation 'junit:junit:4.12' + compile 'com.squareup.retrofit2:retrofit:2.3.0' + compile 'com.squareup.retrofit2:converter-jackson:2.3.0' + compile 'com.squareup.okhttp3:logging-interceptor:3.9.0' + androidTestImplementation 'com.android.support.test:runner:1.0.1' + androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' +} diff --git a/lab04/AuthCodeApp/app/proguard-rules.pro b/lab04/AuthCodeApp/app/proguard-rules.pro new file mode 100644 index 0000000..6e7ffa9 --- /dev/null +++ b/lab04/AuthCodeApp/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/lab04/AuthCodeApp/app/src/androidTest/java/spring2go/io/authcodeapp/ExampleInstrumentedTest.java b/lab04/AuthCodeApp/app/src/androidTest/java/spring2go/io/authcodeapp/ExampleInstrumentedTest.java new file mode 100644 index 0000000..80f1498 --- /dev/null +++ b/lab04/AuthCodeApp/app/src/androidTest/java/spring2go/io/authcodeapp/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package spring2go.io.authcodeapp; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getTargetContext(); + + assertEquals("spring2go.io.authcodeapp", appContext.getPackageName()); + } +} diff --git a/lab04/AuthCodeApp/app/src/main/AndroidManifest.xml b/lab04/AuthCodeApp/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..a1306ea --- /dev/null +++ b/lab04/AuthCodeApp/app/src/main/AndroidManifest.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/client/ClientAPI.java b/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/client/ClientAPI.java new file mode 100644 index 0000000..606f425 --- /dev/null +++ b/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/client/ClientAPI.java @@ -0,0 +1,22 @@ +package spring2go.io.authcodeapp.client; + +import spring2go.io.authcodeapp.client.interceptor.OAuth2ClientAuthenticationInterceptor; +import spring2go.io.authcodeapp.client.oauth2.OAuth2API; +import spring2go.io.authcodeapp.client.userinfo.UserInfoAPI; +import spring2go.io.authcodeapp.client.userinfo.UserInfoAPI; + +public class ClientAPI { + public static final String BASE_URL = "172.30.17.42:8080"; + + public static UserInfoAPI userInfo() { + RetrofitAPIFactory api = new RetrofitAPIFactory(BASE_URL, null); + return api.getRetrofit().create(UserInfoAPI.class); + } + + public static OAuth2API oauth2() { + RetrofitAPIFactory api = new RetrofitAPIFactory(BASE_URL, + new OAuth2ClientAuthenticationInterceptor()); + return api.getRetrofit().create(OAuth2API.class); + } + +} diff --git a/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/client/RetrofitAPIFactory.java b/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/client/RetrofitAPIFactory.java new file mode 100644 index 0000000..7415da9 --- /dev/null +++ b/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/client/RetrofitAPIFactory.java @@ -0,0 +1,34 @@ +package spring2go.io.authcodeapp.client; + +import spring2go.io.authcodeapp.client.interceptor.BearerTokenHeaderInterceptor; +import spring2go.io.authcodeapp.client.interceptor.ErrorInterceptor; +import spring2go.io.authcodeapp.client.interceptor.OAuth2ClientAuthenticationInterceptor; +import okhttp3.OkHttpClient; +import retrofit2.Retrofit; +import retrofit2.converter.jackson.JacksonConverterFactory; + +class RetrofitAPIFactory { + private final Retrofit retrofit; + + RetrofitAPIFactory(String baseUrl, + OAuth2ClientAuthenticationInterceptor clientAuthentication) { + retrofit = new Retrofit.Builder() + .baseUrl("http://" + baseUrl) + .addConverterFactory(JacksonConverterFactory.create()) + .client(createClient(clientAuthentication)) + .build(); + } + + public Retrofit getRetrofit() { return retrofit; } + + private OkHttpClient createClient( + OAuth2ClientAuthenticationInterceptor clientAuthentication) { + OkHttpClient.Builder client = new OkHttpClient.Builder(); + client.addInterceptor(new ErrorInterceptor()); + client.addInterceptor(new BearerTokenHeaderInterceptor()); + if (clientAuthentication != null) { + client.addInterceptor(clientAuthentication); + } + return client.build(); + } +} diff --git a/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/client/interceptor/BearerTokenHeaderInterceptor.java b/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/client/interceptor/BearerTokenHeaderInterceptor.java new file mode 100644 index 0000000..22e963f --- /dev/null +++ b/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/client/interceptor/BearerTokenHeaderInterceptor.java @@ -0,0 +1,33 @@ +package spring2go.io.authcodeapp.client.interceptor; + +import java.io.IOException; +import java.util.List; + +import okhttp3.Interceptor; +import okhttp3.Request; +import okhttp3.Response; + +/** + * Interceptor that adds Bearer prefix to an access token + */ +public class BearerTokenHeaderInterceptor implements Interceptor { + @Override + public Response intercept(Chain chain) throws IOException { + + Request request = chain.request(); + + List headers = request.headers("Authorization"); + if (headers.size() > 0) { + String accessTokenValue = headers.get(0); + + request = request.newBuilder() + .removeHeader("Authorization") + .addHeader("Authorization", "Bearer " + accessTokenValue) + .build(); + + } + + return chain.proceed(request); + + } +} diff --git a/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/client/interceptor/ErrorInterceptor.java b/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/client/interceptor/ErrorInterceptor.java new file mode 100644 index 0000000..8264a2f --- /dev/null +++ b/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/client/interceptor/ErrorInterceptor.java @@ -0,0 +1,28 @@ +package spring2go.io.authcodeapp.client.interceptor; + +import java.io.IOException; + +import okhttp3.Interceptor; +import okhttp3.Request; +import okhttp3.Response; + +public class ErrorInterceptor implements Interceptor { + @Override + public Response intercept(Chain chain) throws IOException { + Request request = chain.request(); + Response response = chain.proceed(request); + + boolean httpError = (response.code() >= 400); + if (httpError) { + throw new HttpException(response.code() + ":" + response.message()); + } + + return response; + } + + public static class HttpException extends RuntimeException { + public HttpException(String message) { + super(message); + } + } +} diff --git a/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/client/interceptor/OAuth2ClientAuthenticationInterceptor.java b/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/client/interceptor/OAuth2ClientAuthenticationInterceptor.java new file mode 100644 index 0000000..e179d1f --- /dev/null +++ b/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/client/interceptor/OAuth2ClientAuthenticationInterceptor.java @@ -0,0 +1,29 @@ +package spring2go.io.authcodeapp.client.interceptor; + +import android.util.Base64; + +import java.io.IOException; + +import okhttp3.Interceptor; +import okhttp3.Request; +import okhttp3.Response; + +public class OAuth2ClientAuthenticationInterceptor implements Interceptor { + @Override + public Response intercept(Chain chain) throws IOException { + Request request = chain.request(); + + Request authenticatedRequest = request.newBuilder() + .addHeader("Authorization", getEncodedAuthorization()) + .addHeader("Content-Type", "application/x-www-form-urlencoded") + .method(request.method(), request.body()) + .build(); + + return chain.proceed(authenticatedRequest); + } + + private String getEncodedAuthorization() { + return "Basic " + Base64.encodeToString( + "mobileclient:112233".getBytes(), Base64.NO_WRAP); + } +} diff --git a/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/client/oauth2/AccessToken.java b/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/client/oauth2/AccessToken.java new file mode 100644 index 0000000..bed8bf5 --- /dev/null +++ b/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/client/oauth2/AccessToken.java @@ -0,0 +1,70 @@ +package spring2go.io.authcodeapp.client.oauth2; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.Date; + +public class AccessToken { + + @JsonProperty("access_token") + private String value; + + @JsonProperty("token_type") + private String tokenType; + + @JsonProperty("expires_in") + private Long expiresIn; + + @JsonIgnore + private Long issuedAt = new Date().getTime(); // issued at in milliseconds + + private String scope; + + public boolean isExpired() { + Long expirationTimeInSeconds = (issuedAt / 1000) + expiresIn; + Long nowInSeconds = (new Date().getTime()) / 1000; + + return expirationTimeInSeconds < nowInSeconds; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public String getTokenType() { + return tokenType; + } + + public void setTokenType(String tokenType) { + this.tokenType = tokenType; + } + + public Long getExpiresIn() { + return expiresIn; + } + + public void setExpiresIn(Long expiresIn) { + this.expiresIn = expiresIn; + } + + public String getScope() { + return scope; + } + + public void setScope(String scope) { + this.scope = scope; + } + + public Long getIssuedAt() { + return issuedAt; + } + + public void setIssuedAt(Long issuedAt) { + this.issuedAt = issuedAt; + } +} diff --git a/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/client/oauth2/AccessTokenRequest.java b/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/client/oauth2/AccessTokenRequest.java new file mode 100644 index 0000000..7869278 --- /dev/null +++ b/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/client/oauth2/AccessTokenRequest.java @@ -0,0 +1,17 @@ +package spring2go.io.authcodeapp.client.oauth2; + +import java.util.HashMap; +import java.util.Map; + +public class AccessTokenRequest { + + public static Map fromCode(String code) { + Map map = new HashMap<>(); + map.put("code", code); + map.put("scope", "read_userinfo"); + map.put("grant_type", "authorization_code"); + map.put("redirect_uri", AuthorizationRequest.REDIRECT_URI); + return map; + } + +} diff --git a/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/client/oauth2/AuthorizationRequest.java b/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/client/oauth2/AuthorizationRequest.java new file mode 100644 index 0000000..d95072d --- /dev/null +++ b/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/client/oauth2/AuthorizationRequest.java @@ -0,0 +1,24 @@ +package spring2go.io.authcodeapp.client.oauth2; + +import android.net.Uri; + +import spring2go.io.authcodeapp.client.ClientAPI; + +public class AuthorizationRequest { + public static final String REDIRECT_URI + = "oauth2://userinfo/callback"; + + public static Uri createAuthorizationUri(String state) { + return new Uri.Builder() + .scheme("http") + .encodedAuthority(ClientAPI.BASE_URL) + .path("/oauth/authorize") + .appendQueryParameter("client_id", "mobileclient") + .appendQueryParameter("response_type", "code") + .appendQueryParameter("redirect_uri", REDIRECT_URI) + .appendQueryParameter("scope", "read_userinfo") + .appendQueryParameter("state", state) + .build(); + } + +} diff --git a/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/client/oauth2/OAuth2API.java b/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/client/oauth2/OAuth2API.java new file mode 100644 index 0000000..b890b37 --- /dev/null +++ b/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/client/oauth2/OAuth2API.java @@ -0,0 +1,16 @@ +package spring2go.io.authcodeapp.client.oauth2; + +import java.util.Map; + +import retrofit2.Call; +import retrofit2.http.FieldMap; +import retrofit2.http.FormUrlEncoded; +import retrofit2.http.POST; + +public interface OAuth2API { + + @FormUrlEncoded + @POST("oauth/token") + Call requestToken(@FieldMap Map tokenRequest); + +} diff --git a/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/client/oauth2/OAuth2StateManager.java b/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/client/oauth2/OAuth2StateManager.java new file mode 100644 index 0000000..330668f --- /dev/null +++ b/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/client/oauth2/OAuth2StateManager.java @@ -0,0 +1,28 @@ +package spring2go.io.authcodeapp.client.oauth2; + +import android.content.Context; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; + +public class OAuth2StateManager { + + private final SharedPreferences prefs; + + public OAuth2StateManager(Context context) { + prefs = PreferenceManager.getDefaultSharedPreferences(context); + } + + public void saveState(String state) { + SharedPreferences.Editor editor = prefs.edit(); + editor.putString("state", state); + editor.commit(); + } + + public String getState() { + return prefs.getString("state", ""); + } + + public boolean isValidState(String state) { + return this.getState().equals(state); + } +} diff --git a/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/client/oauth2/TokenStore.java b/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/client/oauth2/TokenStore.java new file mode 100644 index 0000000..f771e11 --- /dev/null +++ b/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/client/oauth2/TokenStore.java @@ -0,0 +1,40 @@ +package spring2go.io.authcodeapp.client.oauth2; + + +import android.content.Context; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; + +public class TokenStore { + private final SharedPreferences prefs; + + public TokenStore(Context context) { + prefs = PreferenceManager.getDefaultSharedPreferences(context); + } + + public void save(AccessToken accessToken) { + SharedPreferences.Editor editor = prefs.edit(); + editor.putBoolean("authorized", true); + editor.putString("access_token", accessToken.getValue()); + editor.putString("scope", accessToken.getScope()); + editor.putString("token_type", accessToken.getTokenType()); + editor.putLong("expires_in", accessToken.getExpiresIn()); + editor.putLong("issued_at", accessToken.getIssuedAt()); + editor.commit(); + } + + public AccessToken getToken() { + AccessToken token = null; + + boolean authorized = prefs.getBoolean("authorized", false); + if (authorized) { + token = new AccessToken(); + token.setValue(prefs.getString("access_token", null)); + token.setScope(prefs.getString("scope", "")); + token.setTokenType(prefs.getString("token_type", "bearer")); + token.setExpiresIn(prefs.getLong("expires_in", -1)); // prevents / 0 + token.setIssuedAt(prefs.getLong("issued_at", -1)); // prevents / 0 + } + return token; + } +} diff --git a/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/client/userinfo/UserInfo.java b/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/client/userinfo/UserInfo.java new file mode 100644 index 0000000..bd63465 --- /dev/null +++ b/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/client/userinfo/UserInfo.java @@ -0,0 +1,16 @@ +package spring2go.io.authcodeapp.client.userinfo; + +public class UserInfo { + + private String name; + private String email; + + public String getName() { + return name; + } + + public String getEmail() { + return email; + } + +} diff --git a/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/client/userinfo/UserInfoAPI.java b/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/client/userinfo/UserInfoAPI.java new file mode 100644 index 0000000..e48da62 --- /dev/null +++ b/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/client/userinfo/UserInfoAPI.java @@ -0,0 +1,13 @@ +package spring2go.io.authcodeapp.client.userinfo; + + +import retrofit2.Call; +import retrofit2.http.GET; +import retrofit2.http.Header; + +public interface UserInfoAPI { + + @GET("api/userinfo") + Call token(@Header("Authorization") String accessToken); + +} diff --git a/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/presenter/AuthorizationCodeActivity.java b/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/presenter/AuthorizationCodeActivity.java new file mode 100644 index 0000000..c450776 --- /dev/null +++ b/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/presenter/AuthorizationCodeActivity.java @@ -0,0 +1,73 @@ +package spring2go.io.authcodeapp.presenter; + +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.util.Log; +import android.widget.Toast; + +import spring2go.io.authcodeapp.R; +import spring2go.io.authcodeapp.client.ClientAPI; +import spring2go.io.authcodeapp.client.oauth2.AccessToken; +import spring2go.io.authcodeapp.client.oauth2.AccessTokenRequest; +import spring2go.io.authcodeapp.client.oauth2.OAuth2StateManager; +import spring2go.io.authcodeapp.client.oauth2.TokenStore; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; + +public class AuthorizationCodeActivity extends AppCompatActivity { + + private String code; + private String state; + private TokenStore tokenStore; + private OAuth2StateManager manager; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_user_info); + + tokenStore = new TokenStore(this); + manager = new OAuth2StateManager(this); + + Uri callbackUri = Uri.parse(getIntent().getDataString()); + + code = callbackUri.getQueryParameter("code"); + state = callbackUri.getQueryParameter("state"); + + // validates state + if (!manager.isValidState(state)) { + Toast.makeText(this, "CSRF Attack detected", Toast.LENGTH_SHORT).show(); + return; + } + + Call accessTokenCall = ClientAPI + .oauth2() + .requestToken(AccessTokenRequest.fromCode(code)); + + accessTokenCall.enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) { + AccessToken token = response.body(); + tokenStore.save(token); + + // go to the other activity with an access token in hands!!!!!!! + + Intent intent = new Intent(AuthorizationCodeActivity.this, + UserInfoActivity.class); + startActivity(intent); + finish(); + } + + @Override + public void onFailure(Call call, Throwable t) { + Log.e("AuthorizationCode", "Error retrieving access token", t); + } + }); + + + } + +} diff --git a/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/presenter/MainActivity.java b/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/presenter/MainActivity.java new file mode 100644 index 0000000..d8cd165 --- /dev/null +++ b/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/presenter/MainActivity.java @@ -0,0 +1,62 @@ +package spring2go.io.authcodeapp.presenter; + +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.View; +import android.widget.Button; + +import java.util.UUID; + +import spring2go.io.authcodeapp.R; +import spring2go.io.authcodeapp.client.oauth2.AccessToken; +import spring2go.io.authcodeapp.client.oauth2.AuthorizationRequest; +import spring2go.io.authcodeapp.client.oauth2.OAuth2StateManager; +import spring2go.io.authcodeapp.client.oauth2.TokenStore; + +public class MainActivity extends AppCompatActivity + implements View.OnClickListener { + + private Button userInfoButton; + + private TokenStore tokenStore; + + private OAuth2StateManager oauth2StateManager; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + tokenStore = new TokenStore(this); + oauth2StateManager = new OAuth2StateManager(MainActivity.this); + + userInfoButton = (Button) findViewById(R.id.userinfo_button); + userInfoButton.setOnClickListener(this); + } + + @Override + public void onClick(View view) { + + AccessToken accessToken = tokenStore.getToken(); + if (accessToken != null && !accessToken.isExpired()) { + Intent intent = new Intent(this, UserInfoActivity.class); + startActivity(intent); + return; + } + + // create a state parameter to start the authorization flow + String state = UUID.randomUUID().toString(); + oauth2StateManager.saveState(state); + + // creates the authorization URI to redirect user + Uri authorizationUri = AuthorizationRequest + .createAuthorizationUri(state); + + Intent authorizationIntent = new Intent(Intent.ACTION_VIEW); + authorizationIntent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); + authorizationIntent.setData(authorizationUri); + startActivity(authorizationIntent); + } +} diff --git a/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/presenter/UserInfoActivity.java b/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/presenter/UserInfoActivity.java new file mode 100644 index 0000000..1286e04 --- /dev/null +++ b/lab04/AuthCodeApp/app/src/main/java/spring2go/io/authcodeapp/presenter/UserInfoActivity.java @@ -0,0 +1,51 @@ +package spring2go.io.authcodeapp.presenter; + +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.util.Log; +import android.widget.TextView; + +import spring2go.io.authcodeapp.R; +import spring2go.io.authcodeapp.client.ClientAPI; +import spring2go.io.authcodeapp.client.oauth2.TokenStore; +import spring2go.io.authcodeapp.client.userinfo.UserInfo; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; + +public class UserInfoActivity extends AppCompatActivity { + + private TextView textName; + private TextView textEmail; + private TokenStore tokenStore; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_user_info); + + tokenStore = new TokenStore(this); + + textName = (TextView) findViewById(R.id.userinfo_name); + textEmail = (TextView) findViewById(R.id.userinfo_email); + + Call call = ClientAPI + .userInfo().token(tokenStore.getToken().getValue()); + + call.enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) { + UserInfo userInfo = response.body(); + textName.setText(userInfo.getName()); + textEmail.setText(userInfo.getEmail()); + } + + @Override + public void onFailure(Call call, Throwable t) { + Log.e("UserInfoActivity", "Error trying to retrieve user info", t); + } + }); + + } + +} \ No newline at end of file diff --git a/lab04/AuthCodeApp/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/lab04/AuthCodeApp/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..ddb26ad --- /dev/null +++ b/lab04/AuthCodeApp/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/lab04/AuthCodeApp/app/src/main/res/drawable/ic_launcher_background.xml b/lab04/AuthCodeApp/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..3a37cf6 --- /dev/null +++ b/lab04/AuthCodeApp/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lab04/AuthCodeApp/app/src/main/res/layout/activity_authorization_code.xml b/lab04/AuthCodeApp/app/src/main/res/layout/activity_authorization_code.xml new file mode 100644 index 0000000..913aa32 --- /dev/null +++ b/lab04/AuthCodeApp/app/src/main/res/layout/activity_authorization_code.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/lab04/AuthCodeApp/app/src/main/res/layout/activity_main.xml b/lab04/AuthCodeApp/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..b09893e --- /dev/null +++ b/lab04/AuthCodeApp/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,15 @@ + + + +