Merge pull request #47 from wesleyfantinel/master
OpenID's id_token implementation
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0181a09..87dfbad 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+# 1.4.0
+
+* OpenID's id_token treated.
+
# 1.3.0
* Added `onCredentialsRefreshed` option when creating `Client` objects.
diff --git a/lib/src/credentials.dart b/lib/src/credentials.dart
index e8a161a..6ea254d 100644
--- a/lib/src/credentials.dart
+++ b/lib/src/credentials.dart
@@ -44,6 +44,16 @@
/// This may be `null`, indicating that the credentials can't be refreshed.
final String refreshToken;
+ /// The token that is received from the authorization server to enable
+ /// End-Users to be Authenticated, contains Claims, represented as a
+ /// JSON Web Token (JWT).
+ ///
+ /// This may be `null`, indicating that the 'openid' scope was not
+ /// requested (or not supported).
+ ///
+ /// [spec]: https://openid.net/specs/openid-connect-core-1_0.html#IDToken
+ final String idToken;
+
/// The URL of the authorization server endpoint that's used to refresh the
/// credentials.
///
@@ -98,6 +108,7 @@
/// [standard JSON response]: https://tools.ietf.org/html/rfc6749#section-5.1
Credentials(this.accessToken,
{this.refreshToken,
+ this.idToken,
this.tokenEndpoint,
Iterable<String> scopes,
this.expiration,
@@ -135,7 +146,7 @@
'required field "accessToken" was not a string, was '
'${parsed["accessToken"]}');
- for (var stringField in ['refreshToken', 'tokenEndpoint']) {
+ for (var stringField in ['refreshToken', 'idToken', 'tokenEndpoint']) {
var value = parsed[stringField];
validate(value == null || value is String,
'field "$stringField" was not a string, was "$value"');
@@ -158,6 +169,7 @@
return new Credentials(parsed['accessToken'],
refreshToken: parsed['refreshToken'],
+ idToken: parsed['idToken'],
tokenEndpoint: tokenEndpoint,
scopes: (scopes as List).map((scope) => scope as String),
expiration: expiration);
@@ -170,6 +182,7 @@
String toJson() => jsonEncode({
'accessToken': accessToken,
'refreshToken': refreshToken,
+ 'idToken': idToken,
'tokenEndpoint':
tokenEndpoint == null ? null : tokenEndpoint.toString(),
'scopes': scopes,
@@ -236,6 +249,7 @@
if (credentials.refreshToken != null) return credentials;
return new Credentials(credentials.accessToken,
refreshToken: this.refreshToken,
+ idToken: credentials.idToken,
tokenEndpoint: credentials.tokenEndpoint,
scopes: credentials.scopes,
expiration: credentials.expiration);
diff --git a/lib/src/handle_access_token_response.dart b/lib/src/handle_access_token_response.dart
index ca4896e..851475a 100644
--- a/lib/src/handle_access_token_response.dart
+++ b/lib/src/handle_access_token_response.dart
@@ -72,7 +72,7 @@
'parameter "expires_in" was not an int, was "$expiresIn"');
}
- for (var name in ['refresh_token', 'scope']) {
+ for (var name in ['refresh_token', 'id_token', 'scope']) {
var value = parameters[name];
if (value != null && value is! String)
throw new FormatException(
@@ -88,6 +88,7 @@
return new Credentials(parameters['access_token'],
refreshToken: parameters['refresh_token'],
+ idToken: parameters['id_token'],
tokenEndpoint: tokenEndpoint,
scopes: scopes,
expiration: expiration);
diff --git a/pubspec.yaml b/pubspec.yaml
index 9ccb43e..9e8c56f 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
name: oauth2
-version: 1.3.0
+version: 1.4.0
author: Dart Team <misc@dartlang.org>
homepage: https://github.com/dart-lang/oauth2
description: >-
diff --git a/test/credentials_test.dart b/test/credentials_test.dart
index 039a060..386f948 100644
--- a/test/credentials_test.dart
+++ b/test/credentials_test.dart
@@ -270,6 +270,7 @@
var credentials = new oauth2.Credentials('access token',
refreshToken: 'refresh token',
+ idToken: 'id token',
tokenEndpoint: tokenEndpoint,
scopes: ['scope1', 'scope2'],
expiration: expiration);
@@ -277,6 +278,7 @@
expect(reloaded.accessToken, equals(credentials.accessToken));
expect(reloaded.refreshToken, equals(credentials.refreshToken));
+ expect(reloaded.idToken, equals(credentials.idToken));
expect(reloaded.tokenEndpoint.toString(),
equals(credentials.tokenEndpoint.toString()));
expect(reloaded.scopes, equals(credentials.scopes));
@@ -306,6 +308,11 @@
throwsFormatException);
});
+ test("should throw a FormatException if idToken is not a string", () {
+ expect(() => fromMap({"accessToken": "foo", "idToken": 12}),
+ throwsFormatException);
+ });
+
test("should throw a FormatException if tokenEndpoint is not a string", () {
expect(() => fromMap({"accessToken": "foo", "tokenEndpoint": 12}),
throwsFormatException);
diff --git a/test/handle_access_token_response_test.dart b/test/handle_access_token_response_test.dart
index fd4b1a7..1556164 100644
--- a/test/handle_access_token_response_test.dart
+++ b/test/handle_access_token_response_test.dart
@@ -259,4 +259,34 @@
expect(credentials.scopes, equals(['scope1', 'scope2']));
});
});
+
+ group('a success response with a id_token', () {
+ oauth2.Credentials handleSuccess(
+ {String contentType = "application/json",
+ accessToken = 'access token',
+ tokenType = 'bearer',
+ expiresIn,
+ idToken = 'decode me',
+ scope}) {
+ return handle(new http.Response(
+ jsonEncode({
+ 'access_token': accessToken,
+ 'token_type': tokenType,
+ 'expires_in': expiresIn,
+ 'id_token': idToken,
+ 'scope': scope
+ }),
+ 200,
+ headers: {'content-type': contentType}));
+ }
+
+ test('with a non-string id token throws a FormatException', () {
+ expect(() => handleSuccess(idToken: 12), throwsFormatException);
+ });
+
+ test('with a id token sets the id token', () {
+ var credentials = handleSuccess(idToken: "decode me");
+ expect(credentials.idToken, equals("decode me"));
+ });
+ });
}