Skip to content

Commit 3ce6a7d

Browse files
authored
Merge pull request #25 from DutchCodingCompany/feature/mirror_oauth_lib
✨ Added more oauth2 package parameters
2 parents f593462 + bf106d6 commit 3ce6a7d

File tree

5 files changed

+138
-31
lines changed

5 files changed

+138
-31
lines changed

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
## 1.1.0
2+
- Synced oauth_chopper with auth2 package. This makes more parameters available which are supported by oauth2.
3+
- Be default `OAuthChopper` client can now also be provided with the following parameter. Which will be passed to oauth2.
4+
- `scopes`
5+
- `basicAuth`
6+
- `delimiter`
7+
- `getParameters`
8+
- Added `newScopes` & `basicAuth` parameters to `OAuthChopper.refresh` which wil be passed to oauth2
9+
- BREAKING: `scopes` has been removed from `AuthorizationCodeGrant`. These are now provided in the `OAuthChopper` client.
10+
- BREAKING: `OAuthGrant.handle` has been extended to support new parameters as optional named parameters, `including` secret and `httpClient`.
11+
112
## 1.0.1
213
- Updated dependencies:
314
- `sdk` to `>=3.4.0 <4.0.0`

lib/src/oauth_chopper.dart

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'dart:async';
22

33
import 'package:http/http.dart' as http;
4+
import 'package:http_parser/http_parser.dart';
45
import 'package:oauth2/oauth2.dart' as oauth2;
56
import 'package:oauth_chopper/src/oauth_grant.dart';
67
import 'package:oauth_chopper/src/oauth_interceptor.dart';
@@ -25,9 +26,13 @@ class OAuthChopper {
2526
OAuthChopper({
2627
required this.authorizationEndpoint,
2728
required this.identifier,
28-
required this.secret,
29+
this.secret,
2930
this.endSessionEndpoint,
3031
this.httpClient,
32+
this.scopes,
33+
this.basicAuth = true,
34+
this.delimiter,
35+
this.getParameters,
3136

3237
/// OAuth storage for storing credentials.
3338
/// By default it will use a in memory storage [MemoryStorage].
@@ -46,7 +51,7 @@ class OAuthChopper {
4651
final String identifier;
4752

4853
/// OAuth secret.
49-
final String secret;
54+
final String? secret;
5055

5156
/// OAuth storage for storing credentials.
5257
/// By default it will use a in memory storage. For persisting the credentials
@@ -58,6 +63,23 @@ class OAuthChopper {
5863
/// for making new requests.
5964
final http.Client? httpClient;
6065

66+
/// The scopes that the client is requesting access to.
67+
/// Will be passed to [oauth2].
68+
final Iterable<String>? scopes;
69+
70+
/// Whether to use HTTP Basic authentication for authorizing the client.
71+
/// Will be passed to [oauth2].
72+
final bool basicAuth;
73+
74+
/// A [String] used to separate scopes; defaults to `" "`.
75+
/// Will be passed to [oauth2].
76+
final String? delimiter;
77+
78+
/// The function used to parse parameters from a host's response.
79+
/// Will be passed to [oauth2].
80+
final Map<String, dynamic> Function(MediaType? contentType, String body)?
81+
getParameters;
82+
6183
/// Get stored [OAuthToken].
6284
Future<OAuthToken?> get token async {
6385
final credentialsJson = await _storage.fetchCredentials();
@@ -78,15 +100,22 @@ class OAuthChopper {
78100
/// instance.
79101
/// Throws an exception when refreshing fails. If the exception is a
80102
/// [oauth2.AuthorizationException] it clears the storage.
81-
/// See [oauth2.Credentials.refresh]
82-
Future<OAuthToken?> refresh() async {
103+
///
104+
/// See [oauth2.Credentials.refresh] for more information
105+
/// and information about [newScopes] and [basicAuth].
106+
Future<OAuthToken?> refresh({
107+
bool basicAuth = true,
108+
Iterable<String>? newScopes,
109+
}) async {
83110
final credentialsJson = await _storage.fetchCredentials();
84111
if (credentialsJson == null) return null;
85112
final credentials = oauth2.Credentials.fromJson(credentialsJson);
86113
try {
87114
final newCredentials = await credentials.refresh(
88115
identifier: identifier,
89116
secret: secret,
117+
newScopes: newScopes,
118+
basicAuth: basicAuth,
90119
httpClient: httpClient,
91120
);
92121
await _storage.saveCredentials(newCredentials.toJson());
@@ -109,8 +138,12 @@ class OAuthChopper {
109138
final credentials = await grant.handle(
110139
authorizationEndpoint,
111140
identifier,
112-
secret,
113-
httpClient,
141+
secret: secret,
142+
httpClient: httpClient,
143+
scopes: scopes,
144+
getParameters: getParameters,
145+
delimiter: delimiter,
146+
basicAuth: basicAuth,
114147
);
115148

116149
await _storage.saveCredentials(credentials);

lib/src/oauth_grant.dart

Lines changed: 72 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import 'package:http/http.dart' as http;
2-
import 'package:oauth2/oauth2.dart' as oauth;
2+
import 'package:http_parser/http_parser.dart';
3+
import 'package:oauth2/oauth2.dart' as oauth2;
4+
import 'package:oauth2/oauth2.dart';
35

46
/// {@template oauth_grant}
57
/// Interface for a OAuth grant.
@@ -20,10 +22,15 @@ abstract interface class OAuthGrant {
2022
/// Obtains credentials from an authorization server.
2123
Future<String> handle(
2224
Uri authorizationEndpoint,
23-
String identifier,
24-
String secret,
25+
String identifier, {
26+
String? secret,
2527
http.Client? httpClient,
26-
);
28+
Iterable<String>? scopes,
29+
bool basicAuth = true,
30+
String? delimiter,
31+
Map<String, dynamic> Function(MediaType? contentType, String body)?
32+
getParameters,
33+
});
2734
}
2835

2936
/// {@template resource_owner_password_grant}
@@ -37,6 +44,7 @@ class ResourceOwnerPasswordGrant implements OAuthGrant {
3744
const ResourceOwnerPasswordGrant({
3845
required this.username,
3946
required this.password,
47+
this.onCredentialsRefreshed,
4048
});
4149

4250
/// Username used for obtaining credentials.
@@ -45,20 +53,36 @@ class ResourceOwnerPasswordGrant implements OAuthGrant {
4553
/// Password used for obtaining credentials.
4654
final String password;
4755

56+
/// Callback to be invoked whenever the credentials are refreshed.
57+
///
58+
/// This will be passed as-is to the constructed [Client].
59+
/// Will be passed to [oauth2].
60+
final CredentialsRefreshedCallback? onCredentialsRefreshed;
61+
4862
@override
4963
Future<String> handle(
5064
Uri authorizationEndpoint,
51-
String identifier,
52-
String secret,
65+
String identifier, {
66+
String? secret,
5367
http.Client? httpClient,
54-
) async {
55-
final client = await oauth.resourceOwnerPasswordGrant(
68+
Iterable<String>? scopes,
69+
bool basicAuth = true,
70+
String? delimiter,
71+
Map<String, dynamic> Function(MediaType? contentType, String body)?
72+
getParameters,
73+
}) async {
74+
final client = await oauth2.resourceOwnerPasswordGrant(
5675
authorizationEndpoint,
5776
username,
5877
password,
5978
secret: secret,
6079
identifier: identifier,
80+
scopes: scopes,
81+
basicAuth: basicAuth,
82+
delimiter: delimiter,
6183
httpClient: httpClient,
84+
getParameters: getParameters,
85+
onCredentialsRefreshed: onCredentialsRefreshed,
6286
);
6387
return client.credentials.toJson();
6488
}
@@ -74,15 +98,24 @@ class ClientCredentialsGrant implements OAuthGrant {
7498
@override
7599
Future<String> handle(
76100
Uri authorizationEndpoint,
77-
String identifier,
78-
String secret,
101+
String identifier, {
102+
String? secret,
79103
http.Client? httpClient,
80-
) async {
81-
final client = await oauth.clientCredentialsGrant(
104+
Iterable<String>? scopes,
105+
bool basicAuth = true,
106+
String? delimiter,
107+
Map<String, dynamic> Function(MediaType? contentType, String body)?
108+
getParameters,
109+
}) async {
110+
final client = await oauth2.clientCredentialsGrant(
82111
authorizationEndpoint,
83112
identifier,
84113
secret,
114+
scopes: scopes,
115+
basicAuth: basicAuth,
116+
delimiter: delimiter,
85117
httpClient: httpClient,
118+
getParameters: getParameters,
86119
);
87120
return client.credentials.toJson();
88121
}
@@ -95,10 +128,11 @@ class AuthorizationCodeGrant implements OAuthGrant {
95128
/// {@macro authorization_code_grant}
96129
const AuthorizationCodeGrant({
97130
required this.tokenEndpoint,
98-
required this.scopes,
99131
required this.redirectUrl,
100132
required this.redirect,
101133
required this.listen,
134+
this.onCredentialsRefreshed,
135+
this.codeVerifier,
102136
});
103137

104138
/// A URL provided by the authorization server that this library uses to
@@ -111,9 +145,16 @@ class AuthorizationCodeGrant implements OAuthGrant {
111145
/// The redirect URL where the resource owner will redirect to.
112146
final Uri redirectUrl;
113147

114-
/// The specific permissions being requested from the authorization server may
115-
/// be specified via [scopes].
116-
final List<String> scopes;
148+
/// Callback to be invoked whenever the credentials are refreshed.
149+
///
150+
/// This will be passed as-is to the constructed [Client].
151+
/// Will be passed to [oauth2].
152+
final CredentialsRefreshedCallback? onCredentialsRefreshed;
153+
154+
/// The PKCE code verifier. Will be generated if one is not provided in the
155+
/// constructor.
156+
/// Will be passed to [oauth2].
157+
final String? codeVerifier;
117158

118159
/// Callback used for redirect the authorizationUrl given by the authorization
119160
/// server.
@@ -125,15 +166,26 @@ class AuthorizationCodeGrant implements OAuthGrant {
125166
@override
126167
Future<String> handle(
127168
Uri authorizationEndpoint,
128-
String identifier,
129-
String secret,
169+
String identifier, {
170+
String? secret,
130171
http.Client? httpClient,
131-
) async {
132-
final grant = oauth.AuthorizationCodeGrant(
172+
Iterable<String>? scopes,
173+
bool basicAuth = true,
174+
String? delimiter,
175+
Map<String, dynamic> Function(MediaType? contentType, String body)?
176+
getParameters,
177+
}) async {
178+
final grant = oauth2.AuthorizationCodeGrant(
133179
identifier,
134180
authorizationEndpoint,
135181
tokenEndpoint,
182+
basicAuth: basicAuth,
183+
delimiter: delimiter,
184+
getParameters: getParameters,
185+
secret: secret,
136186
httpClient: httpClient,
187+
onCredentialsRefreshed: onCredentialsRefreshed,
188+
codeVerifier: codeVerifier,
137189
);
138190

139191
final authorizationUrl = grant.getAuthorizationUrl(

pubspec.yaml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: oauth_chopper
22
description: Add and manage OAuth2 authentication for your Chopper client.
3-
version: 1.0.1
3+
version: 1.1.0
44
homepage: https://github.com/DutchCodingCompany/oauth_chopper
55

66
environment:
@@ -9,7 +9,8 @@ environment:
99
dependencies:
1010
chopper: ^8.0.1+1
1111
http: ^1.2.2
12-
oauth2: ^2.0.2
12+
http_parser: ^4.1.0
13+
oauth2: ^2.0.3
1314

1415
dev_dependencies:
1516
mocktail: ^1.0.4

test/oauth_chopper_test.dart

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,18 @@ void main() {
7878
test('Successful grant is stored', () async {
7979
// arrange
8080
when(() => storageMock.saveCredentials(any())).thenAnswer((_) => null);
81-
when(() => grantMock.handle(any(), any(), any(), null))
82-
.thenAnswer((_) async => testJson);
81+
when(
82+
() => grantMock.handle(
83+
any(),
84+
any(),
85+
secret: any(named: 'secret'),
86+
basicAuth: any(named: 'basicAuth'),
87+
httpClient: any(named: 'httpClient'),
88+
delimiter: any(named: 'delimiter'),
89+
getParameters: any(named: 'getParameters'),
90+
scopes: any(named: 'scopes'),
91+
),
92+
).thenAnswer((_) async => testJson);
8393
final oauthChopper = OAuthChopper(
8494
authorizationEndpoint: Uri.parse('endpoint'),
8595
identifier: 'identifier',
@@ -91,7 +101,7 @@ void main() {
91101
final token = await oauthChopper.requestGrant(grantMock);
92102

93103
// assert
94-
verify(() => grantMock.handle(any(), 'identifier', 'secret', null))
104+
verify(() => grantMock.handle(any(), 'identifier', secret: 'secret'))
95105
.called(1);
96106
verify(() => storageMock.saveCredentials(testJson)).called(1);
97107
expect(token.accessToken, 'accesToken');

0 commit comments

Comments
 (0)