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_URI | The URL that gets authorisation code in query |
APP_CLIENT_ID | Configured 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.
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
The response contians both access_token
and refresh_token
. I am saving them for future reference using shared_preferences
Final Result and Code
The Main Page
{{< renderhtml `
`>}}
On clicking the button it redirects to reddit login page
{{< renderhtml `
`>}}
Once logged in, reddit prompts user to provide necessary permissions to the app
{{< renderhtml `
`>}}
and finally all done.
{{< renderhtml `
`>}}