Index: lib/src/credentials.dart |
diff --git a/lib/src/credentials.dart b/lib/src/credentials.dart |
index 37e71148711a42cad1953f0dc52674d12ca1d37f..0a12f3f8872128583c32d8f823a2e7bc9165e9f1 100644 |
--- a/lib/src/credentials.dart |
+++ b/lib/src/credentials.dart |
@@ -5,11 +5,13 @@ |
library oauth2.credentials; |
import 'dart:async'; |
+import 'dart:collection'; |
import 'dart:convert'; |
import 'package:http/http.dart' as http; |
import 'handle_access_token_response.dart'; |
+import 'utils.dart'; |
/// Credentials that prove that a client is allowed to access a resource on the |
/// resource owner's behalf. |
@@ -72,11 +74,15 @@ class Credentials { |
/// [AuthorizationCodeGrant]. Alternately, it may be loaded from a serialized |
/// form via [Credentials.fromJson]. |
Credentials( |
- this.accessToken, |
- [this.refreshToken, |
- this.tokenEndpoint, |
- this.scopes, |
- this.expiration]); |
+ this.accessToken, |
+ {this.refreshToken, |
+ this.tokenEndpoint, |
+ Iterable<String> scopes, |
+ this.expiration}) |
+ : scopes = new UnmodifiableListView( |
+ // Explicitly type-annotate the list literal to work around |
+ // sdk#24202. |
+ scopes == null ? <String>[] : scopes.toList()); |
/// Loads a set of credentials from a JSON-serialized form. |
/// |
@@ -126,10 +132,10 @@ class Credentials { |
return new Credentials( |
parsed['accessToken'], |
- parsed['refreshToken'], |
- tokenEndpoint, |
- scopes, |
- expiration); |
+ refreshToken: parsed['refreshToken'], |
+ tokenEndpoint: tokenEndpoint, |
+ scopes: scopes, |
+ expiration: expiration); |
} |
/// Serializes a set of credentials to JSON. |
@@ -152,19 +158,25 @@ class Credentials { |
/// You may request different scopes than the default by passing in |
/// [newScopes]. These must be a subset of [scopes]. |
/// |
- /// This will throw a [StateError] if these credentials can't be refreshed, an |
+ /// This throws an [ArgumentError] if [secret] is passed without [identifier], |
+ /// a [StateError] if these credentials can't be refreshed, an |
/// [AuthorizationException] if refreshing the credentials fails, or a |
/// [FormatError] if the authorization server returns invalid responses. |
Future<Credentials> refresh( |
- String identifier, |
+ {String identifier, |
String secret, |
- {List<String> newScopes, |
- http.Client httpClient}) async { |
+ Iterable<String> newScopes, |
+ bool basicAuth: true, |
+ http.Client httpClient}) async { |
var scopes = this.scopes; |
- if (newScopes != null) scopes = newScopes; |
- if (scopes == null) scopes = <String>[]; |
+ if (newScopes != null) scopes = newScopes.toList(); |
+ if (scopes == null) scopes = []; |
if (httpClient == null) httpClient = new http.Client(); |
+ if (identifier == null && secret != null) { |
+ throw new ArgumentError("secret may not be passed without identifier."); |
+ } |
+ |
var startTime = new DateTime.now(); |
if (refreshToken == null) { |
throw new StateError("Can't refresh credentials without a refresh " |
@@ -174,29 +186,34 @@ class Credentials { |
"endpoint."); |
} |
- var fields = { |
+ var headers = {}; |
+ |
+ var body = { |
"grant_type": "refresh_token", |
- "refresh_token": refreshToken, |
- // TODO(nweiz): the spec recommends that HTTP basic auth be used in |
- // preference to form parameters, but Google doesn't support that. |
- // Should it be configurable? |
- "client_id": identifier, |
- "client_secret": secret |
+ "refresh_token": refreshToken |
}; |
- if (!scopes.isEmpty) fields["scope"] = scopes.join(' '); |
+ if (!scopes.isEmpty) body["scope"] = scopes.join(' '); |
+ |
+ if (basicAuth && secret != null) { |
+ headers["Authorization"] = basicAuthHeader(identifier, secret); |
+ } else { |
+ if (identifier != null) body["client_id"] = identifier; |
+ if (secret != null) body["client_secret"] = secret; |
+ } |
- var response = await httpClient.post(tokenEndpoint, body: fields); |
+ var response = await httpClient.post(tokenEndpoint, |
+ headers: headers, body: body); |
var credentials = await handleAccessTokenResponse( |
- response, tokenEndpoint, startTime, scopes); |
+ response, tokenEndpoint, startTime, scopes); |
// The authorization server may issue a new refresh token. If it doesn't, |
// we should re-use the one we already have. |
if (credentials.refreshToken != null) return credentials; |
return new Credentials( |
credentials.accessToken, |
- this.refreshToken, |
- credentials.tokenEndpoint, |
- credentials.scopes, |
- credentials.expiration); |
+ refreshToken: this.refreshToken, |
+ tokenEndpoint: credentials.tokenEndpoint, |
+ scopes: credentials.scopes, |
+ expiration: credentials.expiration); |
} |
} |