GithubHelp home page GithubHelp logo

teranetsrl / oauth2_client Goto Github PK

View Code? Open in Web Editor NEW
92.0 6.0 114.0 269 KB

Simple Dart library for interacting with OAuth2 servers.

License: BSD 2-Clause "Simplified" License

Dart 100.00%
oauth2-client oauth oauth2

oauth2_client's Introduction

codecov

oauth2_client

Simple Flutter library for interacting with OAuth2 servers. It provides convenience classes for interacting with the "usual suspects" (Google, Facebook, LinkedIn, GitHub), but it's particularly suited for implementing clients for custom OAuth2 servers.

The library handles Authorization Code, Client Credentials and Implicit Grant flows.

Prerequisites

Android

On Android you must first set the minSdkVersion in the build.gradle file:

defaultConfig {
   ...
   minSdkVersion 18
   ...

If at all possible, when registering your application on the OAuth provider try not to use HTTPS as the scheme part of the redirect uri, because in that case your application won't intercept the server redirection, as it will be automatically handled by the system browser (at least on Android). Just use a custom scheme, such as "my.test.app" or any other scheme you want.

If the OAuth2 server allows only HTTPS uri schemes, refer to the FAQ section.

Again on Android, if your application uses the Authorization Code flow, you first need to modify the AndroidManifest.xml file adding the activity com.linusu.flutter_web_auth_2.CallbackActivity with the intent filter needed to open the browser window for the authorization workflow. The library relies on the flutter_web_auth_2 package to allow the Authorization Code flow.

AndroidManifest.xml

<activity android:name="com.linusu.flutter_web_auth_2.CallbackActivity" android:exported="true">
	<intent-filter android:label="flutter_web_auth_2">
		<action android:name="android.intent.action.VIEW" />
		<category android:name="android.intent.category.DEFAULT" />
		<category android:name="android.intent.category.BROWSABLE" />
		<data android:scheme="my.test.app" /> <!-- This must correspond to the custom scheme used for instantiatng the client... See below -->
	</intent-filter>
</activity>

iOS

On iOS you need to set the platform in the ios/Podfile file:

platform :ios, '11.0'

Web

Web support has been added in the 2.2.0 version, and should be considered preliminary.

On the web platform you must register your application using an HTTPS redirect uri.

When the authorization code flow is used, the authorization phase will be carried out by opening a popup window to the provider login page.

After the user grants access to your application, the server will redirect the browser to the redirect uri. This page should contain some javascript code to read the code parameter sent by the authorization server and pass it to the parent window through postMessage.

Something like:

window.onload = function() {
	const urlParams = new URLSearchParams(window.location.search);
	const code = urlParams.get('code');
	if(code) {
		window.opener.postMessage(window.location.href, _url_of_the_opener_window_);
	}
}

Please note that the browser can't securely store confidential information! The OAuth2Helper class, when used on the web, stores the tokens in the localStorage, and this means they won't be encrypted!

Installation

Add the library to your pubspec.yaml file:

dependencies:
	oauth2_client: ^3.0.0

Upgrading from previous versions (< 3.0.0)

Version 3.0.0 introduced some breaking changes that need to be addressed if you are upgrading from previous versions.

Please take note of the following:

  • From version 3.0.0, flutter_web_auth has been replaced by flutter_web_auth_2. Please refer to the upgrade instructions.

  • The migration to flutter_web_auth_2 marks the transition to Flutter 3. This means that you must upgrade to Flutter 3 (a simple flutter upgrade should be enough).

  • Version 3.0.0 migrates away from the pedantic package (that's now deprecated) to flutter_lints. This too entails some breaking changes. In particular, constants names are now in camelCase format for compliance with the style guides enforced by flutter_lints.

    These include:

# Before 3.0.0
OAuth2Helper.AUTHORIZATION_CODE
OAuth2Helper.CLIENT_CREDENTIALS
OAuth2Helper.IMPLICIT_GRANT

# After 3.0.0 become:
OAuth2Helper.authorizationCode
OAuth2Helper.clientCredentials
OAuth2Helper.implicitGrant

As well as the CredentialsLocation enum:

# Before 3.0.0
CredentialsLocation.HEADER
CredentialsLocation.BODY

# After 3.0.0 become:
CredentialsLocation.header
CredentialsLocation.body
  • The OAuth2Helper.setAuthorizationParams has been definitively removed, after being deprecated since version 2.0.0. You must use the OAuth2Helper constructor parameters instead.

  • The OAuth2Client.accessTokenRequestHeaders class field has been removed. You can now send custom headers by passing the accessTokenHeaders parameter to the getTokenWithAuthCodeFlow method, or the customHeaders parameter to the getTokenWithClientCredentialsFlow method. When using the helper, you can pass the custom parameters in the helper's constructor through the accessTokenHeaders parameter. For example:

OAuth2Client client = OAuth2Client(
  redirectUri: ...,
  customUriScheme: ...,
  accessTokenRequestHeaders: {
    'Accept': 'application/json'
  });

client.getTokenWithClientCredentialsFlow(clientId: ..., clientSecret: ..., customHeaders: {
    'MyCustomHeaderName': 'MyCustomHeaderValue'
});
//or...
client.getTokenWithAuthCodeFlow(clientId: ..., clientSecret: ..., accessTokenHeaders: {
    'MyCustomHeaderName': 'MyCustomHeaderValue'
});

//...or, if using the OAuth2Helper...
OAuth2Helper hlp = OAuth2Helper(client, {
	...,
	accessTokenHeaders: {
    	'MyCustomHeaderName': 'MyCustomHeaderValue'
	}
});

...

Usage with the helper class

The simplest way to use the library is through the OAuth2Helper class. This class transparently handles tokens request/refresh, as well as storing and caching them.

Besides, it implements convenience methods to transparently perform http requests adding the generated access tokens.

First, instantiate and setup the helper:

import 'package:oauth2_client/oauth2_helper.dart';
//We are going to use the google client for this example...
import 'package:oauth2_client/google_oauth2_client.dart';
import 'package:http/http.dart' as http;

//Instantiate an OAuth2Client...
GoogleOAuth2Client client = GoogleOAuth2Client(
	customUriScheme: 'my.test.app', //Must correspond to the AndroidManifest's "android:scheme" attribute
	redirectUri: 'my.test.app:/oauth2redirect', //Can be any URI, but the scheme part must correspond to the customeUriScheme
);

//Then, instantiate the helper passing the previously instantiated client
OAuth2Helper oauth2Helper = OAuth2Helper(client,
	grantType: OAuth2Helper.authorizationCode,
	clientId: 'your_client_id',
	clientSecret: 'your_client_secret',
	scopes: ['https://www.googleapis.com/auth/drive.readonly']);

In the example we used the Google client, but you can use any other provided client or implement your own (see below).

Note that the redirect uri has just one slash. This is required per Google specs. Other providers could require double slash!

Now you can use the helper class to perform HTTP requests to the server.

http.Response resp = helper.get('https://www.googleapis.com/drive/v3/files');

The helper will:

  • check if a token already exists in the secure storage
  • if it doesn't exist:
    • request the token using the flow and the parameters specified in the setAuthorizationParams call. For example, for the Authorization Code flow this involves opening a web browser for the authorization code and then requesting the actual access token. The token is then stored in secure storage.
  • if the token already exists, but is expired, a new one is automatically generated using the refresh_token flow. The token is then stored in secure storage.
  • Perform the actual http request with the access token included.

Usage without the helper class

You can use the library without the helper class, using one of the base client classes.

This way tokens won't be automatically stored, nor will be automatically refreshed. Furthermore, you will have to add the access token to http requests by yourself.

//Import the client you need (see later for available clients)...
import 'myclient.dart'; //Not an actual client!
import 'package:oauth2_client/access_token_response.dart';

...

//Instantiate the client
client = MyClient(...);

//Request a token using the Authorization Code flow...
AccessTokenResponse tknResp = await client.getTokenWithAuthCodeFlow(
	clientId: 'your_client_id',
	scopes: ['scope1', 'scope2', ...]
);

//Request a token using the Client Credentials flow...
AccessTokenResponse tknResp = await client.getTokenWithClientCredentialsFlow(
	clientId: 'XXX', //Your client id
	clientSecret: 'XXX', //Your client secret
	scopes: ['scope1', 'scope2', ...] //Optional
);

//Or, if you already have a token, check if it is expired and in case refresh it...
if(tknResp.isExpired()) {
	tknResp = client.refreshToken(tknResp.refreshToken);
}

Acessing custom/non standard response fields

You can access non standard fields in the response by calling the getRespField method.

For example:

AccessTokenResponse tknResp = await client.getTokenWithAuthCodeFlow(
	clientId: 'your_client_id',
	scopes: ['scope1', 'scope2', ...]
);

if(tknResp.isExpired()) {
	var myCustomFieldVal = tknResp.getRespField('my_custom_field');
}

Predefined clients

The library implements clients for the following services/organizations:

  • Google
  • Facebook
  • LinkedIn
  • GitHub
  • Shopify
  • Spotify
  • Twitter
  • Microsoft

Google client

In order to use this client you need to first configure OAuth2 credentials in the Google API console (https://console.developers.google.com/apis/).

First you need to create a new Project if it doesn't already exists, then you need to create the OAuth2 credentials ("OAuth Client ID").

Select iOS as Application Type, specify a name for the client and a Bundle ID. Now edit the just created Client ID and take note of the "IOS URL scheme". This should look something like com.googleusercontent.apps.XXX and is the custom scheme you'll need to use.

Then in your code:

import 'package:oauth2_client/google_oauth2_client.dart';

OAuth2Client googleClient = GoogleOAuth2Client(
	redirectUri: 'com.googleusercontent.apps.XXX:/oauth2redirect', //Just one slash, required by Google specs
	customUriScheme: 'com.googleusercontent.apps.XXX'
);

Then you can instantiate an helper class or directly use the client methods to acquire access tokens.

Facebook client

In order to use this client you need to first configure OAuth2 credentials in the Facebook dashboard.

Then in your code:

import 'package:oauth2_client/facebook_oauth2_client.dart';

OAuth2Client fbClient = FacebookOAuth2Client(
	redirectUri: 'my.test.app://oauth2redirect',
	customUriScheme: 'my.test.app'
);

Then you can instantiate an helper class or directly use the client methods to acquire access tokens.

LinkedIn client

In order to use this client you need to first configure OAuth2 credentials. See https://docs.microsoft.com/it-it/linkedin/shared/authentication/authorization-code-flow.

Then in your code:

import 'package:oauth2_client/linkedin_oauth2_client.dart';

OAuth2Client liClient = LinkedInOAuth2Client(
	redirectUri: 'my.test.app://oauth2redirect',
	customUriScheme: 'my.test.app'
);

Then you can instantiate an helper class or directly use the client methods to acquire access tokens.

GitHub client

In order to use this client you need to first create a new OAuth2 App in the GittHub Developer Settings (https://github.com/settings/developers)

Then in your code:

import 'package:oauth2_client/github_oauth2_client.dart';

OAuth2Client ghClient = GitHubOAuth2Client(
	redirectUri: 'my.test.app://oauth2redirect',
	customUriScheme: 'my.test.app'
);

Then you can instantiate an helper class or directly use the client methods to acquire access tokens.

Implementing your own client

Implementing your own client is quite simple, and often it requires only few lines of code.

In the majority of cases you only need to extend the base OAuth2Client class and configure the proper endpoints for the authorization and token url.

import 'package:oauth2_client/oauth2_client.dart';

class MyOAuth2Client extends OAuth2Client {
  MyOAuth2Client({required String redirectUri, required String customUriScheme}): super(
    authorizeUrl: 'https://...', //Your service's authorization url
    tokenUrl: 'https://...', //Your service access token url
    redirectUri: redirectUri,
    customUriScheme: customUriScheme
  );
}

Open ID support

Open ID Connect is currently in development. Stay tuned for updates!

FAQ

Tokens are not getting stored!

If when using the helper class the tokens seem to not getting stored, it could be that the requested scopes differs from those returned by the Authorization server.

OAuth2 specs state that the server could optionally return the granted scopes. The OAuth2Helper, when storing an access token, keeps track of the scopes it has been granted for, so the next time a token is needed for one or more of those scopes, it will be readily available without performing another authorization flow.

If the client requests an authorization grant for scopes "A" and "B", but the server for some reason returns a token valid for scope "A" only, that token will be stored along with scope "A", and not "B". This means that the next time the client will need a token for scopes "A" and "B", the helper will check its storage looking for a token for both "A" and "B", but will only find a token valid for "A", so it will start a new authorization process.

To verify that the requested scopes are really the ones granted on the server, you can use something like the following:

var client = OAuth2Client(
  authorizeUrl: <YOUR_AUTHORIZE_URL>,
  tokenUrl: <YOUR_TOKEN_URL>,
  redirectUri: <YOUR_REDIRECT_URI>,
  customUriScheme: <YOUR_CUSTOM_SCHEME>);

var tknResp = await client.getTokenWithAuthCodeFlow(
  clientId: <YOUR_CLIENT_ID>,
  scopes: [
	  <LIST_OF_SCOPES>
  ]);

print(tknResp.httpStatusCode);
print(tknResp.error);
print(tknResp.expirationDate);
print(tknResp.scope);

Apart from the order, the printed scopes should correspond exactly to the ones you requested.

I get an error PlatformException(CANCELED, User canceled login, null, null) on Android

Please make sure you modified the AndroidManifest.xml file adding the com.linusu.flutter_web_auth_2.CallbackActivity and the intent filter needed to open the browser window for the authorization workflow.

The AndroidManifest.xml file must contain the com.linusu.flutter_web_auth_2.CallbackActivity activity. Copy and paste the below code and CHANGE the value of android:scheme to match the scheme used in the redirect uri:

<activity android:name="com.linusu.flutter_web_auth_2.CallbackActivity" android:exported="true">
	<intent-filter android:label="flutter_web_auth_2">
		<action android:name="android.intent.action.VIEW" />
		<category android:name="android.intent.category.DEFAULT" />
		<category android:name="android.intent.category.BROWSABLE" />
		<data android:scheme="my.test.app" />
	</intent-filter>
</activity>

If you are sure your intent filter is set up correctly, maybe you have another one enabled that clashes with flutter_web_auth_2's (ThexXTURBOXx/flutter_web_auth_2#8)?

Can I use https instead of a custom scheme?

If you want to use an HTTPS url as the redirect uri, you must set it up as an App Link. First you need to specify both the android:host and android:pathPrefix attributes, as long as the android:autoVerify="true" attribute in the intent-filter tag inside the AndroidManifest.xml:

<activity android:name="com.linusu.flutter_web_auth_2.CallbackActivity">
	<intent-filter android:label="flutter_web_auth_2" android:autoVerify="true">
		<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="www.myapp.com"
				android:pathPrefix="/oauth2redirect" />
	</intent-filter>
</activity>

Then you need to prove ownership of the domain host by publishing a Digital Asset Links JSON file on your website. This involves generating an App signing key and signing your app with it.

oauth2_client's People

Contributors

abhishek-khanal avatar alensugimoto avatar bangfalse avatar basti394 avatar chrislaurie-io avatar domesticmouse avatar eggman87 avatar eraps7 avatar glacials avatar jakub-bacic avatar jheld avatar jon-salmon avatar kodingdev avatar lavalleeale avatar mauriciocartagena avatar nvx avatar okrad avatar piotrmitkowski avatar qasim90 avatar sbu-wbt avatar snailvanquisher avatar supermar1010 avatar teranetsrl avatar thefedex87 avatar thexxturboxx avatar xolf avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

oauth2_client's Issues

TokenStorage not working correctly

Hi

I've a problem with TokenStorage. I'm using OAuth2Helper.AUTHORIZATION_CODE with scope parameters ['api', 'openid', 'profile']. After login i'm trying to do a get. But the token is null from TokenStorage and i'm prompt to login again. There seems to be a problem with the scopeKey not being correctly defined when writing and getting tokens from TokenStorage.

Add a flag to disable PKCE when using OAuth2Helper

I am consuming an API that does not support PKCE and returns a 400 status code when trying to get the token using OAuth2Helper.

I see there is a param called enablePKCE to use when calling directly OAuth2Client.getTokenWithAuthCodeFlow, but I cannot find any way to disable PKCE when using OAuth2Helper.

Am I missing anything?

Close Custom Tabs on Android on Result

On Android, the Chrome / Browser Window stays open after the successfull redirect back to the app.

In some cases, when logging in again, the same instance gets opened, resulting in an unusable screen.

Is there an alternative to using custom tabs? Like webview i.e.?

Google Oauth2 - PlatformException(CANCELED, User canceled login, null, null)

Hi,

I'm using your library to retrieve a google oauth2 token with Authorization Code flow.
The process is ok on IOS but each time I call the API on Android, i get the following error:
PlatformException(CANCELED, User canceled login, null, null)

On the GCP, I create a OAuth 2.0 Client IDs for Android and get the following package name:
package_name

In my AndroidManifest.xml, I create a new Intent:
intent

And I create the Google client API as below:
googleoauth

I'm not sure to understand what I'm doing wrong.
Could you please help me to solve this problem?

Thanks.

Token is not getting saved in storage.

My code Implementation as per the given documentation

I tried to use the package to get data from Spotify API. I made a new client as shown below

class SpotifyOAuth2Client extends OAuth2Client {
  SpotifyOAuth2Client(
      {@required String redirectUri, @required String customUriScheme})
      : super(
            authorizeUrl:
                'https://accounts.spotify.com/authorize', 
            tokenUrl:
                'https://accounts.spotify.com/api/token', 
            redirectUri: redirectUri,
            customUriScheme: customUriScheme) {
    this.accessTokenRequestHeaders = {'Accept': 'application/json'};
  }
}

I used this code to get data using the helper

    SpotifyOAuth2Client client = SpotifyOAuth2Client(
        customUriScheme: 'mypakagename',
        redirectUri: 'mypackagename:/oauth2redirect');
    
    OAuth2Helper helper = OAuth2Helper(client,
        grantType: OAuth2Helper.AUTHORIZATION_CODE, clientId: _keys[0]);

The Problem

This code works perfectly, except this wont store tokens Everytime this code is run, it redirects me to get an accesstoken again. So I tried the code below.

My improvisation

OAuth2Helper auth2helper = OAuth2Helper(client);
    auth2helper.setAuthorizationParams(
        grantType: OAuth2Helper.AUTHORIZATION_CODE,
        clientId: _keys[0],
        clientSecret: _keys[1]);
    auth2helper.getToken();

Error found during the improvisation
When I execute this code I get the following error

Unhandled Exception: Exception: "state" parameter in response doesn't correspond to the expected value

Is there something wrong with the package or is my implementationwrong? Do help me out

Complete Error trace

This is the complete error trace. PS This code is in the file spotify_client.dart in the class SpotifyAPI

E/flutter (30049): [ERROR:flutter/lib/ui/ui_dart_state.cc(166)] Unhandled Exception: Exception: "state" parameter in response doesn't correspond to the expected value
E/flutter (30049): #0 new AuthorizationResponse.fromRedirectUri (package:oauth2_client/authorization_response.dart:31:9)
E/flutter (30049): #1 OAuth2Client.requestAuthorization (package:oauth2_client/oauth2_client.dart:148:34)
E/flutter (30049):
E/flutter (30049): #2 OAuth2Client.getTokenWithAuthCodeFlow (package:oauth2_client/oauth2_client.dart:77:26)
E/flutter (30049): #3 OAuth2Helper.fetchToken (package:oauth2_client/oauth2_helper.dart:100:30)
E/flutter (30049): #4 OAuth2Helper.getToken (package:oauth2_client/oauth2_helper.dart:78:23)
E/flutter (30049):
E/flutter (30049): #5 OAuth2Helper.get (package:oauth2_client/oauth2_helper.dart:212:25)
E/flutter (30049): #6 SpotifyAPI.getData (package:musicplayer/utilities/spotify_client.dart:51:43)
E/flutter (30049): #7 _rootRunUnary (dart:async/zone.dart:1198:47)
E/flutter (30049): #8 _CustomZone.runUnary (dart:async/zone.dart:1100:19)
E/flutter (30049): #9 _FutureListener.handleValue (dart:async/future_impl.dart:143:18)
E/flutter (30049): #10 Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:696:45)
E/flutter (30049): #11 Future._propagateToListeners (dart:async/future_impl.dart:725:32)
E/flutter (30049): #12 Future._completeWithValue (dart:async/future_impl.dart:529:5)
E/flutter (30049): #13 _AsyncAwaitCompleter.complete (dart:async-patch/async_patch.dart:40:15)
E/flutter (30049): #14 _completeOnAsyncReturn (dart:async-patch/async_patch.dart:311:13)
E/flutter (30049): #15 kGetKeys (package:musicplayer/utilities/constants.dart)
E/flutter (30049): #16 _rootRunUnary (dart:async/zone.dart:1198:47)
E/flutter (30049): #17 _CustomZone.runUnary (dart:async/zone.dart:1100:19)
E/flutter (30049): #18 _FutureListener.handleValue (dart:async/future_impl.dart:143:18)
E/flutter (30049): #19 Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:696:45)
E/flutter (30049): #20 Future._propagateToListeners (dart:async/future_impl.dart:725:32)
E/flutter (30049): #21 Future._completeWithValue (dart:async/future_impl.dart:529:5)
E/flutter (30049): #22 _AsyncAwaitCompleter.complete (dart:async-patch/async_patch.dart:40:15)
E/flutter (30049): #23 _completeOnAsyncReturn (dart:async-patch/async_patch.dart:311:13)
E/flutter (30049): #24 AssetBundle.loadString (package:flutter/src/services/asset_bundle.dart)
E/flutter (30049): #25 _rootRunUnary (dart:async/zone.dart:1198:47)
E/flutter (30049): #26 _CustomZone.runUnary (dart:async/zone.dart:1100:19)
E/flutter (30049): #27 _FutureListener.handleValue (dart:async/future_impl.dart:143:18)
E/flutter (30049): #28 Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:696:45)
E/flutter (30049): #29 Future._propagateToListeners (dart:async/future_impl.dart:725:32)
E/flutter (30049): #30 Future._completeWithValue (dart:async/future_impl.dart:529:5)
E/flutter (30049): #31 _AsyncAwaitCompleter.complete (dart:async-patch/async_patch.dart:40:15)
E/flutter (30049): #32 _completeOnAsyncReturn (dart:async-patch/async_patch.dart:311:13)
E/flutter (30049): #33 PlatformAssetBundle.load (package:flutter/src/services/asset_bundle.dart)
E/flutter (30049): #34 _rootRunUnary (dart:async/zone.dart:1198:47)
E/flutter (30049): #35 _CustomZone.runUnary (dart:async/zone.dart:1100:19)
E/flutter (30049): #36 _FutureListener.handleValue (dart:async/future_impl.dart:143:18)
E/flutter (30049): #37 Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:696:45)
E/flutter (30049): #38 Future._propagateToListeners (dart:async/future_impl.dart:725:32)
E/flutter (30049): #39 Future._completeWithValue (dart:async/future_impl.dart:529:5)
E/flutter (30049): #40 Future._asyncCompleteWithValue. (dart:async/future_impl.dart:567:7)
E/flutter (30049): #41 _rootRun (dart:async/zone.dart:1190:13)
E/flutter (30049): #42 _CustomZone.run (dart:async/zone.dart:1093:19)
E/flutter (30049): #43 _CustomZone.runGuarded (dart:async/zone.dart:997:7)
E/flutter (30049): #44 _CustomZone.bindCallbackGuarded. (dart:async/zone.dart:1037:23)
E/flutter (30049): #45 _microtaskLoop (dart:async/schedule_microtask.dart:41:21)
E/flutter (30049): #46 _startMicrotaskLoop (dart:async/schedule_microtask.dart:50:5)

Web view opens for every call

Every call I make with a get is showing the Chrome window briefly while the request is processing.

Is this normal behaviour? If a token is passed, should it still launch the Chrome window?

I'm launching my custom client with the following function:

OAuth2Helper doauth({String authUrl, String tokenUrl}) {
        MyOAuth2Client client = MyOAuth2Client(
        // redirectUri: 'fhir_cerner/oauth2redirect',
        // redirectUri: '',
        // redirectUri: 'http://fhircerner/oauth2redirect',
        customUriScheme: 'fhircerner',
        authUrl: authUrl,
        tokenUrl: tokenUrl);

    OAuth2Helper oauth2Helper = OAuth2Helper(client,
        grantType: OAuth2Helper.AUTHORIZATION_CODE,
        clientId: 'xxx',
        scopes: ['user/Observation.read user/MedicationHistory.read user/Patient.read', 
        '&aud=${Settings().baseUrl}'
        ]);
    return oauth2Helper;
  }

And making calls with:

      http.Response result =
          await meta.oAuth2Helper.get(url, headers: Settings().headers);

OAuth2Helper does not provide PUT and PATCH requests

Issue

Currently, the OAuth helper does not provide PUT and PATCH requests. I think it would be nice to add support for those (and possibly also for OPTIONS, HEAD and TRACE).

Solution

This would be a very simple non-breaking change, as the post() method could basically be copied for PUT and PATCH. I currently extend the helper by only adding put() and patch() that are basically identical to post(), but I think it would be better to include those in the OAuth2Helper.

Any thoughts or am I overlooking a reason not to add this? Otherwise I'll make a PR soon.

Auth0

Hi there, firstly: this package looks really cool, excited to try it out. Secondly: do you have Auth0 on the roadmap? Or would it be better to wait for their dedicated SDK? (they haven't announced it yet but the rumors are strong).

After successful Login, App gets nothing.

W/ActivityThread(16136): handleWindowVisibility: no activity for token android.os.BinderProxy@3d68cc0
The above is the only I can see in the run console. How to fix this.

Does not redirect

Using the code provided in the example with the Google App, it does open the browser and exchange the code, but instead of returning to the app once the permission is provided, it just shows the token in the browser and asks that you copy/paste it back into your app.
screenshot

Oauth2 Refresh tokens

Hi ,
Once I have created my custom OAuth2Client class as shown

import 'package:oauth2_client/oauth2_client.dart';
import 'package:meta/meta.dart';

class CustomClient extends OAuth2Client {
  MyOAuth2Client({@required String redirectUri, @required String customUriScheme}): super(
    authorizeUrl: 'https://...', //Your service's authorization url
    tokenUrl: 'https://...', //Your service access token url
    redirectUri: redirectUri,
    customUriScheme: customUriScheme
  );
}

Can you tell me how do I Check if my token is expired or not?
I fetch the token using

`OAuth2Client client = CustomClient(
      redirectUri: URL',
      customUriScheme: 'com.example.Companyname');

  OAuth2Helper oauth2Helper = OAuth2Helper(client,
      grantType:
          OAuth2Helper.CLIENT_CREDENTIALS, //default value, can be omitted
      clientId: '',
      clientSecret: '',
      scopes: ['']);            //       Assume these values to be filled...

  oauth2Helper.fetchToken().then((val) {
    print(val); // My token 
  });`

My approach would be to check for 401 response from the API I am calling
Is there any predefined method that I can implement?

[Question] Need help with GitHubOAuth2Client

Hi, this is more of a help question rather than an issue. I didn't know where to ask.Sorry for that...

I am completely new to this OAuth world. I tried to implement GitHub OAuth flow as described in Readme of this repo.

I also created an OAuth App at github.com/settings/developers. But, I am not sure what to put in 'Authorisation Callback URL'. Do I need to put a valid website URL? If yes, I tried putting example.com. And when I log-in, it just redirects to the example.com.

My question is, how to get the control back to our app when user logs-in to GitHub?

If I have to put a valid website URL in that (Authorisation callback URL), then what response do I need to send from my website URL, so that it gets back to my app??

Thanks.

Token not getting stored

Hi @okrad,

Thanks for the wonderful library. It has significantly simplified my implementation of Microsoft authentication integration.

I have a similar problem to the one mentioned at #27.

this._oauth2Client = OAuth2Client( authorizeUrl: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize", tokenUrl: "https://login.microsoftonline.com/common/oauth2/v2.0/token", redirectUri: "msauth.me.test.appv1://auth", customUriScheme: "msauth.me.test.appv1"); this._oauth2Helper = OAuth2Helper( this._oauth2Client, grantType: OAuth2Helper.AUTHORIZATION_CODE, clientId: '<client id>', scopes: [ 'https://graph.microsoft.com/Calendars.Read', 'https://graph.microsoft.com/Calendars.Read.Shared', 'https://graph.microsoft.com/email', 'https://graph.microsoft.com/Mail.ReadBasic', 'https://graph.microsoft.com/profile', 'https://graph.microsoft.com/User.Read' ], );

Every time i invoke a "helper.get", it redirects to oauth popup asking to "sign in" again.
http.Response resp = await this._oauth2Helper.get("https://graph.microsoft.com/v1.0/me"); Map<String, dynamic> user = convert.jsonDecode(resp.body); print(user);

I tried debugging by invoking "getToken" even that triggers an oauth popup - which leads me to believe that it is a token not being stored problem.

Looking forward for any suggestions to solve it on my end

Thanks
Manju

Android build fails

Ran flutter doctor. No issues found,
Also did flutter clean.
I am not sure why I am seeing this only for flutter_web_auth.


* What went wrong:
A problem occurred configuring project ':flutter_web_auth'.
> Could not resolve all artifacts for configuration ':flutter_web_auth:classpath'.
   > Could not resolve com.android.tools.build:gradle:3.6.3.
     Required by:
         project :flutter_web_auth
      > Could not resolve com.android.tools.build:gradle:3.6.3.
         > Could not get resource 'https://dl.google.com/dl/android/maven2/com/android/tools/build/gradle/3.6.3/gradle-3.6.3.pom'.
            > Could not GET 'https://dl.google.com/dl/android/maven2/com/android/tools/build/gradle/3.6.3/gradle-3.6.3.pom'.
               > sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
> Could not get unknown property 'android' for project ':flutter_web_auth' of type org.gradle.api.Project.

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

Not able to redirect to app after successful auth verification

Library version: 1.4.6
Android version: 9 and 10 (Phone and Emulator)
I have added the following intent in my App.Manifest file

<activity
    android:name="com.linusu.flutter_web_auth.CallbackActivity">
    <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="https"
            android:host="loginapp.flutterapp.com"
            android:pathPrefix="/oauth2redirect"/>
    </intent-filter>
</activity>

Also tried with:

<activity
    android:name="com.linusu.flutter_web_auth.CallbackActivity">
    <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="http" />
    </intent-filter>
</activity>

Request code:

Future<void> myOauth2Method() async {
    MyOAuth2Client client = MyOAuth2Client(
      customUriScheme: 'https',
      redirectUri: 'https://loginapp.flutterapp.com/oauth2redirect',
    );

   OAuth2Helper helper = OAuth2Helper(client,
        grantType: OAuth2Helper.AUTHORIZATION_CODE,
        clientId: '*************',
        clientSecret: '************',
        scopes: ['com.intuit.quickbooks.accounting openid email profile'],
        );

     var token = await helper.getToken();

    print('Token: ${token.accessToken}');
  }

After successful authorization the redirect uri is : https://loginapp.flutterapp.com/oauth2redirect?code=*******
But, instead of launching my app it is trying to open the website with that url.

Screenshot_1599236076

Should I be able to use this with Azure AD B2C?

Hi,

Thanks for this lib. It looks, in theory, like I should be able to use this with Azure. But I'm having trouble getting it to cooperate. Even though I can successfully authenticate, the redirect causes a 404 rather than the callback scheme being recognized and causing the resumption of the sign-in process.

Let me state up-front that one possible factor in this issue is that the Azure portal requires a leading https:// on any redirect URLs you enter. Therefore, I have to enter https://myapp.azurewebsites.net/:/oauth2redirect rather than myapp.azurewebsites.net/:/oauth2redirect.

The 404 looks like this:

image

(I'm referring to the blurred component of the URIs as myapp throughout this issue)

I have tried all these things as values for the android:scheme:

  • myapp.azurewebsites.net
  • https://myapp.azurewebsites.net
  • https://myapp.azurewebsites.net/

Nothing seems to pick up the redirect when using the following:

customUriScheme: 'https://myapp.azurewebsites.net/',
redirectUri: 'https://myapp.azurewebsites.net/:/oauth2redirect',

The same thing happens if I change the custom/redirect URIs to this (so that the customUriScheme exactly matches what's in the manifest):

customUriScheme: 'myapp.azurewebsites.net',
redirectUri: 'https://myapp.azurewebsites.net/:/oauth2redirect',

Is there something I'm doing wrong here?

Endpoint with null scope in response can produce NPE

This is probably just an implementation issue with the endpoint I am using (imgur.com). This oauth endpoint does not use scopes so I do not provide any scopes on my request. The token response has null in the response map returned for scopes though. This causes a NPE in the response handling code as it calls isEmpty on the scope value.

Should be an easy fix. Will throw up a PR.

Screen Shot 2021-01-17 at 11 19 45 AM

image

NoSuchMethodError: The method 'cast' was called on null

Hi all.

I just update the lib oauth2_client 1.3.0 to 1.7.1 but now my openid connection failed.

Here is the error message that I have :


[VERBOSE-2:ui_dart_state.cc(186)] Unhandled Exception: NoSuchMethodError: The method 'cast' was called on null.
Receiver: null
Tried calling: cast<String>()
#0      Object.noSuchMethod (dart:core-patch/object_patch.dart:54:5)
#1      TokenStorage.getToken.<anonymous closure> (package:oauth2_client/src/token_storage.dart:36:59)
#2      Iterable.firstWhere (dart:core/iterable.dart:513:15)
#3      TokenStorage.getToken (package:oauth2_client/src/token_storage.dart:28:40)
<asynchronous suspension>
#4      OAuth2Helper.getTokenFromStorage (package:oauth2_client/oauth2_helper.dart:105:12)
<asynchronous suspension>
#5      OAuth2Helper.getToken (package:oauth2_client/oauth2_helper.dart:80:19)
<asynchronous suspension>
#6      OAuth2Helper._request (package:oauth2_client/oauth2_helper.dart:287:19)
<asynchronous suspension>
#7      _MyHomePageState.apiUserInfo (package:myproject_mobile/main.dart:358:34)
<asynchronous suspension>

If someone can help me to fix this, it will be awesome.

Thx a lot.

Cannot sign out from session

I'm calling the /logout endpoint on my Keycloak Auth server but the session isn't logging out. Is there a way to force end the session when using Authorization Code flow?

When I use the Auth Code flow it will open the browser to login with user name and password. Once i have done that i call /logout. I get 200 back. I also delete the token. When i login again using the/auth url it doesn't prompt for login and automatically recognises the session and closes the embedded browser + receive a new access token. I need to be able to logout properly.

I'm able to logout from the session manually if i set the redirect URL to the account page or the logout link. It seems like the session is stored in the embedded browser.

FYI im using the OAuthHelper for all the http requests

AccessToken does not exist

In the Usage without the helper class section import 'package:oauth2_client/access_token.dart'; doesn't exist.

tokenType is null and token is not saved to storage

Hi there,

great library! I'm running into a few issues while using it to communicate with Slack's API.
While working with the helper class I've noticed that the tokenType is not set to bearer and instead is set to null.

Steps which might be useful to reproduce:

  1. Own a Slack workspace
  2. Create your own Slack app
  3. Set its scope to users.profile:write
  4. Add a redirect URL and configure it according to your OS in the corresponding file

I'm using it like this:

class TelestoOAuth2Client extends OAuth2Client {
  TelestoOAuth2Client(
      {@required String redirectUri, @required String customUriScheme})
      : super(
          authorizeUrl: 'https://slack.com/oauth/authorize',
          tokenUrl: 'https://slack.com/api/oauth.access',
          customUriScheme: 'slack.status.app',
          redirectUri: 'slack.status.app://oauth2redirect',
        );
}

OAuth2Client telestoClient = TelestoOAuth2Client(
    customUriScheme: 'slack.status.app',
    redirectUri: 'slack.status.app://oauth2redirect');

OAuth2Helper oauth2Helper = OAuth2Helper(telestoClient,
    grantType: OAuth2Helper.AUTHORIZATION_CODE,
    clientId: 'secret',
    clientSecret: 'secret',
    scopes: ['users.profile:write']); 

The method itself:

Future<http.Response> setSlackStatus(String statusText, String statusEmoji,
    // AccessTokenResponse token,
    {int statusExpiration = 0}) async {
  // var tokenString = token.accessToken;

  final res = await oauth2Helper.post(
      Uri.encodeFull('https://slack.com/api/users.profile.set'),
      headers: {
        'Content-Type': 'application/json; charset=UTF-8',
        // 'Authorization': 'Bearer $tokenString',
      },
      body: jsonEncode({
        'profile': {
          'status_text': statusText,
          'status_emoji': ':$statusEmoji:',
          'status_expiration': statusExpiration,
        }
      }));

  print(res.statusCode);

  return res;
}

As you can see I've commented out some lines, with these lines it's working fine but that's the task of the helper class to manage the token.

When calling the method like this:

main() async {
  WidgetsFlutterBinding.ensureInitialized();
  // AccessTokenResponse token = await oauth2Helper.fetchToken();
  // http.Response res = await setSlackStatus('WE', 'cat', token);

  http.Response res = await setSlackStatus('WE', 'cat');

  print(res.body);
  print(res.headers);
  print(res.statusCode);

  print('======');

  http.Response res2 = await setSlackStatus('WE', 'dog');
  print(res2.body);
  print(res2.headers);
  print(res2.statusCode);
}

I will get asked twice to accept the API request. Also when I manipulate oauth2_helper.dart to set tknResp.tokenType to bearer at least the request itself works. Is there a way to have a option which sets the token to be a bearer type?

Also do you have any ideas why it doesn't store the token in the storage?

[BUG] With some OAuth providers, the OAuth call will be made on each requests

Hi there!

With my OAuth2 provider, I retrieve the following token :

{"public":{"http_status_code":200,"access_token":"token","token_type":"Bearer","refresh_token":"token","scope":["public"],"expires_in":7889237,"expiration_date":1608174048837,"error":null,"errorDescription":null,"errorUri":null}}

This doesn't work with oauth2_client, as in token_storage.dart , line 23, we're checking if the token response contains scope at its top, and retrieve the token from the inside of this scope.

This result in authentication working, but the token not being store in secure storage, so the call to the OAuth client is made on each call.

Migrate to null safety

This is a request to add support for null safety .
We depend on your awesome package, so would be great to have null safety enabled.

The Dart/Flutter team already encourages publishing the migrated packages: See this blog post.

See the migration guide for details about enabling null safety.

web support?

Hi, is there going to be web support in the future?

HTTP Error 500.35-ANCM

Hi ,
So I am using your plugin for a flutter application.
This is my version oauth2_client: ^1.2.2

I call my API's by building a custom oauth2Class similar to this code here:
class MyOAuth2Client extends OAuth2Client { MyOAuth2Client({@required String redirectUri, @required String customUriScheme}): super( authorizeUrl: 'https://...', //Your service's authorization url tokenUrl: 'https://...', //Your service access token url redirectUri: redirectUri, customUriScheme: customUriScheme ); }
But the apis function properly for sometime but later give an error: HTTP Error 500.35 ANCM Multiple In process Applications in same process. Till where I understand this could be a server side error.

But my API's and server side codes are correct. Is there anything in the oauth2_client that can cause this? something related to refreshing the tokens?

This API problem get fixed on uninstalling the app and reinstalling it. I highly doubt it can be a server side error .

Could you please tell me any potential causes/bugs that I might have to consider while working with oauth2_client ?

Unhandled Exception: Exception: "state" parameter in response doesn't correspond to the expected value

I am using the OAuth2Helper class which I believe has been working until recently. However when I recently deleted the app from a device and reinstalled, I am receiving the error:

Unhandled Exception: Exception: "state" parameter in response doesn't correspond to the expected value

The error seems to be coming from authorization_response. I did some debugging and found that the state variable and checkState variable are both different.

Why would these variables become out of sync and how do I manually "logout" or reset these variables so I get a proper Oauth2 response again?

Docs unclear for https redirects

Sorry, but I could not find anywhere in the docs, how to handle https redirects.

Actually, my OAuth2 server requires the redirect URI to be an https one and here, the example only shows how to handle custom URI schemes. Like myapp, or com.teranet.app.

Which values should be put in customUriScheme and redirectUri for this use case??

Exception thrown when scope not present in AccessTokenResponse.

Hey!

We've just started with implementing OAuth2 into our app using this package, thanks for building it!

I've found an issue of which I'm unsure if our own OAuth2 IDP is out of spec, or if it's just a small bug. Hope you can advise me on this. We are using the latest version.

When authenticating against our own OAuth2 IDP, when we're redirected back to the app, we encountered the following exception:

E/flutter (19580): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: NoSuchMethodError: The getter 'isNotEmpty' was called on null.
E/flutter (19580): Receiver: null
E/flutter (19580): Tried calling: isNotEmpty
E/flutter (19580): #0      Object.noSuchMethod (dart:core-patch/object_patch.dart:53:5)
E/flutter (19580): #1      TokenStorage.getScopeKey (package:oauth2_client/src/token_storage.dart:72:15)
E/flutter (19580): #2      TokenStorage.insertToken (package:oauth2_client/src/token_storage.dart:40:29)
E/flutter (19580): <asynchronous suspension>
E/flutter (19580): #3      TokenStorage.addToken (package:oauth2_client/src/token_storage.dart:34:37)
E/flutter (19580): #4      OAuth2Helper.fetchToken (package:oauth2_client/oauth2_helper.dart:91:26)
E/flutter (19580): <asynchronous suspension>
E/flutter (19580): #5      OAuth2Helper.getToken (package:oauth2_client/oauth2_helper.dart:66:23)
E/flutter (19580): <asynchronous suspension>
E/flutter (19580): #6      OAuth2Helper.get (package:oauth2_client/oauth2_helper.dart:164:41)
E/flutter (19580): #7      requestApiCallResult (package:ankev928/shared/api_call.dart:22:43)
E/flutter (19580): #8      _LoginHandlerState.login (package:ankev928/pages/login.dart:52:43)
E/flutter (19580): #9      _LoginHandlerState.initState.<anonymous closure> (package:ankev928/pages/login.dart:26:57)
E/flutter (19580): #10     SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1113:15)
E/flutter (19580): #11     SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1060:9)
E/flutter (19580): #12     SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:968:5)
E/flutter (19580): #13     _rootRun (dart:async/zone.dart:1184:13)
E/flutter (19580): #14     _CustomZone.run (dart:async/zone.dart:1077:19)
E/flutter (19580): #15     _CustomZone.runGuarded (dart:async/zone.dart:979:7)
E/flutter (19580): #16     _invoke (dart:ui/hooks.dart:261:10)
E/flutter (19580): #17     _drawFrame (dart:ui/hooks.dart:219:3)
E/flutter (19580):

In token_storage.dart I placed a breakpoint on the contents of tknResp, and found that the scope is null:

image

I'm thinking our OAuth2 IDP is not returning any scope since we are not using scopes within our server application. We're initialising the OAuth2 client with the scopes: ['*'].

I am able to work around the issue by changing token_storage.dart as follows:

Future<void> addToken(AccessTokenResponse tknResp) async {
    tknResp.scope = ['*'];
    Map<String, Map> tokens = await insertToken(tknResp);
    await storage.write(key, jsonEncode(tokens));
}

So the question is, is our IDP out of spec (and should it return scope * when we're not explicitly working with scopes), or should the package just assume that if no scope is returned, the default is *? I'm not familiar enough with the OAuth2 spec to answer this question, but since we haven't run into this issue with an iOS app someone else is developing on our IDP, I've went ahead and opened an issue here.

Looking forward to a reply. :) Thanks!

Using this with Facebook web flow login

Hi there - thanks a bunch for this plugin. I have an issue with respect to my redirect URI and not getting passed back to my Flutter app. I'm guessing I've done something silly and wanted to ask for your guidance.

I'm trying to use this plugin to provide Facebook authentication with Firebase - and I was expecting the plugin to initiate the full login flow, return a token that I can use with FirebaseAuth and then validate against firebase.

I use the following simple code to start the Facebook login flow :

try {
     FacebookOAuth2Client client = FacebookOAuth2Client(
         redirectUri: 'https://myapp.firebaseapp.com/__/auth/handler',         
         customUriScheme: "https");

     OAuth2Helper oauth2Helper = OAuth2Helper(client,
         grantType: OAuth2Helper.AUTHORIZATION_CODE,
         clientId: 'my facebook app id',
         scopes: ['email']);

     var t = await oauth2Helper.fetchToken();
   } catch (e) {
     print(e.toString());
   }

This appears to work and asks firstly for permission :

image

and then when I grant will show me the Facebook page - since I have already logged in , this page is shown on my device (Simulator) :

image

The issue I have now is that when I tap continue control does not return back to my app so that I can process the token .

I just get this screen on my device/simulator being shown :

image

And unless I cancel, control does not go back to my app.

Is there something basic I'm missing here with regards to using your plugin properly with the firebase callback and customUriScheme or have I misunderstood the correct usage of FacebookOAuthClient, OAuthHelper and fetchToken ?

Implement getting id_token and id_token_hint

Hi,

Is Implementing also getting the id_token and id_token_hint planned for next releases?

while debugging I did see the id_token but it was the same as the access_token, what I understand is that it's not supposed to be the same.

Thanks.

Never return from browser

I have built a simple Flutter application with a login screen what contains a "Sign in with Google" button.

This button call your sample code:

GoogleOAuth2Client client = GoogleOAuth2Client(
                                    customUriScheme: "http",
                                    redirectUri: "http://localhost");

 OAuth2Helper oauth2Helper = OAuth2Helper(client,
                                    grantType: OAuth2Helper.AUTHORIZATION_CODE,
                                    clientId:
                                        '{client_id}',
                                    scopes: [
                                      'https://www.googleapis.com/auth/drive.readonly'
                                    ]);

                                var resp = await oauth2Helper.get(
                                    'https://www.googleapis.com/drive/v3/files');

And add intent-filter next to an exist tag in the 'main/Androidmanifest.xml'

<activity android:name="com.linusu.flutter_web_auth.CallbackActivity" >
          <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="http" android:host="localhost"/>
          </intent-filter>
        </activity> 

I tried without android:host or with my.test.app on the scheme, but it seems like my app not see my intent-filter...
When I used your sample (with my.test.app) i always get "custom scheme cannot have authority" error in the google.
So i can only using localhost for success login, but google always redirect to my "localhost" and response never come back to my app.

(the redirectUri is not exist. Just a fake address what google api can accept, so when redirect i get a "site can't be reached" error)

In Google Api Console i created an Android Credential, so i don't have secret key only a client_id.

Can you describe this package's operation?

Name of scheme on the intent-filter's data tag is a fake name? Or an exist server somewhere?
RedirectUri is a fake site and intent handle it??

In my opinion the operation this:
Click on "Sign in with Google" button in the Android app
Browser open
Login with google account
Google redirect
Andoid app see the intent and close browser
Android app get response.

Thank you

Optional state parameter

I understand that the state parameter is recommended and is good for security, but I have come across certain APIs that don't expect the state parameter, so they don't return it on the redirect, causing the auth flow to break. Would you be open to having it be optional, similar to enablePKCE? I'd be happy to make the changes and open a pull request, as I'm already using a local modified copy with these changes.

is there an example of this working with AAD

I can get Azure Active Directory to accept my request and send me a token, but it always responds with the default audience, the Graph API (aud: 00000002-0000-0000-c000-000000000000). I am unable to get it to give me a token for other resources, APIs I expose on other apps. Is this currently possible? If so, any chance of an example?

Thanks

Ivan

Error on redirect

I created a custom Oauth2 client just like you suggested in your article https://dev.to/okrad/oauth2client-implement-oauth2-clients-with-flutter-4jjl . Once the app redirects to authorization page and the user authorizes the client it redirects back to the app with an error. This is the error I am getting

E/flutter ( 3753): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: PlatformException(CANCELED, User canceled login, null)
E/flutter ( 3753): #0      StandardMethodCodec.decodeEnvelope 
package:flutter//services/message_codecs.dart:569
E/flutter ( 3753): #1      MethodChannel._invokeMethod 
package:flutter//services/platform_channel.dart:156
E/flutter ( 3753): <asynchronous suspension>
E/flutter ( 3753): #2      MethodChannel.invokeMethod 
package:flutter//services/platform_channel.dart:329
E/flutter ( 3753): #3      FlutterWebAuth.authenticate 
package:flutter_web_auth/flutter_web_auth.dart:35
E/flutter ( 3753): #4      WebAuth.authenticate 
package:oauth2_client/src/web_auth.dart:7
E/flutter ( 3753): #5      OAuth2Client.requestAuthorization 
package:oauth2_client/oauth2_client.dart:135
E/flutter ( 3753): #6      OAuth2Client.getTokenWithAuthCodeFlow 
package:oauth2_client/oauth2_client.dart:73
E/flutter ( 3753): #7      _HomeScreenState.authorize 
package:vend_mobile//screens/home_screen.dart:49
E/flutter ( 3753): #8      _HomeScreenState.build.<anonymous closure> 
package:vend_mobile//screens/home_screen.dart:81
E/flutter ( 3753): #9      _InkResponseState._handleTap 
package:flutter//material/ink_well.dart:779
E/flutter ( 3753): #10     _InkResponseState.build.<anonymous closure> 
package:flutter//material/ink_well.dart:862
E/flutter ( 3753): #11     GestureRecognizer.invokeCallback 
package:flutter//gestures/recognizer.dart:182
E/flutter ( 3753): #12     TapGestureRecognizer.handleTapUp 
package:flutter//gestures/tap.dart:504
E/flutter ( 3753): #13     BaseTapGestureRecognizer._checkUp 
package:flutter//gestures/tap.dart:282
E/flutter ( 3753): #14     BaseTapGestureRecognizer.handlePrimaryPointer 
package:flutter//gestures/tap.dart:217
E/flutter ( 3753): #15     PrimaryPointerGestureRecognizer.handleEvent 

This is the custom client

class MyCustomOauthClient extends OAuth2Client {
  MyCustomOauthClient(
      {@required String redirectUri, @required String customUriScheme})
      : super(
            authorizeUrl: 'AuthorizeURL Ednpoint',
            tokenUrl: 'Token URL Endpoint',
            redirectUri: redirectUri,
            customUriScheme: customUriScheme) {
    this.accessTokenRequestHeaders = {'Accept': 'application/json'};
  }
}

The success response form the API is '{redirect_uri}?code={code}&domain_prefix={domain_prefix}&state={state}'. I am not sure where this should go. Is it supposed to be in the accessTokenRequestHeaders?. Also the tokenendpoint has an element from the authorization code{domain_prefix} how can I implement that here?

I have been tryting to get this work for a week now and I haven't figuered it yet. Thank you in advance

Custom claims

Hi,

Is this library intended to allow access to custom claims? From what I can tell, any custom claims are discarded because AccessTokenResponse just ignores anything it doesn't understand.

How to logout?

How to logout? It seems no that method in OAuth2Helper.

Access idToken

Hey,
I need to access idToken rather than accessToken. How to handle it?

Add client_id to refreshToken Request

Hey, can you add a client_id parameter to the refreshToken method or a parameter List that can extend the body? I get an error form our AuthProvider. Thanks.

Google doesn't return refresh_token after first expire

Future<Map<String, Map>> insertToken(AccessTokenResponse tknResp) async {
final serTokens = await storage.read(key);
final scopeKey = getScopeKey(tknResp.scope);
var tokens = <String, Map>{};
if (serTokens != null) {
tokens = Map.from(jsonDecode(serTokens));
}
tokens[scopeKey] = tknResp.toMap();
return tokens;
}

Steps:

  1. With Google OAuth 2.0 Mobile
      final client = GoogleOAuth2Client(
        customUriScheme: 'my.app',
        redirectUri: 'my.app:/',
      );
      final oauth2Helper = OAuth2Helper(
        client,
        grantType: OAuth2Helper.AUTHORIZATION_CODE,
        clientId:
            '<mytoken>.apps.googleusercontent.com',
        scopes: ['https://www.googleapis.com/auth/userinfo.profile'],
      );
  1. Try to get resource
  2. Complete auth flow
  3. refresh_token is returned from Google api, and access_token is correctly used
  4. Token expired, e.g. tknResp.expiresIn = 0, tknResp.expirationDate = DateTime(2020)
  5. Try to get resource again
  6. refresh_token is used to get new token
  7. Google returns new access_token but without refresh_token
  8. refresh_token: null is written to store
  9. Token expired again
  10. Try to get resource
  11. Error happens because here https://github.com/teranetsrl/oauth2_client/blob/master/lib/oauth2_client.dart#L170 refreshToken is null

So the possible solution is to use refresh_token from previous token is there is no refresh_token in new one.

Android is returning a token even when tokenStorage is not being used

on iOS there is no issue. However on Android - after I authenticate successfully via the web popup -> the next time I restart the app and call:

var oauth2Helper = OAuth2Helper(client, grantType: 4)

the OAuth2Helper returns the token used in that first call above and refuses to display the web popup (via FlutterWebAuth). This should NOT be happening as I understand it. Rather it should only do so when the tokenStorage property has been set. Correct ?

iOS App crashes

Hi,
I am using AUTHORIZATION_CODE grantType and just started using this library. My iOS app crashes when I call helper.get().
I am using v1.1.2

IOS: Reading token from Storage fails when invoked from a background task

Hi,

The code AccessTokenResponse tknResp = await this._oAuth2Helper.getTokenFromStorage(); will always return null when invoked from a background task [my iPhone is/was locked].

To Isolate the issue, I created a "MemoryStorage':
`class MemoryStorage extends Storage {
@OverRide
Future read(String key) async {
return {get the key from a map};
}

@OverRide
Future write(String key, String value) async {
{store the key in a map}
}
}`

Use MemoryStorage as TokenStorage in Helper:
this._oAuth2Helper = OAuth2Helper( this._oauth2Client, clientId: "your client id", grantType: OAuth2Helper.AUTHORIZATION_CODE, scopes: [ "your scopes" ], tokenStorage: TokenStorage( this._oauth2Client.tokenUrl, storage: MemoryStorage(), ), );

With this switch in TokenStorage provider (from SecureStorage to MemoryStorage), the issue is resolved. The code The code AccessTokenResponse tknResp = await this._oAuth2Helper.getTokenFromStorage(); no longer returns null always when invoked from Background Task.

On Further investigation this may be due to the fact that Flutter_Secure_Storage operations are invoked without IOSOptions:
Future<void> write( {@required String key, @required String value, IOSOptions iOptions, AndroidOptions aOptions})

In secure_storage.dart we don't pass IOSOptions at all:
`class SecureStorage extends Storage {
static final FlutterSecureStorage storage = FlutterSecureStorage();

SecureStorage();

@OverRide
Future read(String key) async {
return await storage.read(key: key);
}

@OverRide
Future write(String key, String value) async {
return await storage.write(key: key, value: value);
}
}`

I am not an IOS expert nor a Flutter Secure Storage expert, but i feel that we need to pass IOSOptions with:
`enum IOSAccessibility {
// The data in the keychain can only be accessed when the device is unlocked.
// Only available if a passcode is set on the device.
// Items with this attribute do not migrate to a new device.
passcode,

// The data in the keychain item can be accessed only while the device is unlocked by the user.
unlocked,

// The data in the keychain item can be accessed only while the device is unlocked by the user.
// Items with this attribute do not migrate to a new device.
unlocked_this_device,

// The data in the keychain item cannot be accessed after a restart until the device has been unlocked once by the user.
first_unlock,

// The data in the keychain item cannot be accessed after a restart until the device has been unlocked once by the user.
// Items with this attribute do not migrate to a new device.
first_unlock_this_device,
}`

May be pass first_unlock in our invocations? I am not a Flutter expert, otherwise i would have loved to create a PR to fix the issue. Happy to help!

Thanks
Manju

Not able to get refresh_token - Custom aad client

Hi!

So as the title of this issue says: when I'm authenticating with the following aad custom client:

class AzureOAuth2Client extends OAuth2Client {
  AzureOAuth2Client({@required String redirectUri, @required customUriScheme})
      : super(
            authorizeUrl: 'https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/authorize',
            tokenUrl: 'https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token',
            redirectUri: redirectUri,
            customUriScheme: customUriScheme);
}

the authentication process is working, I'm getting an access_token as expected, but not any refresh_token.
Here is my sign in method:

signInWithMicrosoft() async {
    try {
      OAuth2Client azureClient = AzureOAuth2Client(redirectUri: 'area.app://auth', customUriScheme: 'area.app');
      OAuth2Helper oauth2Helper = OAuth2Helper(azureClient, grantType: OAuth2Helper.AUTHORIZATION_CODE, clientId: APP_ID, scopes: [
        'https://graph.microsoft.com/openid',
        'https://graph.microsoft.com/profile',
        'https://graph.microsoft.com/offline_access'
      ]);
      AccessTokenResponse accessTokenResponse = await oauth2Helper.getToken();
      log(accessTokenResponse.accessToken);
      log(accessTokenResponse.refreshToken);
    } catch (e) {
      log(e.toString());
      this.showToast("Something went wrong.");
    }
  }

Am i doing something wrong?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.