OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library credentials; | 5 library oauth2.credentials; |
6 | 6 |
7 import 'dart:async'; | 7 import 'dart:async'; |
8 import 'dart:convert'; | 8 import 'dart:convert'; |
9 | 9 |
10 import 'package:http/http.dart' as http; | 10 import 'package:http/http.dart' as http; |
11 | 11 |
12 import 'handle_access_token_response.dart'; | 12 import 'handle_access_token_response.dart'; |
13 import 'utils.dart'; | |
14 | 13 |
15 /// Credentials that prove that a client is allowed to access a resource on the | 14 /// Credentials that prove that a client is allowed to access a resource on the |
16 /// resource owner's behalf. These credentials are long-lasting and can be | 15 /// resource owner's behalf. |
17 /// safely persisted across multiple runs of the program. | 16 /// |
| 17 /// These credentials are long-lasting and can be safely persisted across |
| 18 /// multiple runs of the program. |
18 /// | 19 /// |
19 /// Many authorization servers will attach an expiration date to a set of | 20 /// Many authorization servers will attach an expiration date to a set of |
20 /// credentials, along with a token that can be used to refresh the credentials | 21 /// credentials, along with a token that can be used to refresh the credentials |
21 /// once they've expired. The [Client] will automatically refresh its | 22 /// once they've expired. The [Client] will automatically refresh its |
22 /// credentials when necessary. It's also possible to explicitly refresh them | 23 /// credentials when necessary. It's also possible to explicitly refresh them |
23 /// via [Client.refreshCredentials] or [Credentials.refresh]. | 24 /// via [Client.refreshCredentials] or [Credentials.refresh]. |
24 /// | 25 /// |
25 /// Note that a given set of credentials can only be refreshed once, so be sure | 26 /// Note that a given set of credentials can only be refreshed once, so be sure |
26 /// to save the refreshed credentials for future use. | 27 /// to save the refreshed credentials for future use. |
27 class Credentials { | 28 class Credentials { |
28 /// The token that is sent to the resource server to prove the authorization | 29 /// The token that is sent to the resource server to prove the authorization |
29 /// of a client. | 30 /// of a client. |
30 final String accessToken; | 31 final String accessToken; |
31 | 32 |
32 /// The token that is sent to the authorization server to refresh the | 33 /// The token that is sent to the authorization server to refresh the |
33 /// credentials. This is optional. | 34 /// credentials. |
| 35 /// |
| 36 /// This may be `null`, indicating that the credentials can't be refreshed. |
34 final String refreshToken; | 37 final String refreshToken; |
35 | 38 |
36 /// The URL of the authorization server endpoint that's used to refresh the | 39 /// The URL of the authorization server endpoint that's used to refresh the |
37 /// credentials. This is optional. | 40 /// credentials. |
| 41 /// |
| 42 /// This may be `null`, indicating that the credentials can't be refreshed. |
38 final Uri tokenEndpoint; | 43 final Uri tokenEndpoint; |
39 | 44 |
40 /// The specific permissions being requested from the authorization server. | 45 /// The specific permissions being requested from the authorization server. |
| 46 /// |
41 /// The scope strings are specific to the authorization server and may be | 47 /// The scope strings are specific to the authorization server and may be |
42 /// found in its documentation. | 48 /// found in its documentation. |
43 final List<String> scopes; | 49 final List<String> scopes; |
44 | 50 |
45 /// The date at which these credentials will expire. This is likely to be a | 51 /// The date at which these credentials will expire. |
46 /// few seconds earlier than the server's idea of the expiration date. | 52 /// |
| 53 /// This is likely to be a few seconds earlier than the server's idea of the |
| 54 /// expiration date. |
47 final DateTime expiration; | 55 final DateTime expiration; |
48 | 56 |
49 /// Whether or not these credentials have expired. Note that it's possible the | 57 /// Whether or not these credentials have expired. |
50 /// credentials will expire shortly after this is called. However, since the | 58 /// |
51 /// client's expiration date is kept a few seconds earlier than the server's, | 59 /// Note that it's possible the credentials will expire shortly after this is |
52 /// there should be enough leeway to rely on this. | 60 /// called. However, since the client's expiration date is kept a few seconds |
| 61 /// earlier than the server's, there should be enough leeway to rely on this. |
53 bool get isExpired => expiration != null && | 62 bool get isExpired => expiration != null && |
54 new DateTime.now().isAfter(expiration); | 63 new DateTime.now().isAfter(expiration); |
55 | 64 |
56 /// Whether it's possible to refresh these credentials. | 65 /// Whether it's possible to refresh these credentials. |
57 bool get canRefresh => refreshToken != null && tokenEndpoint != null; | 66 bool get canRefresh => refreshToken != null && tokenEndpoint != null; |
58 | 67 |
59 /// Creates a new set of credentials. | 68 /// Creates a new set of credentials. |
60 /// | 69 /// |
61 /// This class is usually not constructed directly; rather, it's accessed via | 70 /// This class is usually not constructed directly; rather, it's accessed via |
62 /// [Client.credentials] after a [Client] is created by | 71 /// [Client.credentials] after a [Client] is created by |
63 /// [AuthorizationCodeGrant]. Alternately, it may be loaded from a serialized | 72 /// [AuthorizationCodeGrant]. Alternately, it may be loaded from a serialized |
64 /// form via [Credentials.fromJson]. | 73 /// form via [Credentials.fromJson]. |
65 Credentials( | 74 Credentials( |
66 this.accessToken, | 75 this.accessToken, |
67 [this.refreshToken, | 76 [this.refreshToken, |
68 this.tokenEndpoint, | 77 this.tokenEndpoint, |
69 this.scopes, | 78 this.scopes, |
70 this.expiration]); | 79 this.expiration]); |
71 | 80 |
72 /// Loads a set of credentials from a JSON-serialized form. Throws | 81 /// Loads a set of credentials from a JSON-serialized form. |
73 /// [FormatException] if the JSON is incorrectly formatted. | 82 /// |
| 83 /// Throws a [FormatException] if the JSON is incorrectly formatted. |
74 factory Credentials.fromJson(String json) { | 84 factory Credentials.fromJson(String json) { |
75 void validate(bool condition, String message) { | 85 void validate(bool condition, String message) { |
76 if (condition) return; | 86 if (condition) return; |
77 throw new FormatException( | 87 throw new FormatException( |
78 "Failed to load credentials: $message.\n\n$json"); | 88 "Failed to load credentials: $message.\n\n$json"); |
79 } | 89 } |
80 | 90 |
81 var parsed; | 91 var parsed; |
82 try { | 92 try { |
83 parsed = JSON.decode(json); | 93 parsed = JSON.decode(json); |
84 } on FormatException catch (e) { | 94 } on FormatException catch (_) { |
85 validate(false, 'invalid JSON'); | 95 validate(false, 'invalid JSON'); |
86 } | 96 } |
87 | 97 |
88 validate(parsed is Map, 'was not a JSON map'); | 98 validate(parsed is Map, 'was not a JSON map'); |
89 validate(parsed.containsKey('accessToken'), | 99 validate(parsed.containsKey('accessToken'), |
90 'did not contain required field "accessToken"'); | 100 'did not contain required field "accessToken"'); |
91 validate(parsed['accessToken'] is String, | 101 validate(parsed['accessToken'] is String, |
92 'required field "accessToken" was not a string, was ' | 102 'required field "accessToken" was not a string, was ' |
93 '${parsed["accessToken"]}'); | 103 '${parsed["accessToken"]}'); |
94 | 104 |
(...skipping 20 matching lines...) Expand all Loading... |
115 } | 125 } |
116 | 126 |
117 return new Credentials( | 127 return new Credentials( |
118 parsed['accessToken'], | 128 parsed['accessToken'], |
119 parsed['refreshToken'], | 129 parsed['refreshToken'], |
120 tokenEndpoint, | 130 tokenEndpoint, |
121 scopes, | 131 scopes, |
122 expiration); | 132 expiration); |
123 } | 133 } |
124 | 134 |
125 /// Serializes a set of credentials to JSON. Nothing is guaranteed about the | 135 /// Serializes a set of credentials to JSON. |
126 /// output except that it's valid JSON and compatible with | 136 /// |
127 /// [Credentials.toJson]. | 137 /// Nothing is guaranteed about the output except that it's valid JSON and |
| 138 /// compatible with [Credentials.toJson]. |
128 String toJson() => JSON.encode({ | 139 String toJson() => JSON.encode({ |
129 'accessToken': accessToken, | 140 'accessToken': accessToken, |
130 'refreshToken': refreshToken, | 141 'refreshToken': refreshToken, |
131 'tokenEndpoint': tokenEndpoint == null ? null : tokenEndpoint.toString(), | 142 'tokenEndpoint': tokenEndpoint == null ? null : tokenEndpoint.toString(), |
132 'scopes': scopes, | 143 'scopes': scopes, |
133 'expiration': expiration == null ? null : expiration.millisecondsSinceEpoch | 144 'expiration': expiration == null ? null : expiration.millisecondsSinceEpoch |
134 }); | 145 }); |
135 | 146 |
136 /// Returns a new set of refreshed credentials. See [Client.identifier] and | 147 /// Returns a new set of refreshed credentials. |
137 /// [Client.secret] for explanations of those parameters. | 148 /// |
| 149 /// See [Client.identifier] and [Client.secret] for explanations of those |
| 150 /// parameters. |
138 /// | 151 /// |
139 /// You may request different scopes than the default by passing in | 152 /// You may request different scopes than the default by passing in |
140 /// [newScopes]. These must be a subset of [scopes]. | 153 /// [newScopes]. These must be a subset of [scopes]. |
141 /// | 154 /// |
142 /// This will throw a [StateError] if these credentials can't be refreshed, an | 155 /// This will throw a [StateError] if these credentials can't be refreshed, an |
143 /// [AuthorizationException] if refreshing the credentials fails, or a | 156 /// [AuthorizationException] if refreshing the credentials fails, or a |
144 /// [FormatError] if the authorization server returns invalid responses. | 157 /// [FormatError] if the authorization server returns invalid responses. |
145 Future<Credentials> refresh( | 158 Future<Credentials> refresh( |
146 String identifier, | 159 String identifier, |
147 String secret, | 160 String secret, |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
180 // we should re-use the one we already have. | 193 // we should re-use the one we already have. |
181 if (credentials.refreshToken != null) return credentials; | 194 if (credentials.refreshToken != null) return credentials; |
182 return new Credentials( | 195 return new Credentials( |
183 credentials.accessToken, | 196 credentials.accessToken, |
184 this.refreshToken, | 197 this.refreshToken, |
185 credentials.tokenEndpoint, | 198 credentials.tokenEndpoint, |
186 credentials.scopes, | 199 credentials.scopes, |
187 credentials.expiration); | 200 credentials.expiration); |
188 } | 201 } |
189 } | 202 } |
OLD | NEW |