Skip to content

Commit

Permalink
Merge pull request #18 from DutchCodingCompany/feature/add_custom_client
Browse files Browse the repository at this point in the history
✨ Add custom client
  • Loading branch information
Guldem authored Mar 15, 2024
2 parents 4162fe5 + dfeea5f commit 0141d56
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 13 deletions.
33 changes: 23 additions & 10 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 All @@ -27,6 +28,7 @@ class OAuthChopper {
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].
Expand All @@ -53,6 +55,10 @@ 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;

/// Get stored [OAuthToken].
Future<OAuthToken?> get token async {
final credentialsJson = await _storage.fetchCredentials();
Expand All @@ -76,18 +82,21 @@ class OAuthChopper {
/// Tries to refresh the available credentials and returns a new [OAuthToken]
/// instance.
/// Throws an exception when refreshing fails. If the exception is a
/// [AuthorizationException] it clears the storage.
/// See [Credentials.refresh]
/// [oauth2.AuthorizationException] it clears the storage.
/// See [oauth2.Credentials.refresh]
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 @@ -99,11 +108,15 @@ 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
8 changes: 8 additions & 0 deletions lib/src/oauth_grant.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:http/http.dart' as http;
import 'package:oauth2/oauth2.dart' as oauth;

/// {@template oauth_grant}
Expand All @@ -13,6 +14,7 @@ abstract interface class OAuthGrant {
Uri authorizationEndpoint,
String identifier,
String secret,
http.Client? httpClient,
);
}

Expand Down Expand Up @@ -40,13 +42,15 @@ class ResourceOwnerPasswordGrant implements OAuthGrant {
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 @@ -64,11 +68,13 @@ class ClientCredentialsGrant implements OAuthGrant {
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 Down Expand Up @@ -113,11 +119,13 @@ class AuthorizationCodeGrant implements OAuthGrant {
Uri authorizationEndpoint,
String identifier,
String secret,
http.Client? httpClient,
) async {
final grant = oauth.AuthorizationCodeGrant(
identifier,
authorizationEndpoint,
tokenEndpoint,
httpClient: httpClient,
);

final authorizationUrl = grant.getAuthorizationUrl(
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
mocktail: ^1.0.3
test: ^1.25.2
very_good_analysis: ^5.1.0
5 changes: 3 additions & 2 deletions test/oauth_chopper_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,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 @@ -106,7 +106,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 0141d56

Please sign in to comment.