OLD | NEW |
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 16 matching lines...) Expand all Loading... |
27 | 27 |
28 namespace Google.Apis.Auth.OAuth2 | 28 namespace Google.Apis.Auth.OAuth2 |
29 { | 29 { |
30 /// <summary> | 30 /// <summary> |
31 /// OAuth 2.0 credential for accessing protected resources using an access t
oken, as well as optionally refreshing· | 31 /// OAuth 2.0 credential for accessing protected resources using an access t
oken, as well as optionally refreshing· |
32 /// the access token when it expires using a refresh token. | 32 /// the access token when it expires using a refresh token. |
33 /// </summary> | 33 /// </summary> |
34 public class UserCredential : IHttpExecuteInterceptor, IHttpUnsuccessfulResp
onseHandler, | 34 public class UserCredential : IHttpExecuteInterceptor, IHttpUnsuccessfulResp
onseHandler, |
35 IConfigurableHttpClientInitializer | 35 IConfigurableHttpClientInitializer |
36 { | 36 { |
37 private static readonly ILogger Logger = ApplicationContext.Logger.ForTy
pe<UserCredential>(); | 37 protected static readonly ILogger Logger = ApplicationContext.Logger.For
Type<UserCredential>(); |
38 | 38 |
39 private TokenResponse token; | 39 private TokenResponse token; |
40 private object lockObject = new object(); | 40 private object lockObject = new object(); |
41 | 41 |
42 /// <summary>Gets the token response which contains the access token.</s
ummary> | 42 /// <summary>Gets or sets the token response which contains the access t
oken.</summary> |
43 public TokenResponse Token | 43 public TokenResponse Token |
44 { | 44 { |
45 get | 45 get |
46 { | 46 { |
47 lock (lockObject) | 47 lock (lockObject) |
48 { | 48 { |
49 return token; | 49 return token; |
50 } | 50 } |
51 } | 51 } |
52 private set | 52 set |
53 { | 53 { |
54 lock (lockObject) | 54 lock (lockObject) |
55 { | 55 { |
56 token = value; | 56 token = value; |
57 } | 57 } |
58 } | 58 } |
59 } | 59 } |
60 | 60 |
| 61 /// <summary>Gets the authorization code flow.</summary> |
| 62 public IAuthorizationCodeFlow Flow |
| 63 { |
| 64 get { return flow; } |
| 65 } |
| 66 |
| 67 /// <summary>Gets the user identity.</summary> |
| 68 public string UderId |
| 69 { |
| 70 get { return userId; } |
| 71 } |
| 72 |
61 private readonly IAuthorizationCodeFlow flow; | 73 private readonly IAuthorizationCodeFlow flow; |
62 private readonly string userId; | 74 private readonly string userId; |
63 | 75 |
64 /// <summary>Constructs a new credential instance.</summary> | 76 /// <summary>Constructs a new credential instance.</summary> |
65 /// <param name="flow">Authorization code flow.</param> | 77 /// <param name="flow">Authorization code flow.</param> |
66 /// <param name="userId">User identifier.</param> | 78 /// <param name="userId">User identifier.</param> |
67 /// <param name="token">An initial token for the user.</param> | 79 /// <param name="token">An initial token for the user.</param> |
68 public UserCredential(IAuthorizationCodeFlow flow, string userId, TokenR
esponse token) | 80 public UserCredential(IAuthorizationCodeFlow flow, string userId, TokenR
esponse token) |
69 { | 81 { |
70 this.flow = flow; | 82 this.flow = flow; |
71 this.userId = userId; | 83 this.userId = userId; |
72 this.token = token; | 84 this.token = token; |
73 } | 85 } |
74 | 86 |
| 87 #region IHttpExecuteInterceptor |
| 88 |
75 /// <summary> | 89 /// <summary> |
76 /// Default implementation is to try to refresh the access token if ther
e is no access token or if we are 1· | 90 /// Default implementation is to try to refresh the access token if ther
e is no access token or if we are 1· |
77 /// minute away from expiration. If token server is unavailable, it will
try to use the access token even if· | 91 /// minute away from expiration. If token server is unavailable, it will
try to use the access token even if· |
78 /// has expired. If successful, it will call <seealso cref="IAccessMetho
d.Intercept"/>. | 92 /// has expired. If successful, it will call <seealso cref="IAccessMetho
d.Intercept"/>. |
79 /// </summary> | 93 /// </summary> |
80 public async Task InterceptAsync(HttpRequestMessage request, Cancellatio
nToken taskCancellationToken) | 94 public async Task InterceptAsync(HttpRequestMessage request, Cancellatio
nToken taskCancellationToken) |
81 { | 95 { |
82 if (Token.IsExpired(flow.Clock)) | 96 if (Token.IsExpired(flow.Clock)) |
83 { | 97 { |
84 Logger.Debug("Token has expired, trying to refresh it."); | 98 Logger.Debug("Token has expired, trying to refresh it."); |
85 if (!await RefreshTokenAsync(taskCancellationToken).ConfigureAwa
it(false)) | 99 if (!await RefreshTokenAsync(taskCancellationToken).ConfigureAwa
it(false)) |
86 { | 100 { |
87 throw new InvalidOperationException("The access token has ex
pired but we can't refresh it"); | 101 throw new InvalidOperationException("The access token has ex
pired but we can't refresh it"); |
88 } | 102 } |
89 } | 103 } |
90 | 104 |
91 flow.AccessMethod.Intercept(request, Token.AccessToken); | 105 flow.AccessMethod.Intercept(request, Token.AccessToken); |
92 } | 106 } |
93 | 107 |
| 108 #endregion |
| 109 |
| 110 #region IHttpUnsuccessfulResponseHandler |
| 111 |
| 112 public async Task<bool> HandleResponseAsync(HandleUnsuccessfulResponseAr
gs args) |
| 113 { |
| 114 // TODO(peleyal): check WWW-Authenticate header. |
| 115 if (args.Response.StatusCode == HttpStatusCode.Unauthorized) |
| 116 { |
| 117 return !Object.Equals(Token.AccessToken, flow.AccessMethod.GetAc
cessToken(args.Request)) |
| 118 || await RefreshTokenAsync(args.CancellationToken).Configure
Await(false); |
| 119 } |
| 120 |
| 121 return false; |
| 122 } |
| 123 |
| 124 #endregion |
| 125 |
| 126 #region IConfigurableHttpClientInitializer |
| 127 |
| 128 public void Initialize(ConfigurableHttpClient httpClient) |
| 129 { |
| 130 httpClient.MessageHandler.ExecuteInterceptors.Add(this); |
| 131 httpClient.MessageHandler.UnsuccessfulResponseHandlers.Add(this); |
| 132 } |
| 133 |
| 134 #endregion |
| 135 |
94 /// <summary> | 136 /// <summary> |
95 /// Refreshes the token by calling to <seealso cref="IAuthorizationCodeF
low.RefreshTokenAsync"/>. Then it· | 137 /// Refreshes the token by calling to <seealso cref="IAuthorizationCodeF
low.RefreshTokenAsync"/>. Then it· |
96 /// updates the <see cref="TokenResponse"/> with the new token instance. | 138 /// updates the <see cref="TokenResponse"/> with the new token instance. |
97 /// </summary> | 139 /// </summary> |
98 /// <param name="taskCancellationToken">Cancellation token to cancel an
operation.</param> | 140 /// <param name="taskCancellationToken">Cancellation token to cancel an
operation.</param> |
99 /// <returns><c>true</c> if the token was refreshed.</returns> | 141 /// <returns><c>true</c> if the token was refreshed.</returns> |
100 public async Task<bool> RefreshTokenAsync(CancellationToken taskCancella
tionToken) | 142 public async Task<bool> RefreshTokenAsync(CancellationToken taskCancella
tionToken) |
101 { | 143 { |
102 if (Token.RefreshToken == null) | 144 if (Token.RefreshToken == null) |
103 { | 145 { |
(...skipping 10 matching lines...) Expand all Loading... |
114 | 156 |
115 if (newToken.RefreshToken == null) | 157 if (newToken.RefreshToken == null) |
116 { | 158 { |
117 newToken.RefreshToken = Token.RefreshToken; | 159 newToken.RefreshToken = Token.RefreshToken; |
118 } | 160 } |
119 | 161 |
120 Token = newToken; | 162 Token = newToken; |
121 return true; | 163 return true; |
122 } | 164 } |
123 | 165 |
124 public async Task<bool> HandleResponseAsync(HandleUnsuccessfulResponseAr
gs args) | 166 /// <summary> |
| 167 /// Asynchronously revokes the token by calling |
| 168 /// <see cref="Google.Apis.Auth.OAuth2.Flows.IAuthorizationCodeFlow.Revo
keTokenAsync"/>. |
| 169 /// </summary> |
| 170 /// <param name="taskCancellationToken">Cancellation token to cancel an
operation.</param> |
| 171 /// <returns><c>true</c> if the token was revoked successfully.</returns
> |
| 172 public async Task<bool> RevokeTokenAsync(CancellationToken taskCancellat
ionToken) |
125 { | 173 { |
126 // TODO(peleyal): check WWW-Authenticate header. | 174 if (Token == null) |
127 if (args.Response.StatusCode == HttpStatusCode.Unauthorized) | |
128 { | 175 { |
129 return !Object.Equals(Token.AccessToken, flow.AccessMethod.GetAc
cessToken(args.Request)) | 176 Logger.Warning("Token is already null, no need to revoke it."); |
130 || await RefreshTokenAsync(args.CancellationToken).Configure
Await(false); | 177 return false; |
131 } | 178 } |
132 | 179 |
133 return false; | 180 await flow.RevokeTokenAsync(userId, Token.AccessToken, taskCancellat
ionToken).ConfigureAwait(false); |
134 } | 181 Logger.Info("Access token was revoked successfully"); |
135 | 182 // We don't set the token to null, cause we want that the next reque
st (without reauthorizing) will fail). |
136 public void Initialize(ConfigurableHttpClient httpClient) | 183 return true; |
137 { | |
138 httpClient.MessageHandler.ExecuteInterceptors.Add(this); | |
139 httpClient.MessageHandler.UnsuccessfulResponseHandlers.Add(this); | |
140 } | 184 } |
141 } | 185 } |
142 } | 186 } |
OLD | NEW |