DEV Community

oauth2_client: implement OAuth2 clients with Flutter

Enrico Triolo on April 06, 2020

In the first part of this article I will give you an overview of the OAuth 2 standard, in particular I will introduce the two most used grants, the...
Collapse
 
mma15 profile image
Mubashir Ali Mir

Hi Enrico! Thank you for the great article, it is really helpful. I do although need some help, I am trying to set up my own Oauth2 client for ios and I am able to to get to the Authorization page for this client but after I sign up it does not redirect back.

I added this to the Info.plist:

    <key>CFBundleURLTypes</key>
    <array>
        <dict>
            <key>CFBundleURLName</key>
            <string>https://zelos.gg</string>
            <key>CFBundleURLSchemes</key>
            <array>
                <string>zelos</string>
            </array>
        </dict>
    </array>
Enter fullscreen mode Exit fullscreen mode

As you can see our redirect is https (the auth specifically requires https) and I based on what I read this might be the issue but I am not 100% sure. Any suggestions would be greatly helpful.

Collapse
 
hiiamtrong profile image
hiiamtrong

Hi @mma15, i'm getting an error like you, Had you resolved it ?

Collapse
 
mucasantos profile image
Samuel Luis dos Santos

Hi Enrico. How I said before, this article is really amazing! I am just starting programming. I could implement a oauth2 following your tutorial and it works fine using iOS (flutter). However, it doesn't work when I try to use Android. I changed all this and still not works. What am I doing wrong??

My Manifest:

        <intent-filter android:label="flutter_web_auth">
            <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.example.ClassReport"
                />
        </intent-filter>
    </activity>
Enter fullscreen mode Exit fullscreen mode

and my GoogleClient:

final GoogleOAuth2Client client = GoogleOAuth2Client(
redirectUri: 'com.example.ClassReport:/accounts.google.com:/', //Can be any URI, but the scheme part must correspond to the customeUriScheme
customUriScheme: 'com.example.ClassReport');

What happen when I try to compile to Android: after put password and click login, I don't go back to App. It keeps on google chrome.

Thanks,

Collapse
 
okrad profile image
Enrico Triolo

Hi Samuel,
try changing the redirectUri, removing the ":/" at the end and using "://" after the scheme.

Something like com.example.ClassReport://accounts.google.com should work.

Remember to update the redirect uri accordingly in the Google application registration panel. Obviously you can use the same redirectUri for both iOS and Android versions.

Collapse
 
mucasantos profile image
Samuel Luis dos Santos

Thank you again for supporting me. I did that but the error persists. So, I downloaded my json file from Google and implemented my own class (I'd like to say I'm learning a lot!) But, still I have two different situations:

  1. If I use the redirect_uri of my json, I receive a browse message with an auth code;
  2. If instead, I use the word 'auto', the browse shows me: ' You have authorization. Change to your app'. However, if I do that Flutter gives me an 'abort error'.

I'm so sorry if I'm bothering you.

Below my class and my Json.
Json --> (client_id *, project_id *
"auth_uri":"accounts.google.com/o/oauth2/auth"...}

Class -->
import 'package:oauth2_client/oauth2_client.dart';
import 'package:meta/meta.dart';

class MyOAuth2Client extends OAuth2Client {
MyOAuth2Client({@required String redirectUri, @required String customUriScheme}): super(
authorizeUrl: 'accounts.google.com/o/oauth2/auth', //Your service's authorization url
tokenUrl: 'oauth2.googleapis.com/token', //Your service access token url
redirectUri: "urn:ietf:wg:oauth:2.0:oob:auto",
customUriScheme: "com.example.ClassReport"
);
}

Thanks again, Enrico.

Thread Thread
 
mucasantos profile image
Samuel Luis dos Santos • Edited

I cloned your test app from Git and believe or not, I received the same error when I try to use :// --> (image) I think this is a Brazil problem. heheheh

Collapse
 
dibaggio_1598 profile image
Dibaggio

Try to change the package's name, com.example.ClassReport to com.example.classreport redirect is not working when the package's name is in upperCase

Collapse
 
darrinps profile image
Darrin

You mentioned PKCE briefly. As it is recommended for use with Authorization Code in a mobile app, does oauth2_client support that under the covers? I don't see where to provide a 43-256 character code verifier so I am guessing either you do that under the covers or don't support PKCE?

Also, it is recommended that mobile apps use either an external user agent (a browser) or, to keep things cleaner UI wise, an in-app browser tab (Chrome tab for Android, or SFSafariViewController for iOS). Does oauth2_client bring up an in-app browser?

Collapse
 
okrad profile image
Enrico Triolo

Hi Darrin, yes oauth2_client uses PKCE by default.
The code verifier is generated by the client class before performing the authorization code request, but you can even supply your own to the getTokenWithAuthCodeFlow method.
There is currently no way of supplying a custom code verifier through the helper class.

As for the user agent, afaik the recommendation is to use an external browser and not an embedded one, mainly for security reasons (such as avoiding the app to modify or inspect the browser window). As such, oauth2_client relies only on an external browser instance.

Collapse
 
darrinps profile image
Darrin

Agreed that an external browser is suggested but is also a relatively poor user experience. They don't want you using a native flow, but as of OAuth2.0, they were good with an in-app browser tab. So:

In iOS use SFSafariViewController
In Android use CustomTabsService /Chrometabs

That's a better user experience I think most will agree.

Thread Thread
 
okrad profile image
Enrico Triolo

I agree with you it would be a better user experience and probably not even a security concern, since it wouldn't use a webview. I guess I'll take a look at how it could be integrated in the library...

Thank you for the suggestion!

Collapse
 
mayank99 profile image
Mayank • Edited

You said "oauth2_client uses PKCE by default" but the getTokenWithAuthCodeFlow method and the OAuth2Helper constructor both still ask for a client secret. The whole point of PKCE is to do away with client secrets because storing them in the frontend is insecure. How is this handled?

Collapse
 
okrad profile image
Enrico Triolo

Hi, the library is meant to be general, so it can be used in different scenarios.The client secret is optional, you can avoid passing it if the provider doesn't require it. On the other hand PKCE is enabled by default but can be disabled by passing enablePKCE: false to the getTokenWithAuthCodeFlow method.

Collapse
 
purvekta profile image
purvekta

Hi nice tutorial
I want to know how to get authentication code through flutter and give more example with oauth2.0 and flutter in more detail with complete process of login and logout. It will help a lot

Collapse
 
okrad profile image
Enrico Triolo

Hi thank you!
I just set up a new GitHub repository with a sample application.

Hope it helps!

Collapse
 
nuvolari2020 profile image
Nuvolari2020 • Edited

Salve Enrico,
Given I am new in programming, I am having this issue that I am not able to solve.
I implemented the helper to post call for Legrand API: developer.legrand.com/tutorials/0a...
The server returns the access token valid for 60 mins and refresh token valid for 90days. The received refresh_token changes at each request. In order to be valid lifetime, you have to use the new generated refresh_token code to refresh your access_token.
I can use the getToken() and I can also post a call but every time I call it I need to log in within Legrand, so I am not able to refresh the accesstoken. Moreover, (see example below) I tried to check if the token was stored after the request but I always get tokenResponse as null, so seems the getToken() every times call the fetchToken().
Any thoughts? Thanks in advance for the help!

Code example:
var hlp = OAuth2Helper(client, clientId: clientId, clientSecret: clientSecret, scopes: scopes);

hlp.post(URL, headers: headersTest, body: bodyTest);

// check if access token is working
var tokenResp = await hlp.getToken();
accessToken = tokenResp.accessToken;
print('access token = $accessToken');

//check if token is stored
var tokenResponse = await hlp.getTokenFromStorage();
print('token response from storage $tokenResponse ');

Collapse
 
okrad profile image
Enrico Triolo

Ciao Nuvolari2020!
So every time you post a request you are required to log in again?
This can happen when the requested scopes are incorrect or misspelled... Can you take a look at this issue, maybe it could help you too...

Collapse
 
nuvolari2020 profile image
Nuvolari2020

Ciao Enrico, thanks! I did not pass the right scopes, I solved it. Thanks a lot for the help and for the useful package. Thanks!!!

Thread Thread
 
okrad profile image
Enrico Triolo

Great, glad it helped!

Collapse
 
allviss0211 profile image
Allviss0211

Hi there, I'm using LinkedInOauthClient and Oauth2helper to get data user from LinkedIn. But it doesn't auto close browser to response data to me. It just load page redirect. If I back browser by back button it'll throw exception "user close browser" and no data response. Can u help me?

Collapse
 
okrad profile image
Enrico Triolo

Hi, which scheme are you using for the redirect uri? On Android it normally is much simpler to use a custom scheme instead of https.

You can find more info in the FAQ section of the readme in the github repository.

Collapse
 
mucasantos profile image
Samuel Luis dos Santos

Hi dear friend. It's work now!!! Problem? A capital letter. I started to read the Google screen and noticed that there was a capital letter. So, I changed that letter, stopped the all process and I started again, works fine. Ten hours just because of one letter. But I can't complain: I learnt a lot!
Again, thank you for supporting.
Have a nice and blessed week.
Samuel Santos

Collapse
 
okrad profile image
Enrico Triolo

Hi Samuel, I'm glad it works!

Collapse
 
mauriciocartagena profile image
Mauricio Cartagena

Hello, how can I implement the logout option?

Collapse
 
Sloan, the sloth mascot
Comment deleted
Collapse
 
gokul1212199 profile image
Gokul

Hi @yaredweldegebrialreda are you find out the solution to logout?

Collapse
 
myasinu profile image
myasinu

I tried implementing a custom Oauth2 class however i keep getting an error on redirect. The error says Canceled by user. I am not sure what value goes in the accesstokenrequestheader. The Api authorization returns a string 'redirect_uri?code={code}&state={state}' Also the api doesn't support scopes so I left it as an empty list.

Collapse
 
brwno profile image
brwno

Hi, how are you?
I'm trying to make the tutorial but when I make login with my github, have an error "404 Didn't find anything where". You can help me?
And If I wish use this from connect with my personal provider, I just remove the "github package" and install "oauth package"?
Thanks!!

Collapse
 
mubaola23 profile image
MubaH_dev

Hi Enrico, I must say thank you for python this together it really helps a lot. I was able to I’m the client and everything works fine aside from the fact that the token doesn’t get refreshed when it’s expired. It redirects back to the login page when the tokens expires. I’m looking forward to your response. Thank you

Collapse
 
hvkz______ profile image
Hosea Varghese

Thanks a lot for this. Really helped me learn

Collapse
 
okrad profile image
Enrico Triolo

Thank you, I'm glad it helps!

Collapse
 
mucasantos profile image
Samuel Luis dos Santos

Thank you for sharing this article! I've been looking out for this and I've found you. It really helped me a lot!

Collapse
 
okrad profile image
Enrico Triolo

Thank you Samuel, I'm glad it helped!

Collapse
 
sidoom profile image
Alexander Sidum

Hi thanks for the guide, but how do you assign the callback url on github?
mine always show "Url must be a valid URL" warning when i input the app package name on the callback url

Collapse
 
himdeve profile image
Himdeve

Hi, can you please tell me what should I set to the required parameter "Authorization callback URL" in the github to redirect it back to the app? Thx a lot!

Collapse
 
okrad profile image
Enrico Triolo

Hi, the authorization callback url must correspond to the redirectUri you use in your application, for example my.app://oauth2redirect

Collapse
 
javshak profile image
Javed Shaikh • Edited

Great article - one of the few ones on OAuth and Flutter actually. I had one specific question - would the oauth2_client library work with Flutter Web UI (the beta channel)? Thanks!

Collapse
 
codefinity profile image
Manav Misra

Great ❓. Welcome to the community. I would defer to the author, but if you are already using the Flutter Web UI, maybe just try to incorporate this approach and LUK how it goes. 😃

Collapse
 
strangenoob profile image
Prateek Mohanty • Edited

Hey Enrico,
Can you help me out how to access the token once saved?

Collapse
 
okrad profile image
Enrico Triolo

Hi, to access the token after it has been saved in the storage you can call the getToken() method of the helper.

For example:

var client = GitHubOAuth2Client(
    redirectUri: 'my.app://oauth2redirect',
    customUriScheme: 'my.app');

var oauth2Helper = OAuth2Helper(client,
    clientId: 'myclientid',
    clientSecret: 'myclientsecret',
    scopes: ['repo']);

var resp = await oauth2Helper.get('https://api.github.com/user/repos');

var tkn = oauth2Helper.getToken();
Collapse
 
strangenoob profile image
Prateek Mohanty

Thanks, Enrico for this. I have another doubt. How do I have to pass extra header properties in that getTokenWithAuthCodeFlow()?

Thread Thread
 
okrad profile image
Enrico Triolo

Hi Prateek, you can set the accessTokenRequestHeaders property of the client class.

Something like:

var client = GitHubOAuth2Client(
    redirectUri: 'my.app://oauth2redirect',
    customUriScheme: 'my.app');

client.accessTokenRequestHeaders = {
  'MyHeaderName': 'MyHeaderValue'
};
Collapse
 
fortunegithub profile image
FortuneGithub • Edited

@enrico How Can i implement Oauth2 password grant authentication to my own clients?Please help.

Collapse
 
zulfadlifauzi profile image
Zulfadlifauzi

hi, love the oauth2 package. i have successfully login with oauth2_client but no redirect to app after login . been searching for quite time .

Collapse
 
yaredweldegebrialreda profile image
yared weldegebrial • Edited

Hey Enrico, thank you for your helpful Blog.
Is there a way to implement logout feature using this oauth2 client ?
Thank you.