我有一个网络应用程序,用户登录我们的系统后,可以转到页面,通过谷歌授权整个OAuth流程的gmail访问权限。该过程完成后,我接收access_token和refresh_token,并解析用户子属性的id_token并将其保存到数据库。这样做的目的是,通过授权我们的应用程序的用户,我们然后在控制台应用程序中使用访问和刷新令牌,然后检索他们的电子邮件并将其与我们的内部系统同步。
在google开发者控制台中,我使用相应的重定向uris将凭据创建为Web应用程序。浏览器授权过程一切正常。但是,当我使用访问令牌运行控制台应用程序时,它会打开一个浏览器窗口,并显示谷歌错误
Error: redirect_uri_mismatch
The redirect URI in the request, http://127.0.0.1:57590/authorize/, does not match the ones authorized for the OAuth client.
由于这是一个控制台应用程序,因此没有重定向uri。所以这让我想知道我是否需要将凭证设置为"其他"申请类型。我创建了一个新的凭据,并用我的Web应用程序中的clientid和secret替换了新的凭据。当我完成登录系统然后访问授权页面的过程时,一切正常。然后我回到我的控制台应用程序(现在使用更新的ID),当它运行时,它打开一个浏览器窗口,要求我登录谷歌并再次验证它!我已经在第一步中完成了这项工作。由于这是在应用服务器上运行的控制台应用,因此用户无法与其进行交互以授权访问...我认为这是访问令牌的全部意义!?
这是我的代码。
处理授权的网络应用控制器。
public class GoogleController : Controller
{
private readonly CredentialService _credentialService;
private readonly GoogleEndpoints _endpoints;
private string AuthorizeUrl{ get { return ...; }}
private string AuthorizeResponseUrl{ get { return ...; }}
private string SaveResponseUrl{ get { return ...; }}
public GoogleController()
{
_endpoints = new GoogleEndpoints();
_credentialService = new CredentialService(new CredentialRepository(ConnectionStrings.GeneralInfo));
}
public void Authorize()
{
if (Session["UserID"] == null || Session["Email"] == null)
{
Response.Redirect("~/Login.aspx", true);
Session["LoginSource"] = AuthorizeUrl;
Response.End();
}
else
{
if (Session["SessionId"] == null || Session["SessionId"].ToString().Trim().Length == 0)
Session["SessionId"] = _credentialService.CreateSessionId(Session["UserID"].To<int>());
var url = _endpoints.AuthorizationEndpoint + "?" +
"client_id=" + APIConstants.GMailApiKey + "&" +
"response_type=code&" +
"scope=openid%20email https://www.googleapis.com/auth/gmail.readonly&" +
"redirect_uri=" + AuthorizeResponseUrl + "&" +
"state=" + Session["SessionId"] + "&" +
"login_hint=" + Session["Email"] + "&" +
"access_type=offline&prompt=consent";
Response.Redirect(url);
}
}
public ActionResult AuthorizeResponse()
{
var state = Request.QueryString["state"];
if (state == Session["SessionId"].ToString())
{
var code = Request.QueryString["code"];
var values = new Dictionary<string, object>
{
{"code", code},
{"redirect_uri", AuthorizeResponseUrl},
{"client_id", APIConstants.GMailApiKey},
{"client_secret", APIConstants.GmailApiSecret},
{"grant_type", "authorization_code"},
{"scope", ""}
};
var webmethods = new WebMethods();
var tokenResponse = webmethods.Post(_endpoints.TokenEndpoint, values);
var jobject = JObject.Parse(tokenResponse);
var access_token = jobject.SelectToken("access_token");
var refresh_token = jobject.SelectToken("refresh_token");
var id_token = jobject.SelectToken("id_token");
if (access_token == null || access_token.ToString().Trim().Length == 0)
{
var emailService = new EmailRouterService(new EmailRouterRepository(ConnectionStrings.EmailRouter));
emailService.SendEmail(new Email
{
Body = tokenResponse,
Subject = "Error setting up google for " + Session["Email"] + ". ",
FromAddress = "TenixErrors@synergydatasystems.com",
ToAddress = "tendevs@synergydatasystems.com"
});
return View(new GoogleAuthResponse(tokenResponse, false));
}
var idTokenEls = id_token.ToString().Split('.');
var idTokenString = base64Decode(idTokenEls[1]);
var id_token_json = JObject.Parse(idTokenString);
var credentials = _credentialService.GetUserCredentials(Session["SessionId"].ToString());
credentials.AccessToken = access_token.ToString();
credentials.RefreshToken = refresh_token.ToString();
credentials.TEEmployeeId = Session["UserId"].To<int>();
credentials.EmailAddress = id_token_json.SelectToken("email").ToString();
credentials.GoogleSubKey = id_token_json.SelectToken("sub").ToString();
_credentialService.SaveUserCredentials(credentials);
return View(new GoogleAuthResponse("Integration successful!", true));
}
return View(new GoogleAuthResponse("Missing state information.", false));
}
public string base64Decode(string data)
{
....
}
}
控制台应用正在使用.net google api库。
public class GmailUserSync
{
private readonly CredentialService _credentialService;
public GmailUserSync(CredentialService credentialService)
{
_credentialService = credentialService;
}
public void SyncThreads()
{
var secrets = new ClientSecrets
{
ClientId = APIConstants.GMailApiKey,
ClientSecret = APIConstants.GmailApiSecret
};
var credentials = _credentialService.GetAllCredentials();
foreach (var c in credentials)
{
var credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
secrets,
new[] { GmailService.Scope.GmailReadonly },
c.GoogleSubKey,
CancellationToken.None,
new TenixDataStore(_credentialService)).Result;
var service = new GmailService(new BaseClientService.Initializer
{
HttpClientInitializer = credential,
ApplicationName = "Gmail Integration"
});
var request = service.Users.Messages.List("me");
// List messages.
var messages = request.Execute().Messages;
Console.WriteLine("Messages:");
if (messages != null && messages.Count > 0)
foreach (var message in messages)
{
Console.WriteLine("From : " + message.ThreadId);
}
else
Console.WriteLine("No labels found.");
}
Console.Read();
}
}
我的IDataStore实现。
public class TenixDataStore : IDataStore
{
private readonly ICredentialService _service;
public TenixDataStore(ICredentialService credentialService)
{
_service = credentialService;
}
public Task StoreAsync<T>(string key, T value)
{
if (string.IsNullOrEmpty(key))
throw new ArgumentException("Key MUST have a value");
var serialized = NewtonsoftJsonSerializer.Instance.Serialize(value);
var jObject = JObject.Parse(serialized);
var access_token = jObject.SelectToken("access_token");
var refresh_token = jObject.SelectToken("refresh_token");
if (access_token == null)
throw new ArgumentException("Missing access token");
if (refresh_token == null)
throw new ArgumentException("Missing refresh token");
var credential = _service.GetUserCredentialsFromGoogleSubId(key);
credential.AccessToken = (string) access_token;
credential.RefreshToken = (string) refresh_token;
_service.SaveUserCredentials(credential);
return Task.Delay(0);
}
public Task DeleteAsync<T>(string key)
{
return Task.Delay(0);
}
public Task<T> GetAsync<T>(string googleSubId)
{
var credentials = _service.GetUserCredentialsFromGoogleSubId(googleSubId);
var completionSource = new TaskCompletionSource<T>();
if (!string.IsNullOrEmpty(credentials.RefreshToken))
{
var jsonData = Newtonsoft.Json.JsonConvert.SerializeObject(credentials);
completionSource.SetResult(NewtonsoftJsonSerializer.Instance.Deserialize<T>(jsonData));
}
else
completionSource.SetResult(default(T));
return completionSource.Task;
}
public Task ClearAsync()
{
return Task.Delay(0);
}
}
IDataStore中的GetAsync方法将访问令牌和刷新令牌凭据返回给GoogleWebAuthorizationBroker.AuthorizeAsync,之后就是新浏览器打开,要求我授权该应用程序。
那么,我应该将这个设置在谷歌控制台中作为网络应用类型还是其他类型?而且,既然我试着以任何方式遇到问题,那么我做错了什么?