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 client #18

Merged
merged 5 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
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
Loading