Skip to content

Commit

Permalink
Merge pull request #15 from DutchCodingCompany/feature/add_custom_client
Browse files Browse the repository at this point in the history
✨ Add custom http client
  • Loading branch information
Guldem authored Mar 15, 2024
2 parents bd02c05 + d4c8825 commit 1efd5a9
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 15 deletions.
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

0 comments on commit 1efd5a9

Please sign in to comment.