Skip to content

Instantly share code, notes, and snippets.

@dalegaspi
Last active July 16, 2022 12:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dalegaspi/bd9ea42255ed6783e59e8b945d4bb4a6 to your computer and use it in GitHub Desktop.
Save dalegaspi/bd9ea42255ed6783e59e8b945d4bb4a6 to your computer and use it in GitHub Desktop.
Flickr OAuth 1.0a Authentication with Flickr4J and Scribe

This is a very rudimentary implementation of Flickr OAuth 1.0a with Kotlin and Android 8+; I am trying to learn Android, OAuth, and Kotlin at the same time. A few things to note:

  • Flickr is using an old version of OAuth that nobody else uses.
  • There is less than a handful of example code out there that actually uses Flickr API endpoints that require authentication...most examples do not have auth because it is hard...and I found this the hard way :-)
  • There is probably a way to integrate this in Retrofit by writing a custom interceptor, but I have not gotten around that and I'm not sure if it's worth doing so.

Note the use of recently deprected AsyncTask . The one useful StackOverflow post cannot be used today cannot be used in recent versions of Android because Android OS no longer permit network calls in the UI thread, hence making the async calls everywhere. This solution is essentially that and this other post.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="edu.bu.cs683.myflickr">
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.MyFlickr">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
package edu.bu.cs683.myflickr
import android.net.Uri
import android.os.AsyncTask
import android.os.Bundle
import android.util.Log
import android.view.View
import android.webkit.WebView
import android.webkit.WebViewClient
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.flickr4java.flickr.Flickr
import com.flickr4java.flickr.REST
import com.github.scribejava.apis.FlickrApi
import com.github.scribejava.core.builder.ServiceBuilder
import com.github.scribejava.core.model.OAuth1AccessToken
import com.github.scribejava.core.model.OAuth1RequestToken
import com.github.scribejava.core.model.OAuthRequest
import com.github.scribejava.core.model.Verb
import com.github.scribejava.core.oauth.OAuth10aService
class MainActivity : AppCompatActivity() {
val APIKEY = "YOUR API KEY"
val APISECRET = "YOUR API SECRET"
lateinit var webview: WebView
lateinit var service: OAuth10aService
lateinit var verifier: String
lateinit var requestToken: OAuth1RequestToken
lateinit var accessToken: OAuth1AccessToken
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
webview = findViewById<WebView>(R.id.authWebView)
webview.settings.javaScriptEnabled = true
webview.settings.domStorageEnabled = true
service = ServiceBuilder(APIKEY)
.apiSecret(APISECRET)
.build(FlickrApi.instance(FlickrApi.FlickrPerm.DELETE))
FlickrGetAuthUrlTask().execute()
}
fun getAccessToken() {
object : AsyncTask<Void, Void, OAuth1AccessToken>() {
override fun doInBackground(vararg p0: Void?): OAuth1AccessToken? {
accessToken =
service.getAccessToken(requestToken, verifier)
val request = OAuthRequest(Verb.GET, "https://api.flickr.com/services/rest/")
request.addQuerystringParameter("method", "flickr.test.login")
request.addQuerystringParameter("format", "json")
request.addQuerystringParameter("nojsoncallback", "1")
service.signRequest(accessToken, request)
val response = service.execute(request).body
Log.d(TAG, "user = $response")
return accessToken
}
override fun onPostExecute(result: OAuth1AccessToken) {
Toast.makeText(
applicationContext,
"Token = " + result.token + "Secret = " + result.tokenSecret,
Toast.LENGTH_LONG
).show()
}
}.execute()
}
inner class FlickrGetAuthUrlTask : AsyncTask<Void, Void, String>() {
override fun doInBackground(vararg params: Void?): String? {
val flickr = Flickr(APIKEY, APISECRET, REST())
val authInterface = flickr.authInterface
requestToken = authInterface.getRequestToken("https://www.flickr.com/auth-72157720836323165")
val authURL = service.getAuthorizationUrl(requestToken)
return authURL
}
override fun onPostExecute(authURL: String?) {
webview.webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
// check for our custom callback protocol otherwise use default behavior
if (url != null) {
if (url.contains("oauth_verifier")) {
// if (url.startsWith("oauth")) {
// authorization complete hide webview for now.
webview.setVisibility(View.GONE)
val uri = Uri.parse(url)
verifier = uri.getQueryParameter("oauth_verifier")!!
getAccessToken()
return true
}
}
return super.shouldOverrideUrlLoading(view, url)
}
}
if (authURL != null) {
webview.loadUrl(authURL)
}
}
}
companion object {
val TAG: String = MainActivity::class.java.simpleName
}
}
@dalegaspi
Copy link
Author

dalegaspi commented Apr 28, 2022

I have an updated version that uses Kotlin coroutines instead of the deprecated AsyncTask class. Maybe I will update this gist at some point if there is appetite for it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment