javaandroidapioauthaccountmanager

Android, AccountManager and OAuth


I'm sure this is basic and I'm missing something. I've read through other answers on SO, I've googled, I've read resources and I just can't wrap my head around what I need to do.

I'm trying to figure out how to write an app that connects to Twitch's API, specifically how to authenticate with Twitch's api. Their documentation is here: https://github.com/justintv/Twitch-API/blob/master/authentication.md

I've created an app and stored my keys.

Now comes the part where I want my user to click a button which launches the authentication on their website. From what I can tell I do this by using an AccountManager. Except... I can't figure out what I'm supposed to do.

Here's the excerpt I've found online:

AccountManager am = AccountManager.get(this);
        Bundle options = new Bundle();

        am.getAuthToken(
                myAccount_,                     // Account retrieved using getAccountsByType()
                "Manage your tasks",            // Auth scope
                options,                        // Authenticator-specific options
                this,                           // Your activity
                new OnTokenAcquired(),          // Callback called when a token is successfully acquired
                new Handler(new OnError()));    // Callback called if an error occurs

According to twitch's documentation I want to send the user to:

https://api.twitch.tv/kraken/oauth2/authorize
    ?response_type=code
    &client_id=[your client ID]
    &redirect_uri=[your registered redirect URI]
    &scope=[space separated list of scopes]
    &state=[your provided unique token]

And I simply have no idea how these two things need to be combined.


Solution

  • Firstly, I recommend to read the OAuth2 RFC. This should cover everything you need to know.

    The AccountManager code snippet won't help you much unless there already is an app that provides authentication for Twitch. If that's not the case you either need to use an existing OAuth2 library or implement your own. You could write your own AccountAuthenticator but that's a different challenge (and you still need some kind of OAuth2 client).

    Doing it yourself is not that hard, see below.

    Steps to implement it yourself

    Twitch recommends to use the "Implicit Grant Flow" for mobile apps. That's what I'm going to describe below.

    1. Get a client ID

    Register your app as outlined in Developer Setup to get a client ID

    As redirect URI you can use something like https://localhost:12398/, the actual port doesn't really matter.

    2. Build the authentication URL

    In your client app you need to construct the authentication URL like so:

    https://api.twitch.tv/kraken/oauth2/authorize?
        response_type=token&
        client_id=[your client ID]&
        redirect_uri=[your registered redirect URI]&
        scope=[space separated list of scopes]
    

    Apparently [your client ID] should be replaced by the client ID you've received from Twitch, same goes for [your registered redirect URI] (that's the URL above, i.e. https://localhost:12398/). [space separated list of scopes] is the list of scopes (i.e. features your want to access), see Scopes. Make sure you URL-encode the parameter values properly.

    Assuming your client ID is 123456 and the scopes you need are user_read and channel_read your URL would look like this:

    https://api.twitch.tv/kraken/oauth2/authorize?
        response_type=token&
        client_id=123456&
        redirect_uri=https%3A%2F%2Flocalhost%3A12398%2F&
        scope=user_read%20channel_read
    

    Note that you should also pass a state parameter, just use a randomly generated value. You can also append the (non-standard) force_verify parameter to make sure the user actually needs to log in each time (instead of continuing a previous session), but I think you can achieve the same by clearing the cookie store (given that you open the URL in a webview in the context of your app) before you open the login page.

    With a random state the URL would look like this:

    https://api.twitch.tv/kraken/oauth2/authorize?
        response_type=token&
        client_id=123456&
        redirect_uri=https%3A%2F%2Flocalhost%3A12398%2F&
        scope=user_read%20channel_read&
        state=82hdknaizuVBfd9847guHUIhndzhuehnb
    

    Again, make sure the state value is properly URL encoded.

    3. Open the authentication URL

    Ideally you just open the URL in a WebView inside of your app. In that case you need to intercept all request to load a new URL using WebViewClient.shouldOverrideUrlLoading

    Once the client is redirected to your redirect URL you can close the webview and continue with step 4.

    Theoretically it's possible to utilize the default browser to do the authentication, but I would have security concerns since an external app could learn about your client ID and the access token.

    4. Extract the access token

    The actual URL you get redirected to in step #3 will have the form:

    https://[your registered redirect URI]/#access_token=[an access token]&scope=[authorized scopes]
    

    or to pick up the example

    https://localhost:12398/#access_token=xxx&scope=user_read%20channel_read
    

    Where xxx is the actual access token.

    If you passed a state it will be present like so:

    https://localhost:12398/#access_token=xxx&scope=user_read%20channel_read&state=82hdknaizuVBfd9847guHUIhndzhuehnb
    

    All you have to do now is to parse the (URL encoded) access token, scope and state. Compare the scopes and state to the ones that you actually sent. If they match you can start using the access_token to authenticate.

    Note According to the OAuth2 RFC, the response URL MUST also contain a token_type and it SHOULD contain an expires_in duration in seconds.

    Once you received the access token you can use it to authenticate as described here.

    Access tokens issued by the Implicit Grant Flow usually expire after a certain time and the user needs to authenticate again. The Twitch documentation doesn't mention any expiration time, so it's possible that the token is valid forever. So make sure your app doesn't store it or store it in a secure way (like using Android's key store provider to generate and store a key to encrypt the access token).

    If the implicitly issued access token expires you could consider using the "Authorization Code Flow". That's quite similar but it contains an additional step to receive the access token and a "refresh token" that can be used to renew the access token. I leave it up to you to figure out how that works.