Flutter Oauth Integration with Reddit


Introduction

This tutorial is about integrating a oauth based login flow with a flutter application. Basic understading of flutter framework and oauth flow is assumed.

Flutter Setup

  • Make sure flutter is setup flutter doctor
  • Create new Flutter application (android studio)
  • Add following dependencies in pubspec.yaml
url_launcher : To launch oauth login URL
shared_preferences : To save authorisation code and token
  • Remove the counter code from the main page

Reddit Application

  • Create new Reddit application https://ssl.reddit.com/prefs/apps/
  • Choose installed app An app intended for installation, such as on a mobile phone
  • Add a name, description and about URL
  • Add redirect URI as described below : REDIRECT_URI
  • Note down the code written beneath application name : APP_CLIENT_ID

Redirect URI

Unlike a web app the redirect URI can’t be a http(s) location. For an installed app it has to be an app specific URI so the mobile OS can redirect it properly. For this example I am choosing a URI whose schema matches the package name of the application.

com.knoetical.oauthexample://loginroutes/oauth

In this URI the com.knoetical.oauthexample is scheme, loginroutes is host and oauth is path.

Authorization Flow

From her on I’ll refere to these two variables a lot

REDIRECT_URIThe URL that gets authorisation code in query
APP_CLIENT_IDConfigured application’s client ID

Once configured try to launch the following URL from a browser. It might try to redirect to redirect uri with auth codes.

https://www.reddit.com/api/v1/authorize.compact?client_id={APP_CLIENT_ID}&response_type=code&state=RANDOM_STRING&redirect_uri={REDIRECT_URI}&duration=permanent&scope=read

Goal is to make sure that Android redirects this request to the flutter app.

Setting up URI handler in Android

Enable deeplinking in flutter. Go to Android directory in flutter project and locate Manifest.xml file (both debug and production) and add following lines under mainactivity section.

<meta-data android:name="flutter_deeplinking_enabled" android:value="true" />
<intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <data android:scheme="com.knoetical.oauthexample" />
</intent-filter>

PS: To make can_launch work, add this permission in Manifest.xml

<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>

Setting up Routing in Flutter

In the root MaterialApp, which is the starting point of flutter application, add route handling.

onGenerateRoute: (settings) {
    // ADD HERE //
    switch(settings.name) {
        ....
    }
}

Just before handling the usual route I am adding logic to parse the incoming route and check if there are query parameters for the one coming from oauth redirect.


Uri uri = Uri.parse(settings.name.toString());
switch(uri.path) {
    case '/oauth':
    {
        String query = uri.query;
        return MaterialPageRoute(
            builder: (context) =>
                RedditRedirectView(query: query,)
        );
    }
}

Notice that query parameters are being passed to RedditRedirectView, this will be the intermediary widget which will save the code and fetch the token.

Getting the access_token

The Stateful intermediary view is responsible for saving the authorisation code and fetching the access_token.

The authorisation code should be in the query parameter code.

The typical request takes oauth CLIENT_ID and SECRET as basic HTTP authentication and pass authorisation code and other relevant parameters as POST body.

Get the authorisation header in dart

var basicAuthStr = `CLIENT_ID`+":"; // no ecret in this case
var bytes = utf8.encode(basicAuthStr);
var base64Str = base64.encode(bytes);
var headers = {
    HttpHeaders.authorizationHeader: 'Basic $base64Str',
};

Create the POST body.

{
    "grant_type": "authorization_code",
    "code": code,
    "redirect_uri": REDIRECT_URI
}

For reddit, send the POST request to

https://www.reddit.com/api/v1/access_token

The response contians both access_token and refresh_token. I am saving them for future reference using shared_preferences

Final Result and Code

GitHub Link

The Main Page

{{< renderhtml `

1

`>}}

On clicking the button it redirects to reddit login page

{{< renderhtml `

2

`>}}

Once logged in, reddit prompts user to provide necessary permissions to the app

{{< renderhtml `

3

`>}}

and finally all done.

{{< renderhtml `

4

`>}}