Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Add custom http client #15

Merged
merged 3 commits into from
Mar 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 16 additions & 8 deletions lib/src/oauth_chopper.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'dart:async';

import 'package:oauth2/oauth2.dart';
import 'package:http/http.dart' as http;
import 'package:oauth2/oauth2.dart' as oauth2;
import 'package:oauth_chopper/src/oauth_authenticator.dart';
import 'package:oauth_chopper/src/oauth_grant.dart';
import 'package:oauth_chopper/src/oauth_interceptor.dart';
Expand Down Expand Up @@ -36,11 +37,15 @@ class OAuthChopper {
/// See [OAuthStorage] for more information.
final OAuthStorage _storage;

/// Provide a custom [http.Client] which will be passed to [oauth2] and used for making new requests.
final http.Client? httpClient;

OAuthChopper({
required this.authorizationEndpoint,
required this.identifier,
required this.secret,
this.endSessionEndpoint,
this.httpClient,

/// OAuth storage for storing credentials.
/// By default it will use a in memory storage [MemoryStorage]. For persisting the credentials implement a custom [OAuthStorage].
Expand Down Expand Up @@ -73,13 +78,16 @@ class OAuthChopper {
Future<OAuthToken?> refresh() async {
final credentialsJson = await _storage.fetchCredentials();
if (credentialsJson == null) return null;
final credentials = Credentials.fromJson(credentialsJson);
final credentials = oauth2.Credentials.fromJson(credentialsJson);
try {
final newCredentials =
await credentials.refresh(identifier: identifier, secret: secret);
final newCredentials = await credentials.refresh(
identifier: identifier,
secret: secret,
httpClient: httpClient,
);
await _storage.saveCredentials(newCredentials.toJson());
return OAuthToken.fromCredentials(newCredentials);
} on AuthorizationException {
} on oauth2.AuthorizationException {
_storage.clear();
rethrow;
}
Expand All @@ -89,11 +97,11 @@ class OAuthChopper {
/// Currently supported grants:
/// - [ResourceOwnerPasswordGrant]
/// - [ClientCredentialsGrant]
///
/// - [AuthorizationCodeGrant]
/// Throws an exception if the grant fails.
Future<OAuthToken> requestGrant(OAuthGrant grant) async {
final credentials =
await grant.handle(authorizationEndpoint, identifier, secret);
final credentials = await grant.handle(
authorizationEndpoint, identifier, secret, httpClient);

await _storage.saveCredentials(credentials);

Expand Down
28 changes: 24 additions & 4 deletions lib/src/oauth_grant.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import 'package:http/http.dart' as http;
import 'package:oauth2/oauth2.dart' as oauth;

abstract class OAuthGrant {
const OAuthGrant();

Future<String> handle(
Uri authorizationEndpoint, String identifier, String secret);
Uri authorizationEndpoint,
String identifier,
String secret,
http.Client? httpClient,
);
}

/// Obtains credentials using a [resource owner password grant](https://tools.ietf.org/html/rfc6749#section-1.3.3).
Expand All @@ -17,13 +22,18 @@ class ResourceOwnerPasswordGrant extends OAuthGrant {

@override
Future<String> handle(
Uri authorizationEndpoint, String identifier, String secret) async {
Uri authorizationEndpoint,
String identifier,
String secret,
http.Client? httpClient,
) async {
final client = await oauth.resourceOwnerPasswordGrant(
authorizationEndpoint,
username,
password,
secret: secret,
identifier: identifier,
httpClient: httpClient,
);
return client.credentials.toJson();
}
Expand All @@ -35,11 +45,16 @@ class ClientCredentialsGrant extends OAuthGrant {

@override
Future<String> handle(
Uri authorizationEndpoint, String identifier, String secret) async {
Uri authorizationEndpoint,
String identifier,
String secret,
http.Client? httpClient,
) async {
final client = await oauth.clientCredentialsGrant(
authorizationEndpoint,
identifier,
secret,
httpClient: httpClient,
);
return client.credentials.toJson();
}
Expand All @@ -63,11 +78,16 @@ class AuthorizationCodeGrant extends OAuthGrant {

@override
Future<String> handle(
Uri authorizationEndpoint, String identifier, String secret) async {
Uri authorizationEndpoint,
String identifier,
String secret,
http.Client? httpClient,
) async {
final grant = oauth.AuthorizationCodeGrant(
identifier,
authorizationEndpoint,
tokenEndpoint,
httpClient: httpClient,
);
var authorizationUrl =
grant.getAuthorizationUrl(redirectUrl, scopes: scopes);
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ environment:

dependencies:
chopper: ^7.2.0
http: ^1.2.1
oauth2: ^2.0.2

dev_dependencies:
http: ^1.2.1
lints: ">=2.1.1 <4.0.0"
mocktail: ^1.0.3
test: ^1.25.2
5 changes: 3 additions & 2 deletions test/oauth_chopper_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ void main() {
test("Successful grant is stored", () async {
// arrange
when(() => storageMock.saveCredentials(any())).thenAnswer((_) => null);
when(() => grantMock.handle(any(), any(), any()))
when(() => grantMock.handle(any(), any(), any(), null))
.thenAnswer((_) async => testJson);
final oauthChopper = OAuthChopper(
authorizationEndpoint: Uri.parse('endpoint'),
Expand All @@ -101,7 +101,8 @@ void main() {
final token = await oauthChopper.requestGrant(grantMock);

// assert
verify(() => grantMock.handle(any(), 'identifier', 'secret')).called(1);
verify(() => grantMock.handle(any(), 'identifier', 'secret', null))
.called(1);
verify(() => storageMock.saveCredentials(testJson)).called(1);
expect(token.accessToken, 'accesToken');
expect(token.idToken, 'idToken');
Expand Down