Skip to content

Commit 139ff69

Browse files
committed
Merge branch 'main' of github.com:DutchCodingCompany/oauth_chopper into feature/update-dependencies
# Conflicts: # CHANGELOG.md # example/oauth_chopper_example.dart # pubspec.yaml # test/oauth_authenticator_test.dart # test/oauth_interceptor_test.dart
2 parents 57e3b5b + 2f422a6 commit 139ff69

File tree

9 files changed

+100
-15
lines changed

9 files changed

+100
-15
lines changed

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,22 @@
44
- Added basic github action checks.
55
- Removed mockito in favor of mocktail.
66

7+
## 0.1.2
8+
9+
- Add ID token to OAuth token
10+
11+
## 0.1.1
12+
13+
- Export OauthToken class
14+
15+
## 0.1.0
16+
17+
- Add support for AuthorizationCodeGrant
18+
19+
## 0.0.5
20+
21+
- Update Chopper
22+
723
## 0.0.4
824

925
- Removed import for HttpStatus because platform support

README.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ This can be override by providing a custom storage implementation.
2828

2929
- ✅ ResourceOwnerPasswordGrant
3030
- ✅ ClientCredentialsGrant
31-
- AuthorizationCodeGrant (*TODO*)
31+
- AuthorizationCodeGrant
3232

3333
## Usage
3434

@@ -62,6 +62,15 @@ Example:
6262
password: 'password',
6363
),
6464
);
65+
66+
// Authorization Cod eGrant
67+
oauth_chopper.AuthorizationCodeGrant grant = oauth_chopper.AuthorizationCodeGrant(
68+
tokenEndpoint: Uri.parse("tokenEndpoint"),
69+
scopes: ["scope1", "scope2"],
70+
redirectUrl: Uri.parse("redirectUrl"),
71+
redirect: redirect,
72+
listen: listen,
73+
);
6574
```
6675

6776
If you want to persist the OAuth2 credential information you can provide a custom OAuthStorage

lib/oauth_chopper.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ export 'package:oauth2/src/expiration_exception.dart';
88

99
export 'src/oauth_chopper.dart';
1010
export 'src/oauth_grant.dart';
11+
export 'src/oauth_token.dart';
1112
export 'src/storage/oauth_storage.dart';

lib/src/oauth_chopper.dart

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ class OAuthChopper {
2222
/// OAuth authorization endpoint.
2323
final Uri authorizationEndpoint;
2424

25+
/// OAuth endpoint to end session.
26+
final Uri? endSessionEndpoint;
27+
2528
/// OAuth identifier
2629
final String identifier;
2730

@@ -37,6 +40,7 @@ class OAuthChopper {
3740
required this.authorizationEndpoint,
3841
required this.identifier,
3942
required this.secret,
43+
this.endSessionEndpoint,
4044

4145
/// OAuth storage for storing credentials.
4246
/// By default it will use a in memory storage [MemoryStorage]. For persisting the credentials implement a custom [OAuthStorage].
@@ -47,9 +51,7 @@ class OAuthChopper {
4751
/// Get stored [OAuthToken].
4852
Future<OAuthToken?> get token async {
4953
final credentialsJson = await _storage.fetchCredentials();
50-
return credentialsJson != null
51-
? OAuthToken.fromJson(credentialsJson)
52-
: null;
54+
return credentialsJson != null ? OAuthToken.fromJson(credentialsJson) : null;
5355
}
5456

5557
/// Provides an [OAuthAuthenticator] instance.
@@ -70,8 +72,7 @@ class OAuthChopper {
7072
if (credentialsJson == null) return null;
7173
final credentials = Credentials.fromJson(credentialsJson);
7274
try {
73-
final newCredentials =
74-
await credentials.refresh(identifier: identifier, secret: secret);
75+
final newCredentials = await credentials.refresh(identifier: identifier, secret: secret);
7576
await _storage.saveCredentials(newCredentials.toJson());
7677
return OAuthToken.fromCredentials(newCredentials);
7778
} catch (e) {
@@ -87,8 +88,7 @@ class OAuthChopper {
8788
///
8889
/// Throws an exception if the grant fails.
8990
Future<OAuthToken> requestGrant(OAuthGrant grant) async {
90-
final credentials =
91-
await grant.handle(authorizationEndpoint, identifier, secret);
91+
final credentials = await grant.handle(authorizationEndpoint, identifier, secret);
9292

9393
await _storage.saveCredentials(credentials);
9494

lib/src/oauth_grant.dart

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,37 @@ class ClientCredentialsGrant extends OAuthGrant {
4545
}
4646
}
4747

48-
//TODO: Add AuthorizationCodeGrant
48+
/// Obtains credentials using a [authorization code grant](https://tools.ietf.org/html/rfc6749#section-1.3.1).
49+
class AuthorizationCodeGrant extends OAuthGrant {
50+
const AuthorizationCodeGrant({
51+
required this.tokenEndpoint,
52+
required this.scopes,
53+
required this.redirectUrl,
54+
required this.redirect,
55+
required this.listen,
56+
});
57+
58+
final Uri tokenEndpoint;
59+
final Uri redirectUrl;
60+
final List<String> scopes;
61+
final Future<void> Function(Uri authorizationUri) redirect;
62+
final Future<Uri> Function(Uri redirectUri) listen;
63+
64+
@override
65+
Future<String> handle(
66+
Uri authorizationEndpoint, String identifier, String secret) async {
67+
final grant = oauth.AuthorizationCodeGrant(
68+
identifier,
69+
authorizationEndpoint,
70+
tokenEndpoint,
71+
);
72+
var authorizationUrl =
73+
grant.getAuthorizationUrl(redirectUrl, scopes: scopes);
74+
await redirect(authorizationUrl);
75+
var responseUrl = await listen(redirectUrl);
76+
oauth.Client client =
77+
await grant.handleAuthorizationResponse(responseUrl.queryParameters);
78+
79+
return client.credentials.toJson();
80+
}
81+
}

lib/src/oauth_token.dart

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@ class OAuthToken {
44
final String accessToken;
55
final String? refreshToken;
66
final DateTime? expiration;
7+
final String? idToken;
78

8-
bool get isExpired =>
9-
expiration != null && DateTime.now().isAfter(expiration!);
9+
bool get isExpired => expiration != null && DateTime.now().isAfter(expiration!);
1010

1111
const OAuthToken._(
1212
this.accessToken,
1313
this.refreshToken,
1414
this.expiration,
15+
this.idToken,
1516
);
1617

1718
factory OAuthToken.fromJson(String json) {
@@ -23,5 +24,6 @@ class OAuthToken {
2324
credentials.accessToken,
2425
credentials.refreshToken,
2526
credentials.expiration,
27+
credentials.idToken,
2628
);
2729
}

pubspec.yaml

Lines changed: 1 addition & 1 deletion
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: 0.1.0
3+
version: 0.1.2
44
homepage: https://github.com/DutchCodingCompany/oauth_chopper
55

66
environment:

test/oauth_chopper_test.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ void main() {
1717
{
1818
"accessToken": "accesToken",
1919
"refreshToken": "refreshToken",
20-
"idToken": null,
20+
"idToken": "idToken",
2121
"tokenEndpoint": "https://test.test/oauth/token",
2222
"scopes": [],
2323
"expiration": 1664359530234
@@ -60,6 +60,7 @@ void main() {
6060
// assert
6161
expect(token?.accessToken, 'accesToken');
6262
expect(token?.refreshToken, 'refreshToken');
63+
expect(token?.idToken, 'idToken');
6364
});
6465

6566
test('Returns no token if not in storage', () async {
@@ -89,6 +90,7 @@ void main() {
8990
verify(() => grantMock.handle(any(), 'identifier', 'secret')).called(1);
9091
verify(() => storageMock.saveCredentials(testJson)).called(1);
9192
expect(token.accessToken, 'accesToken');
93+
expect(token?.idToken, 'idToken');
9294
expect(token.refreshToken, 'refreshToken');
9395
});
9496
}

test/oauth_interceptor_test.dart

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import 'package:mocktail/mocktail.dart';
33
import 'package:oauth2/oauth2.dart';
44
import 'package:oauth_chopper/oauth_chopper.dart';
55
import 'package:oauth_chopper/src/oauth_interceptor.dart';
6-
import 'package:oauth_chopper/src/oauth_token.dart';
76
import 'package:test/test.dart';
87

98
class MockOAuthChopper extends Mock implements OAuthChopper {}
@@ -18,7 +17,16 @@ void main() {
1817
),
1918
);
2019

21-
final testRequest = Request('GET', Uri.parse('test'), Uri.parse('test'));
20+
final testIDtoken = OAuthToken.fromCredentials(
21+
Credentials(
22+
'token',
23+
refreshToken: 'refresh',
24+
idToken: 'idToken',
25+
expiration: DateTime(2022, 9, 1),
26+
),
27+
);
28+
29+
final testRequest = Request('GET', Uri(host: 'test'), Uri(host: 'test'));
2230

2331
test('HeaderInterceptor adds available token to headers', () async {
2432
// arrange
@@ -32,6 +40,20 @@ void main() {
3240
// assert
3341
expect(result.headers, expected);
3442
});
43+
44+
test('HeaderInterceptor does not add IDToken when available to headers', () async {
45+
// arrange
46+
when(mockOAuthChopper.token).thenAnswer((_) async => testIDtoken);
47+
final interceptor = OAuthInterceptor(mockOAuthChopper);
48+
final expected = {'Authorization': 'Bearer token'};
49+
50+
// act
51+
final result = await interceptor.onRequest(testRequest);
52+
53+
// assert
54+
expect(result.headers, expected);
55+
});
56+
3557
test('HeaderInterceptor adds no token to headers', () async {
3658
// arrange
3759
when(()=>mockOAuthChopper.token).thenAnswer((_) async => null);

0 commit comments

Comments
 (0)