Implement OAuth token re-authorization
Override the BlogClientBase.Login method with an OAuth2 login flow.
This commit is contained in:
parent
436f2a121b
commit
5919cd1b31
|
@ -18,6 +18,8 @@ using Google.Apis.Util.Store;
|
|||
using Google.Apis.Services;
|
||||
using Google.Apis.Auth.OAuth2.Flows;
|
||||
using OpenLiveWriter.BlogClient.Providers;
|
||||
using Google.Apis.Auth.OAuth2.Responses;
|
||||
using Google.Apis.Util;
|
||||
|
||||
namespace OpenLiveWriter.BlogClient.Clients
|
||||
{
|
||||
|
@ -55,8 +57,7 @@ namespace OpenLiveWriter.BlogClient.Clients
|
|||
|
||||
private static IDataStore GetCredentialsDataStoreForBlog(string blogId)
|
||||
{
|
||||
// The Google APIs will automatically store the OAuth2 tokens in the given path. We use a unique path per
|
||||
// blog to support multiple Blogger accounts.
|
||||
// The Google APIs will automatically store the OAuth2 tokens in the given path.
|
||||
var folderPath = Path.Combine(ApplicationEnvironment.LocalApplicationDataDirectory, "GoogleBloggerv3");
|
||||
return new FileDataStore(folderPath, true);
|
||||
}
|
||||
|
@ -88,9 +89,26 @@ namespace OpenLiveWriter.BlogClient.Clients
|
|||
_clientOptions = clientOptions;
|
||||
}
|
||||
|
||||
private BloggerService GetService()
|
||||
{
|
||||
TransientCredentials transientCredentials = Login();
|
||||
return new BloggerService(new BaseClientService.Initializer()
|
||||
{
|
||||
HttpClientInitializer = (UserCredential)transientCredentials.Token
|
||||
});
|
||||
}
|
||||
|
||||
private bool IsValidToken(TokenResponse token)
|
||||
{
|
||||
// If the token is expired but we have a non-null RefreshToken, we can assume the token will be
|
||||
// automatically refreshed when we query Google Blogger and is therefore valid.
|
||||
return token != null && (!token.IsExpired(SystemClock.Default) || token.RefreshToken != null);
|
||||
}
|
||||
|
||||
protected override TransientCredentials Login()
|
||||
{
|
||||
TransientCredentials transientCredentials = Credentials.TransientCredentials as TransientCredentials;
|
||||
var transientCredentials = Credentials.TransientCredentials as TransientCredentials ??
|
||||
new TransientCredentials(Credentials.Username, Credentials.Password, null);
|
||||
VerifyAndRefreshCredentials(transientCredentials);
|
||||
return transientCredentials;
|
||||
}
|
||||
|
@ -102,39 +120,64 @@ namespace OpenLiveWriter.BlogClient.Clients
|
|||
|
||||
private void VerifyAndRefreshCredentials(TransientCredentials tc)
|
||||
{
|
||||
var flowInitializer = new GoogleAuthorizationCodeFlow.Initializer
|
||||
{
|
||||
ClientSecretsStream = ClientSecretsStream,
|
||||
DataStore = GetCredentialsDataStoreForBlog(tc.Username),
|
||||
Scopes = new List<string>() { BloggerServiceScope, PicasaServiceScope },
|
||||
};
|
||||
var flow = new GoogleAuthorizationCodeFlow(flowInitializer);
|
||||
var userCredential = tc.Token as UserCredential;
|
||||
var token = userCredential?.Token;
|
||||
var cancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
// Attempt to load a cached OAuth token.
|
||||
var loadTokenTask = flow.LoadTokenAsync(tc.Username, cancellationTokenSource.Token);
|
||||
loadTokenTask.Wait();
|
||||
if (loadTokenTask.IsCompleted)
|
||||
if (IsValidToken(token))
|
||||
{
|
||||
var token = loadTokenTask.Result;
|
||||
if (token == null || (token.RefreshToken == null && token.IsExpired(flow.Clock)))
|
||||
// We already have a valid OAuth token.
|
||||
return;
|
||||
}
|
||||
|
||||
if (userCredential == null)
|
||||
{
|
||||
// Attempt to load a cached OAuth token.
|
||||
var flow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
|
||||
{
|
||||
// The token is invalid, so we need to login again.
|
||||
if (BlogClientUIContext.SilentModeForCurrentThread)
|
||||
{
|
||||
// If we're in silent mode where prompting isn't allowed, throw the verification exception
|
||||
throw new BlogClientAuthenticationException(String.Empty, String.Empty);
|
||||
}
|
||||
ClientSecretsStream = ClientSecretsStream,
|
||||
DataStore = GetCredentialsDataStoreForBlog(tc.Username),
|
||||
Scopes = new List<string>() { BloggerServiceScope, PicasaServiceScope },
|
||||
});
|
||||
|
||||
var authorizationTask = GetOAuth2AuthorizationAsync(tc.Username, cancellationTokenSource.Token);
|
||||
authorizationTask.Wait();
|
||||
|
||||
if (authorizationTask.Result?.Token == null)
|
||||
{
|
||||
throw new BlogClientOperationCancelledException();
|
||||
}
|
||||
var loadTokenTask = flow.LoadTokenAsync(tc.Username, cancellationTokenSource.Token);
|
||||
loadTokenTask.Wait();
|
||||
if (loadTokenTask.IsCompleted)
|
||||
{
|
||||
// We were able re-create the user credentials from the cache.
|
||||
userCredential = new UserCredential(flow, tc.Username, loadTokenTask.Result);
|
||||
token = loadTokenTask.Result;
|
||||
}
|
||||
}
|
||||
|
||||
if (!IsValidToken(token))
|
||||
{
|
||||
// The token is invalid, so we need to login again. This likely includes popping out a new browser window.
|
||||
if (BlogClientUIContext.SilentModeForCurrentThread)
|
||||
{
|
||||
// If we're in silent mode where prompting isn't allowed, throw the verification exception
|
||||
throw new BlogClientAuthenticationException(String.Empty, String.Empty);
|
||||
}
|
||||
|
||||
// Start an OAuth flow to renew the credentials.
|
||||
var authorizationTask = GetOAuth2AuthorizationAsync(tc.Username, cancellationTokenSource.Token);
|
||||
authorizationTask.Wait();
|
||||
if (authorizationTask.IsCompleted)
|
||||
{
|
||||
userCredential = authorizationTask.Result;
|
||||
token = userCredential?.Token;
|
||||
}
|
||||
}
|
||||
|
||||
if (!IsValidToken(token))
|
||||
{
|
||||
// The token is still invalid after all of our attempts to refresh it. The user did not complete the
|
||||
// authorization flow, so we interpret that as a cancellation.
|
||||
throw new BlogClientOperationCancelledException();
|
||||
}
|
||||
|
||||
// Stash the valid user credentials.
|
||||
tc.Token = userCredential;
|
||||
}
|
||||
|
||||
public void OverrideOptions(IBlogClientOptions newClientOptions)
|
||||
|
@ -145,15 +188,7 @@ namespace OpenLiveWriter.BlogClient.Clients
|
|||
public BlogInfo[] GetUsersBlogs()
|
||||
{
|
||||
var cancellationTokenSource = new CancellationTokenSource();
|
||||
var userCredentialsTask = GetOAuth2AuthorizationAsync(Credentials.Username, cancellationTokenSource.Token);
|
||||
userCredentialsTask.Wait(cancellationTokenSource.Token);
|
||||
|
||||
BloggerService service = new BloggerService(new BaseClientService.Initializer()
|
||||
{
|
||||
HttpClientInitializer = userCredentialsTask.Result
|
||||
});
|
||||
|
||||
var listBlogsTask = service.Blogs.ListByUser("self").ExecuteAsync();
|
||||
var listBlogsTask = GetService().Blogs.ListByUser("self").ExecuteAsync();
|
||||
listBlogsTask.Wait(cancellationTokenSource.Token);
|
||||
return listBlogsTask.Result?.Items?.Select(x => new BlogInfo(x.Id, x.Name, x.Url)).ToArray();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue