Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(5150)

Side by Side Diff: Src/GoogleApis.Auth.DotNet4/OAuth2/ServiceAccountCredential.cs

Issue 181560043: ComputeCredential (Closed) Base URL: https://google-api-dotnet-client.googlecode.com/hg/
Patch Set: reverting changes to Program release Created 9 years, 6 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | Src/GoogleApis.Auth/GoogleApis.Auth.csproj » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 Copyright 2013 Google Inc 2 Copyright 2013 Google Inc
3 3
4 Licensed under the Apache License, Version 2.0 (the "License"); 4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License. 5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at 6 You may obtain a copy of the License at
7 7
8 http://www.apache.org/licenses/LICENSE-2.0 8 http://www.apache.org/licenses/LICENSE-2.0
9 9
10 Unless required by applicable law or agreed to in writing, software 10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS, 11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and 13 See the License for the specific language governing permissions and
14 limitations under the License. 14 limitations under the License.
15 */ 15 */
16 16
17 using System; 17 using System;
18 using System.Collections.Generic; 18 using System.Collections.Generic;
19 using System.Net;
20 using System.Net.Http;
21 using System.Security.Cryptography; 19 using System.Security.Cryptography;
22 using System.Security.Cryptography.X509Certificates; 20 using System.Security.Cryptography.X509Certificates;
23 using System.Text; 21 using System.Text;
24 using System.Threading; 22 using System.Threading;
25 using System.Threading.Tasks; 23 using System.Threading.Tasks;
26 24
27 using Google.Apis.Auth.OAuth2.Requests; 25 using Google.Apis.Auth.OAuth2.Requests;
28 using Google.Apis.Auth.OAuth2.Responses;
29 using Google.Apis.Http;
30 using Google.Apis.Json; 26 using Google.Apis.Json;
31 using Google.Apis.Logging; 27 using Google.Apis.Logging;
32 using Google.Apis.Util; 28 using Google.Apis.Util;
33 29
34 namespace Google.Apis.Auth.OAuth2 30 namespace Google.Apis.Auth.OAuth2
35 { 31 {
36 /// <summary> 32 /// <summary>
37 /// Google OAuth 2.0 credential for accessing protected resources using an a ccess token. The Google OAuth 2.0· 33 /// Google OAuth 2.0 credential for accessing protected resources using an a ccess token. The Google OAuth 2.0·
38 /// Authorization Server supports server-to-server interactions such as thos e between a web application and Google 34 /// Authorization Server supports server-to-server interactions such as thos e between a web application and Google
39 /// Cloud Storage. The requesting application has to prove its own identity to gain access to an API, and an· 35 /// Cloud Storage. The requesting application has to prove its own identity to gain access to an API, and an·
40 /// end-user doesn't have to be involved.· 36 /// end-user doesn't have to be involved.·
41 /// <para> 37 /// <para>
42 /// Take a look in https://developers.google.com/accounts/docs/OAuth2Service Account for more details. 38 /// Take a look in https://developers.google.com/accounts/docs/OAuth2Service Account for more details.
43 /// </para> 39 /// </para>
44 /// </summary> 40 /// </summary>
45 public class ServiceAccountCredential : IHttpExecuteInterceptor, IHttpUnsucc essfulResponseHandler, 41 public class ServiceAccountCredential : ServiceCredential
46 IConfigurableHttpClientInitializer
47 { 42 {
48 private static readonly ILogger Logger = ApplicationContext.Logger.ForTy pe<ServiceAccountCredential>();
49
50 /// <summary>An initializer class for the service account credential. </ summary> 43 /// <summary>An initializer class for the service account credential. </ summary>
51 public class Initializer 44 public class Initializer : ServiceCredential.Initializer
52 { 45 {
53 /// <summary>Gets the service account ID (typically an e-mail addres s).</summary> 46 /// <summary>Gets the service account ID (typically an e-mail addres s).</summary>
54 public string Id { get; private set; } 47 public string Id { get; private set; }
55 48
56 /// <summary>Gets the token server URL.</summary>
57 public string TokenServerUrl { get; private set; }
58
59 /// <summary> 49 /// <summary>
60 /// Gets or sets the email address of the user the application is tr ying to impersonate in the service· 50 /// Gets or sets the email address of the user the application is tr ying to impersonate in the service·
61 /// account flow or <c>null</c>. 51 /// account flow or <c>null</c>.
62 /// </summary> 52 /// </summary>
63 public string User { get; set; } 53 public string User { get; set; }
64 54
65 /// <summary>Gets the scopes which indicate API access your applicat ion is requesting.</summary> 55 /// <summary>Gets the scopes which indicate API access your applicat ion is requesting.</summary>
66 public IEnumerable<string> Scopes { get; set; } 56 public IEnumerable<string> Scopes { get; set; }
67 57
68 /// <summary> 58 /// <summary>
69 /// Gets or sets the clock. The clock is used to determine if the to ken has expired, if so we will try to
70 /// refresh it. The default value is <see cref="Google.Apis.Util.Sys temClock.Default"/>.
71 /// </summary>
72 public IClock Clock { get; set; }
73
74 /// <summary>
75 /// Gets or sets the key which is used to sign the request, as speci fied in 59 /// Gets or sets the key which is used to sign the request, as speci fied in
76 /// https://developers.google.com/accounts/docs/OAuth2ServiceAccount #computingsignature. 60 /// https://developers.google.com/accounts/docs/OAuth2ServiceAccount #computingsignature.
77 /// </summary> 61 /// </summary>
78 public RSACryptoServiceProvider Key { get; set; } 62 public RSACryptoServiceProvider Key { get; set; }
79 63
80 /// <summary>
81 /// Gets or sets the method for presenting the access token to the r esource server.
82 /// The default value is <see cref="BearerToken.AuthorizationHeaderA ccessMethod"/>.
83 /// </summary>
84 public IAccessMethod AccessMethod { get; set; }
85
86 /// <summary>
87 /// Gets or sets the factory for creating a <see cref="System.Net.Ht tp.HttpClient"/> instance.
88 /// </summary>
89 public IHttpClientFactory HttpClientFactory { get; set; }
90
91 /// <summary>
92 /// Get or sets the exponential back-off policy. Default value is < c>UnsuccessfulResponse503</c>, which·
93 /// means that exponential back-off is used on 503 abnormal HTTP res ponses.
94 /// If the value is set to <c>None</c>, no exponential back-off poli cy is used, and it's up to the user to
95 /// configure the <see cref="Google.Apis.Http.ConfigurableMessageHan dler"/> in an
96 /// <see cref="Google.Apis.Http.IConfigurableHttpClientInitializer"/ > to set a specific back-off
97 /// implementation (using <see cref="Google.Apis.Http.BackOffHandler "/>).
98 /// </summary>
99 public ExponentialBackOffPolicy DefaultExponentialBackOffPolicy { ge t; set; }
100
101 /// <summary>Constructs a new initializer using the given id.</summa ry> 64 /// <summary>Constructs a new initializer using the given id.</summa ry>
102 public Initializer(string id) 65 public Initializer(string id)
103 : this(id, GoogleAuthConsts.TokenUrl) { } 66 : this(id, GoogleAuthConsts.TokenUrl) { }
104 67
105 /// <summary>Constructs a new initializer using the given id and the token server URL.</summary> 68 /// <summary>Constructs a new initializer using the given id and the token server URL.</summary>
106 public Initializer(string id, string tokenServerUrl) 69 public Initializer(string id, string tokenServerUrl) : base(tokenSer verUrl)
107 { 70 {
108 Id = id; 71 Id = id;
109 TokenServerUrl = tokenServerUrl;
110
111 AccessMethod = new BearerToken.AuthorizationHeaderAccessMethod() ;
112 Clock = SystemClock.Default;
113 DefaultExponentialBackOffPolicy = ExponentialBackOffPolicy.Unsuc cessfulResponse503;
114 Scopes = new List<string>(); 72 Scopes = new List<string>();
115 } 73 }
116 74
117 /// <summary>Extracts a <see cref="Key"/> from the given certificate .</summary> 75 /// <summary>Extracts a <see cref="Key"/> from the given certificate .</summary>
118 public Initializer FromCertificate(X509Certificate2 certificate) 76 public Initializer FromCertificate(X509Certificate2 certificate)
119 { 77 {
120 // Workaround to correctly cast the private key as a RSACryptoSe rviceProvider type 24. 78 // Workaround to correctly cast the private key as a RSACryptoSe rviceProvider type 24.
121 RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)certifi cate.PrivateKey; 79 RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)certifi cate.PrivateKey;
122 byte[] privateKeyBlob = rsa.ExportCspBlob(true); 80 byte[] privateKeyBlob = rsa.ExportCspBlob(true);
123 Key = new RSACryptoServiceProvider(); 81 Key = new RSACryptoServiceProvider();
124 Key.ImportCspBlob(privateKeyBlob); 82 Key.ImportCspBlob(privateKeyBlob);
125 return this; 83 return this;
126 } 84 }
127 } 85 }
128 86
129 #region Readonly fields 87 #region Readonly fields
130 88
131 private readonly string id; 89 private readonly string id;
132 private readonly string tokenServerUrl;
133 private readonly string user; 90 private readonly string user;
134 private readonly IEnumerable<string> scopes; 91 private readonly IEnumerable<string> scopes;
135 private readonly IClock clock;
136 private readonly IAccessMethod accessMethod;
137 private readonly ConfigurableHttpClient httpClient;
138 private readonly RSACryptoServiceProvider key; 92 private readonly RSACryptoServiceProvider key;
139 93
140 #endregion 94 #endregion
141 95
142 /// <summary>Gets the service account ID (typically an e-mail address).< /summary> 96 /// <summary>Gets the service account ID (typically an e-mail address).< /summary>
143 public string Id { get { return id; } } 97 public string Id { get { return id; } }
144 98
145 /// <summary>Gets the token server URL.</summary>
146 public string TokenServerUrl { get { return tokenServerUrl; } }
147
148 /// <summary> 99 /// <summary>
149 /// Gets the email address of the user the application is trying to impe rsonate in the service account flow· 100 /// Gets the email address of the user the application is trying to impe rsonate in the service account flow·
150 /// or <c>null</c>. 101 /// or <c>null</c>.
151 /// </summary> 102 /// </summary>
152 public string User { get { return user; } } 103 public string User { get { return user; } }
153 104
154 /// <summary>Gets the service account scopes.</summary> 105 /// <summary>Gets the service account scopes.</summary>
155 public IEnumerable<string> Scopes { get { return scopes; } } 106 public IEnumerable<string> Scopes { get { return scopes; } }
156 107
157 /// <summary> 108 /// <summary>
158 /// Gets the clock. The clock is used to determine if the token has expi red, if so we will try to refresh it.·
159 /// </summary>
160 public IClock Clock { get { return clock; } }
161
162 /// <summary>Gets the method for presenting the access token to the reso urce server.</summary>
163 public IAccessMethod AccessMethod { get { return accessMethod; } }
164
165 /// <summary>Gets the HTTP client used to make authentication requests t o the server.</summary>
166 public ConfigurableHttpClient HttpClient { get { return httpClient; } }
167
168 /// <summary>
169 /// Gets the key which is used to sign the request, as specified in 109 /// Gets the key which is used to sign the request, as specified in
170 /// https://developers.google.com/accounts/docs/OAuth2ServiceAccount#com putingsignature. 110 /// https://developers.google.com/accounts/docs/OAuth2ServiceAccount#com putingsignature.
171 /// </summary> 111 /// </summary>
172 public RSACryptoServiceProvider Key { get { return key; } } 112 public RSACryptoServiceProvider Key { get { return key; } }
173 113
174 private TokenResponse token;
175 private object lockObject = new object();
176
177 /// <summary>Gets the token response which contains the access token.</s ummary>
178 public TokenResponse Token
179 {
180 get
181 {
182 lock (lockObject)
183 {
184 return token;
185 }
186 }
187 private set
188 {
189 lock (lockObject)
190 {
191 token = value;
192 }
193 }
194 }
195
196 /// <summary>Constructs a new service account credential using the given initializer.</summary> 114 /// <summary>Constructs a new service account credential using the given initializer.</summary>
197 /// <param name="initializer"></param> 115 /// <param name="initializer"></param>
198 public ServiceAccountCredential(Initializer initializer) 116 public ServiceAccountCredential(Initializer initializer) : base(initiali zer)
199 { 117 {
200 id = initializer.Id.ThrowIfNullOrEmpty("initializer.Id"); 118 id = initializer.Id.ThrowIfNullOrEmpty("initializer.Id");
201 user = initializer.User; 119 user = initializer.User;
202 scopes = initializer.Scopes; 120 scopes = initializer.Scopes;
203 tokenServerUrl = initializer.TokenServerUrl;
204 accessMethod = initializer.AccessMethod.ThrowIfNull("initializer.Acc essMethod");
205 clock = initializer.Clock.ThrowIfNull("initializer.Clock");
206
207 // Set the HTTP client.
208 var httpArgs = new CreateHttpClientArgs();
209
210 // Add exponential back-off initializer if necessary.
211 if (initializer.DefaultExponentialBackOffPolicy != ExponentialBackOf fPolicy.None)
212 {
213 httpArgs.Initializers.Add(
214 new ExponentialBackOffInitializer(initializer.DefaultExponen tialBackOffPolicy,
215 () => new BackOffHandler(new ExponentialBackOff())));
216 }
217 httpClient = (initializer.HttpClientFactory ?? new HttpClientFactory ()).CreateHttpClient(httpArgs);
218 key = initializer.Key.ThrowIfNull("initializer.Key"); 121 key = initializer.Key.ThrowIfNull("initializer.Key");
219 } 122 }
220 123
221 public void Initialize(ConfigurableHttpClient httpClient) 124 #region ServiceCredential overrides
222 {
223 httpClient.MessageHandler.ExecuteInterceptors.Add(this);
224 httpClient.MessageHandler.UnsuccessfulResponseHandlers.Add(this);
225 }
226
227 public async Task InterceptAsync(HttpRequestMessage request, Cancellatio nToken cancellationToken)
228 {
229 if (Token == null || Token.IsExpired(Clock))
230 {
231 Logger.Debug("Token has expired, trying to get a new one.");
232 if (!await RequestAccessTokenAsync(cancellationToken).ConfigureA wait(false))
233 {
234 throw new InvalidOperationException("The access token has ex pired but we can't refresh it");
235 }
236 Logger.Info("New access token was received successfully");
237 }
238
239 AccessMethod.Intercept(request, Token.AccessToken);
240 }
241
242 public async Task<bool> HandleResponseAsync(HandleUnsuccessfulResponseAr gs args)
243 {
244 // TODO(peleyal): check WWW-Authenticate header.
245 if (args.Response.StatusCode == HttpStatusCode.Unauthorized)
246 {
247 return !Object.Equals(Token.AccessToken, AccessMethod.GetAccessT oken(args.Request))
248 || await RequestAccessTokenAsync(args.CancellationToken).Con figureAwait(false);
249 }
250
251 return false;
252 }
253 125
254 /// <summary> 126 /// <summary>
255 /// Requests a new token as specified in· 127 /// Requests a new token as specified in·
256 /// https://developers.google.com/accounts/docs/OAuth2ServiceAccount#mak ingrequest. 128 /// https://developers.google.com/accounts/docs/OAuth2ServiceAccount#mak ingrequest.
257 /// </summary> 129 /// </summary>
258 /// <param name="taskCancellationToken">Cancellation token to cancel ope ration.</param> 130 /// <param name="taskCancellationToken">Cancellation token to cancel ope ration.</param>
259 /// <returns><c>true</c> if a new token was received successfully.</retu rns> 131 /// <returns><c>true</c> if a new token was received successfully.</retu rns>
260 public async Task<bool> RequestAccessTokenAsync(CancellationToken taskCa ncellationToken) 132 public override async Task<bool> RequestAccessTokenAsync(CancellationTok en taskCancellationToken)
261 { 133 {
262 string serializedHeader = CreateSerializedHeader(); 134 string serializedHeader = CreateSerializedHeader();
263 string serializedPayload = GetSerializedPayload(); 135 string serializedPayload = GetSerializedPayload();
264 136
265 StringBuilder assertion = new StringBuilder(); 137 StringBuilder assertion = new StringBuilder();
266 assertion.Append(UrlSafeBase64Encode(serializedHeader)) 138 assertion.Append(UrlSafeBase64Encode(serializedHeader))
267 .Append(".") 139 .Append(".")
268 .Append(UrlSafeBase64Encode(serializedPayload)); 140 .Append(UrlSafeBase64Encode(serializedPayload));
269 141
270 // Sign the header and the payload. 142 // Sign the header and the payload.
271 var signature = UrlSafeBase64Encode(key.SignData(Encoding.ASCII.GetB ytes(assertion.ToString()), "SHA256")); 143 var signature = UrlSafeBase64Encode(key.SignData(Encoding.ASCII.GetB ytes(assertion.ToString()), "SHA256"));
272 assertion.Append(".").Append(signature); 144 assertion.Append(".").Append(signature);
273 145
274 // Create the request. 146 // Create the request.
275 var request = new GoogleAssertionTokenRequest() 147 var request = new GoogleAssertionTokenRequest()
276 { 148 {
277 Assertion = assertion.ToString() 149 Assertion = assertion.ToString()
278 }; 150 };
279 151
280 Logger.Debug("Request a new access token. Assertion data is: " + req uest.Assertion); 152 Logger.Debug("Request a new access token. Assertion data is: " + req uest.Assertion);
281 153
282 var newToken = await request.ExecuteAsync(httpClient, tokenServerUrl , taskCancellationToken, Clock) 154 var newToken = await request.ExecuteAsync(HttpClient, TokenServerUrl , taskCancellationToken, Clock)
283 .ConfigureAwait(false); 155 .ConfigureAwait(false);
284 Token = newToken; 156 Token = newToken;
285 return true; 157 return true;
286 } 158 }
287 159
160 #endregion
161
288 /// <summary> 162 /// <summary>
289 /// Creates a serialized header as specified in· 163 /// Creates a serialized header as specified in·
290 /// https://developers.google.com/accounts/docs/OAuth2ServiceAccount#for mingheader. 164 /// https://developers.google.com/accounts/docs/OAuth2ServiceAccount#for mingheader.
291 /// </summary> 165 /// </summary>
292 private static string CreateSerializedHeader() 166 private static string CreateSerializedHeader()
293 { 167 {
294 var header = new GoogleJsonWebSignature.Header() 168 var header = new GoogleJsonWebSignature.Header()
295 { 169 {
296 Algorithm = "RS256", 170 Algorithm = "RS256",
297 Type = "JWT" 171 Type = "JWT"
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
330 204
331 /// <summary>Encodes the byte array into an URL safe base64 string.</sum mary> 205 /// <summary>Encodes the byte array into an URL safe base64 string.</sum mary>
332 /// <param name="bytes">Byte array to encode.</param> 206 /// <param name="bytes">Byte array to encode.</param>
333 /// <returns>The URL safe base64 string.</returns> 207 /// <returns>The URL safe base64 string.</returns>
334 private string UrlSafeBase64Encode(byte[] bytes) 208 private string UrlSafeBase64Encode(byte[] bytes)
335 { 209 {
336 return Convert.ToBase64String(bytes).Replace("=", String.Empty).Repl ace('+', '-').Replace('/', '_'); 210 return Convert.ToBase64String(bytes).Replace("=", String.Empty).Repl ace('+', '-').Replace('/', '_');
337 } 211 }
338 } 212 }
339 } 213 }
OLDNEW
« no previous file with comments | « no previous file | Src/GoogleApis.Auth/GoogleApis.Auth.csproj » ('j') | no next file with comments »

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b