Left: | ||
Right: |
LEFT | RIGHT |
---|---|
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 |
(...skipping 17 matching lines...) Expand all Loading... | |
28 { | 28 { |
29 /// <summary> | 29 /// <summary> |
30 /// OAuth 2.0 credential for accessing protected resources using an access t oken, as well as optionally refreshing· | 30 /// OAuth 2.0 credential for accessing protected resources using an access t oken, as well as optionally refreshing· |
31 /// the access token when it expires using a refresh token. | 31 /// the access token when it expires using a refresh token. |
32 /// </summary> | 32 /// </summary> |
33 public class Credential : IHttpExecuteInterceptor, IHttpUnsuccessfulResponse Handler, | 33 public class Credential : IHttpExecuteInterceptor, IHttpUnsuccessfulResponse Handler, |
34 IConfigurableHttpClientInitializer | 34 IConfigurableHttpClientInitializer |
35 { | 35 { |
36 private static readonly ILogger Logger = ApplicationContext.Logger.ForTy pe<Credential>(); | 36 private static readonly ILogger Logger = ApplicationContext.Logger.ForTy pe<Credential>(); |
37 | 37 |
38 private readonly TokenResponse token; | 38 private TokenResponse token; |
39 private object lockObject = new object(); | |
40 | |
41 public TokenResponse Token | |
42 { | |
43 get | |
44 { | |
45 lock (lockObject) | |
46 { | |
47 return token; | |
48 } | |
49 } | |
50 private set | |
51 { | |
52 lock (lockObject) | |
53 { | |
54 token = value; | |
55 } | |
56 } | |
57 } | |
58 | |
39 private readonly IAuthorizationCodeFlow flow; | 59 private readonly IAuthorizationCodeFlow flow; |
40 private readonly string userId; | 60 private readonly string userId; |
41 | 61 |
42 /// <summary>Constructs a new credential instance.</summary> | 62 /// <summary>Constructs a new credential instance.</summary> |
43 /// <param name="flow">Authorization code flow</param> | 63 /// <param name="flow">Authorization code flow</param> |
44 /// <param name="userId">User identifier</param> | 64 /// <param name="userId">User identifier</param> |
45 /// <param name="token">An initial token for the user</param> | 65 /// <param name="token">An initial token for the user</param> |
46 public Credential(IAuthorizationCodeFlow flow, string userId, TokenRespo nse token) | 66 public Credential(IAuthorizationCodeFlow flow, string userId, TokenRespo nse token) |
47 { | 67 { |
48 this.flow = flow; | 68 this.flow = flow; |
69 this.userId = userId; | |
49 this.token = token; | 70 this.token = token; |
50 this.userId = userId; | |
51 } | 71 } |
52 | 72 |
53 /// <summary> | 73 /// <summary> |
54 /// Default implementation is to try to refresh the access token if ther e is no access token or if we are 1· | 74 /// Default implementation is to try to refresh the access token if ther e is no access token or if we are 1· |
55 /// minute away from expiration. If token server is unavailable, it will try to use the access token even if· | 75 /// minute away from expiration. If token server is unavailable, it will try to use the access token even if· |
56 /// has expired. If successful, it will call <seealso cref="IAccessMetho d.Intercept"/>. | 76 /// has expired. If successful, it will call <seealso cref="IAccessMetho d.Intercept"/>. |
57 /// </summary> | 77 /// </summary> |
58 public async Task InterceptAsync(HttpRequestMessage request, Cancellatio nToken taskCancellationToken) | 78 public async Task InterceptAsync(HttpRequestMessage request, Cancellatio nToken taskCancellationToken) |
59 { | 79 { |
60 if (token.IsExpired(flow.Clock)) | 80 if (Token.IsExpired(flow.Clock)) |
61 { | 81 { |
62 if (!await RefreshTokenAsync(taskCancellationToken).ConfigureAwa it(false)) | 82 if (!await RefreshTokenAsync(taskCancellationToken).ConfigureAwa it(false)) |
63 { | 83 { |
64 throw new InvalidOperationException("The access token is exp ired but we can't refresh it"); | 84 throw new InvalidOperationException("The access token is exp ired but we can't refresh it"); |
65 } | 85 } |
66 } | 86 } |
67 | 87 |
68 flow.AccessMethod.Intercept(request, token.AccessToken); | 88 flow.AccessMethod.Intercept(request, Token.AccessToken); |
69 } | 89 } |
70 | 90 |
71 /// <summary> | 91 /// <summary> |
72 /// Refreshes the token by calling to <seealso cref="IAuthorizationCodeF low.RefreshTokenAsync"/>. Then it· | 92 /// Refreshes the token by calling to <seealso cref="IAuthorizationCodeF low.RefreshTokenAsync"/>. Then it· |
73 /// updates the <see cref="TokenResponse"/> with the new token instance. | 93 /// updates the <see cref="TokenResponse"/> with the new token instance. |
74 /// </summary> | 94 /// </summary> |
75 /// <param name="taskCancellationToken">Cancellation token to cancel an operation</param> | 95 /// <param name="taskCancellationToken">Cancellation token to cancel an operation</param> |
76 /// <returns><c>true</c> if the token was refreshed</returns> | 96 /// <returns><c>true</c> if the token was refreshed</returns> |
77 private async Task<bool> RefreshTokenAsync(CancellationToken taskCancell ationToken) | 97 private async Task<bool> RefreshTokenAsync(CancellationToken taskCancell ationToken) |
78 { | 98 { |
79 if (token.RefreshToken == null) | 99 if (Token.RefreshToken == null) |
80 { | 100 { |
81 Logger.Warning("Refresh token is null, can't refresh the token!" ); | 101 Logger.Warning("Refresh token is null, can't refresh the token!" ); |
82 return false; | 102 return false; |
83 } | 103 } |
84 | 104 |
85 // It's possible that two concurrent calls will be made to refresh t he token, in that case the last one· | 105 // It's possible that two concurrent calls will be made to refresh t he token, in that case the last one· |
86 // will win. | 106 // will win. |
87 var newToken = await flow.RefreshTokenAsync(userId, token.RefreshTok en, taskCancellationToken) | 107 var newToken = await flow.RefreshTokenAsync(userId, Token.RefreshTok en, taskCancellationToken) |
88 .ConfigureAwait(false); | 108 .ConfigureAwait(false); |
89 | 109 |
90 Logger.Info("Access token was refreshed"); | 110 Logger.Info("Access token was refreshed"); |
91 | 111 |
92 token.CopyFrom(newToken); | 112 if (newToken.RefreshToken == null) |
peleyal
2013/09/27 14:32:33
What is your recommendation here?
Do you think tha
jonskeet
2013/09/27 15:15:25
Possibly. I think it makes more sense for the toke
peleyal
2013/09/27 18:41:48
Done.
| |
113 { | |
114 newToken.RefreshToken = Token.RefreshToken; | |
115 } | |
93 | 116 |
117 Token = newToken; | |
94 return true; | 118 return true; |
95 } | 119 } |
96 | 120 |
97 public async Task<bool> HandleResponseAsync(HandleUnsuccessfulResponseAr gs args) | 121 public async Task<bool> HandleResponseAsync(HandleUnsuccessfulResponseAr gs args) |
98 { | 122 { |
99 // TODO(peleyal): check WWW-Authenticate header | 123 // TODO(peleyal): check WWW-Authenticate header |
100 if (args.Response.StatusCode == HttpStatusCode.Unauthorized) | 124 if (args.Response.StatusCode == HttpStatusCode.Unauthorized) |
101 { | 125 { |
102 return !Object.Equals(token.AccessToken, flow.AccessMethod.GetAc cessToken(args.Request)) | 126 return !Object.Equals(Token.AccessToken, flow.AccessMethod.GetAc cessToken(args.Request)) |
103 || await RefreshTokenAsync(args.CancellationToken).Configure Await(false); | 127 || await RefreshTokenAsync(args.CancellationToken).Configure Await(false); |
104 } | 128 } |
105 | 129 |
106 return false; | 130 return false; |
107 } | 131 } |
108 | 132 |
109 public void Initialize(ConfigurableHttpClient httpClient) | 133 public void Initialize(ConfigurableHttpClient httpClient) |
110 { | 134 { |
111 httpClient.MessageHandler.ExecuteInterceptors.Add(this); | 135 httpClient.MessageHandler.ExecuteInterceptors.Add(this); |
112 httpClient.MessageHandler.UnsuccessfulResponseHandlers.Add(this); | 136 httpClient.MessageHandler.UnsuccessfulResponseHandlers.Add(this); |
113 } | 137 } |
114 } | 138 } |
115 } | 139 } |
LEFT | RIGHT |