single sign on with appauth poster

Easy Guide To Setup Single Sign-On With AppAuth Android

Appauth Android is a client SDK for native apps to authenticate and authorize end-users usingOAuth 2.0andOpenID Connect. Available foriOS,macOS,Android, andNative JSenvironments. it implements modern security and usabilitybest practicesfor native app authentication and authorization.

It strives to directly map the requests and responses of those specifications while following the idiomatic style of the implementation language. In addition to mapping the raw protocol flows, convenience methods are available to assist with common tasks like performing an action with fresh tokens.

Check out my other post using google APIs:

GooglePlaces Autocomplete Android Example

How To Get Current Latitude And Longitude In Android

How Appauth Android Works

AppAuth supports Android API 16 (Jellybean) and above. Browsers that provide a custom tabs implementation are preferred by the library, but not required. Both Custom URI Schemes (all supported versions of Android) and App Links (Android M / API 23+) can be used with the library.

AppAuth Workflow

Let’s create the example on google appauth using OAuth in android.

1. Add AppAuth As A Build Dependency

Add the following line to build.gradle. This will make the AppAuth library available to your project.

implementation 'net.openid:appauth:0.7.0'

2. Create The ServiceConfiguration

First, AppAuth must be instructed on how to interact with the authorization service. This can be done either by directly creating anAuthorizationServiceConfigurationinstance.

Directly specifying an AuthorizationServiceConfiguration involves providing the URIs of the authorization endpoint and token endpoint.

val serviceConfig = AuthorizationServiceConfiguration(
        Uri.parse("https://accounts.google.com/o/oauth2/v2/auth"), // authorization endpoint
        Uri.parse("https://www.googleapis.com/oauth2/v4/token") // token endpoint
    )

3. Build The AuthorizationRequest

Once you have an instance ofAuthorizationServiceConfiguration, you can now build an instance of AuthorizationRequest which describes the actual authorization request, including your OAuth client id, and the scopes you are requesting.

val clientId = "511828570984-fuprh0cm7665emlne3rnf9pk34kkn86s.apps.googleusercontent.com"
    val redirectUri = Uri.parse("com.google.codelabs.appauth:/oauth2callback")
    val builder = AuthorizationRequest.Builder(
        serviceConfig,
        clientId,
        ResponseTypeValues.CODE,
        redirectUri
    )
    builder.setScopes("profile")

    val authRequest = builder.build()

Be sure to register your own client ID when developing your own apps, update the clientId, and redirect URI values with your own (and the custom scheme registered in the AndroidManifest.xml). If using a different OAuth server, then you’ll need to register a client for that server, following their documentation.

4. Perform The Authorization Request

a startActivityForResult call using an Intent returned from the AuthorizationService.

val authService = AuthorizationService(this)
    val authIntent = authService.getAuthorizationRequestIntent(authRequest)
    startActivityForResult(authIntent, RC_AUTH)

5. Handle The Authorization Response

Once the authorization flow is completed in the browser, the authorization service will redirect to a URI specified as part of the authorization request, providing the response via query parameters. In order for your app to capture this response, it must register with the Android OS as a handler for this redirect URI.

When a custom scheme is used, AppAuth can be easily configured to capture all redirects using this custom scheme through a manifest placeholder:

android.defaultConfig.manifestPlaceholders = [
      'appAuthRedirectScheme': 'com.example.app'
    ]

Alternatively, the redirect URI can be directly configured by adding an intent filter for AppAuth’s RedirectUriReceiverActivity to yourAndroidManifest.xml:

<activity
            android:name="net.openid.appauth.RedirectUriReceiverActivity"
            tools:node="replace">
        <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.google.codelabs.appauth"/>
        </intent-filter>
    </activity>

If an HTTPS redirect URI is required instead of a custom scheme, the same approach (modifying your AndroidManifest.xml) is used:

<activity
            android:name="net.openid.appauth.RedirectUriReceiverActivity"
            tools:node="replace">
        <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="https"
                  android:host="app.example.com" android:path="/oauth2redirect"/>
        </intent-filter>
    </activity>

6. Handling The Authorization Response

Upon completion of the authorization flow, the completion Intent provided to performAuthorizationRequest will be triggered. The authorization response is provided to this activity via Intent extra data, which can be extracted using the fromIntent() methods on AuthorizationResponse and AuthorizationException respectively:

@Override

    protected void onActivityResult(int requestCode, int resultCode, Intent data) {

      if (requestCode == RC_AUTH) {

        AuthorizationResponse resp = AuthorizationResponse.fromIntent(data);

        AuthorizationException ex = AuthorizationException.fromIntent(data);

        // … process the response or exception …

      } else {

        // …

      }

    }

7. Exchanging The Authorization Code

Given a successful authorization response carrying an authorization code, a token request can be made to exchange the code for a refresh token:

mAuthService?.performTokenRequest(
        resp.createTokenExchangeRequest()) { resp, ex ->
        if (resp != null) {
            mStateManager?.updateAfterTokenResponse(resp, ex)

            Log.d("accessToken",resp.accessToken)

            // exchange succeeded
        } else {
            // authorization failed, check ex for more details
        }
    }

That’s it. We got an access token to access our API.

Access Token

8. Making API calls using Access Token

While you can get the tokens directly from the token response, those tokens expire and must be refreshed occasionally. Using AuthState and making your REST API calls insideauthState.performActionWithFreshTokensis recommended, as it will automatically ensure that the tokens are fresh (refreshing them when needed) before executing your code.

In our case, I am getting the username and profile picture of the logged-in user.

if (mStateManager?.current?.isAuthorized!!) {
                Log.d("Auth", "Done")
                button_login.text = "Logout"
                mStateManager?.current?.performActionWithFreshTokens(
                    mAuthService!!
                ) { accessToken, idToken, exception ->
                    ProfileTask().execute(accessToken)
                }

            }
        }

        inner class ProfileTask : AsyncTask<String?, Void, JSONObject>() {
            override fun doInBackground(vararg tokens: String?): JSONObject? {
                val client = OkHttpClient()
                val request = Request.Builder()
                    .url("https://www.googleapis.com/oauth2/v3/userinfo")
                    .addHeader("Authorization", String.format("Bearer %s", tokens[0]))
                    .build()
                try {
                    val response = client.newCall(request).execute()
                    val jsonBody: String = response.body().string()
                    Log.i("LOG_TAG", String.format("User Info Response %s", jsonBody))
                    return JSONObject(jsonBody)
                } catch (exception: Exception) {
                    Log.w("LOG_TAG", exception)
                }
                return null
            }
            override fun onPostExecute(userInfo: JSONObject?) {
                if (userInfo != null) {
                    val fullName = userInfo.optString("name", null)
                    val imageUrl =
                        userInfo.optString("picture", null)
                    if (!TextUtils.isEmpty(imageUrl)) {
                        Glide.with(this@LoginActivity).load(imageUrl).into(imgProfile);
                    }
                    if (!TextUtils.isEmpty(fullName)) {
                        textUsername.setText(fullName)
                    }
                    val message = if (userInfo.has("error")) { java.lang.String.format(
                            "%s [%s]", getString(R.string.request_failed), userInfo.optString("error_description", "No description"))
                    } else {
                        getString(R.string.request_complete)
                    }
                    Snackbar.make(imgProfile, message, Snackbar.LENGTH_SHORT).show()
                }
            }
        }

That’s it. we have implemented the Appauth using OAuth in android.

Download this example on Github.

Conclusion

Thanks for reading.

Please share if you like this post. Also, provide your feedback in the comments.


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *


Latest Posts