前回では、foursquareにアプリを登録して、コールバックURL、CLIENT_ID、CLIENT_SECRETを取得するところまででした。
次に、実際にOAuth2認証をしてみましょう。
※参照記事ではセキュリティのことを考えて云々とあるのですが、そこらへんは全て飛ばして、とりあえずOAuth2認証が通るところまでを目指します。
ソーシャルサービスのAPIを利用する場合、OAuth等で認証して、アクセストークンをもらいます。アクセストークンを持ってAPIにアクセスすることで、「あぁ、このアクセスは認証済みだな」とサーバにわかってもらう訳です。ひらたくいうと、OAuth認証を行うのは、アクセストークンをもらうことが目的です。
普通のソーシャルサービスでは、アクセストークンに有効期限があって、有効期限が切れたらまた新たなトークンを取得してそれを使う、という流れなのですが、foursquareは、なんと有効期限がありません。一度アクセストークンを取得したらずっとそのままです。とはいっても、foursquareのドキュメントには、有効期限を付けるようにするかもしれないからそのときは対応してね、と書いてあります。
ということで、Androidアプリの処理的には、
- アプリにアクセストークンが保存されているか、確認
- アクセストークンがなかったら、OAuth認証を行う
という流れになります。
アクセストークンはPreferencesに登録するのがよさげと判断したので、それ用のクラスを作りました。(まぁ参考資料のまんまに近い)
public class OAuthTokenStore {
private final Context mContext;
private String mToken;
public OAuthTokenStore(Context context) {
mContext = context.getApplicationContext();
mToken = readTokenFromSharedPreferences();
}
public String getToken() {
return mToken;
}
public void updateAccessToken(String accessToken) {
// エラーの場合を確認をしやすくするために、保存されたトークンを
// 消せるようにしてある。
// アプリが完成したらコメントアウト解除
// if (accessToken == null) {
// throw new NullPointerException("access token must not be null");
// }
mToken = accessToken;
saveToken();
}
private void saveToken() {
SharedPreferences pref = getPreferences();
Editor editor = pref.edit();
editor.putString("accessToken", mToken);
editor.commit();
}
private String readTokenFromSharedPreferences() {
SharedPreferences pref = getPreferences();
return pref.getString("accessToken", null);
}
private SharedPreferences getPreferences() {
return PreferenceManager.getDefaultSharedPreferences(mContext);
}
}
上のクラスを、ActivityのonCreateで呼び出します。
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Preferencesからアクセストークンを取得
OAuthTokenStore store = new OAuthTokenStore(getApplicationContext());
String accessToken = store.getToken();
if (accessToken == null) {
// アクセストークンがない場合の処理
// foursquareへの認証をさせるためのActivityへ移動
Intent i = new Intent(this, Login.class);
startActivity(i);
finish();
} else {
// アクセストークンがある場合の処理
}
まぁアクセストークンがなかったらいきなりfoursquareの認証画面に移動するような暗黙的Intentを発行してもいいのですが、アプリユーザにとってはいきなりfoursquareに移動して認証しろと言われるのは乱暴なので、一度確認用の画面(Login.class)をはさみました。といっても、次の画面ではfoursquareへ移動のボタンを置いているだけです。ここは簡単なのでレイアウトは割愛。
そのボタンを押したときに実行されるコード。
// Login.class
/** xmlでonClickを設定 */
public void login(View v) {
// ブラウザで4sqへの認証画面を呼び出す
OAuthClient.login(this);
}
OAuthCientってなんぞ?となると思いますが、これはOAuthを担当するクラスを作りました。(これも参考資料のまんまに近い)OAuthClient.login(Context);では、foursquareのOAuth2認証用のURLを開く暗黙的Intentを発行しています。
public class OAuthClient {
private static final String TAG = "OAuthClient";
private static final String ACCESS_TOKEN_URL = "https://ja.foursquare.com/oauth2/access_token";
private static final String AUTHORIZE_URL = "https://ja.foursquare.com/oauth2/authorize";
private static final String CALLBACK_URL = "com.okolabo.android.oauthsample://callback";
private static final String CLIENT_ID = "***************************";
private static final String CLIENT_SECRET = "****************************";
/**
* ブラウザを起動し、認証を行うURLへ遷移する。
* @param context 呼び出し元のContext
*/
public static void login(Context context) {
Uri uri = Uri.parse(AUTHORIZE_URL).buildUpon()
.appendQueryParameter("client_id", CLIENT_ID)
.appendQueryParameter("response_type", "code")
.appendQueryParameter("redirect_uri", CALLBACK_URL)
.build();
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(uri);
context.startActivity(i);
}
/**
* Authorization Code からアクセストークンを取得します。
* エラーだった場合は{@code null}を返します。
*
* @param code Authorization Code
* @return 取得したアクセストークンを返します。
* @throws ClientProtocolException
* @throws IOException
* @throws JSONException
*/
public static String getTokenFromAuthorizationCode(String code) throws ClientProtocolException, IOException, JSONException {
String token = null;
DefaultHttpClient client = new DefaultHttpClient();
Uri uri = Uri.parse(ACCESS_TOKEN_URL).buildUpon()
.appendQueryParameter("client_id", CLIENT_ID)
.appendQueryParameter("client_secret", CLIENT_SECRET)
.appendQueryParameter("grant_type", "authorization_code")
.appendQueryParameter("redirect_uri", CALLBACK_URL)
.appendQueryParameter("code", code)
.build();
HttpGet httpGet = new HttpGet(uri.toString());
HttpResponse response = client.execute(httpGet);
HttpEntity entity = response.getEntity();
String res = EntityUtils.toString(entity);
entity.consumeContent();
JSONObject json = new JSONObject(res);
if (json.has("access_token")) {
token = json.getString("access_token");
} else {
if (json.has("error")) {
String error = json.getString("error");
Log.d(TAG, error);
}
}
client.getConnectionManager().shutdown();
return token;
}
}
認証用のページに行くと、「登録したアプリから認証が求められているが、大丈夫ならログインしてください」というメッセージが出ます。まぁここはログインしてください。ログインすると、認証コード付きでコールバックURLにリダイレクトされます。
リダイレクトURLを拾わないと認証が進まないので、リダイレクトを拾います。
AndroidManifest.xmlのintent-filterを設定します。
あと、uses-permissionにandroid.permission.INTERNETを追加しといて下さい。
<activity
android:name="Login"
android:launchMode="singleTask"
>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="com.okolabo.android.oauthsample" android:host="callback" />
</intent-filter>
</activity>
これを設定しておいたら、ログインボタンを置いていたLogin.classがintent-filterによって呼び出されます。launchMode=”singleTask”にしておきます。これにより、すでにLogin.classは起動しているため、onNewIntentメソッドが呼ばれます。コールバックURLで呼ばれるコールバックが、Login.classのonNewIntentメソッドだったと思って下さい。
次の記事へ移動