我正在为我们的移动应用程序客户开发Google sue服务中的Google签名。我们的服务位于Dotnet平台(ASP.NET Web API)上,我们的身份验证处理程序与Owin一起是ASP身份,因为它的中间软件。我们的客户使用本地Google库通过Google(用于身份验证)和最后,当用户通过Google进行身份验证时,Google返回了回调事件中的两个重要值,这些值是" ID_TOKEN" 和" auth_code"
正如我之前读过的(https://developers.google.com/inentity/sign-in/android/android/offline-access ), auth_code 应该 hop with with access_token 和 access_token 应为给定为了访问授权资源。
当我想与Access_Token交换Auth_code(由客户端发送)时,我将使用以下请求参数拨打Owin代币端点
- grant_type(授权_code)
- 代码
- client_id
- client_secret请注意,由于我的客户是移动应用程序,我不发送redirect_uri参数
我的响应始终是" Invalid_grant" >但是,当我向Google令牌端点发送完全相同的请求(使用相同的数据)时,它(Google token endpoint)很容易使我访问访问诸如到期之类的数据),因此我的请求参数是正确的,但我不知道为什么我无法从我自己的本地服务获得access_token
这是我的要求正文的样本:grant_type = pretureization_code& code = auth_code& client_id = myclientid.apps.googleusercontent.com& client_secret = myclextecret
>我还为"授权的JavaScript Origins"one_answers"授权重定向URIS"字段设置了Localhost Web API URL
我想我错过了一些东西(也许是在我的配置中)。
这可能是因为我的API代码未部署在公共领域上,如果这样,为什么Google SSO流程仍然适用于Web客户端的Localhost(即使我在Google Console中指定了Localhost)
请注意,我仅适用于移动应用客户端,Web客户端,Google Oauth,ASP Identity和Owin的集成像Magic一样工作,因为在Google回到指定的Redirect_uri中,Google将Access_Token作为查询字符串参数(否)需要将auth_code传递到API服务器以获取access_token)。
这是我在startup.cs文件中的GoogleOauth配置
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Configure the application for OAuth based flow
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
// In production mode set AllowInsecureHttp = false
AllowInsecureHttp = true
};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
// Uncomment the following lines to enable logging in with third party login providers
//app.UseMicrosoftAccountAuthentication(
// clientId: "",
// clientSecret: "");
//app.UseTwitterAuthentication(
// consumerKey: "",
// consumerSecret: "");
//app.UseFacebookAuthentication(
// appId: "",
// appSecret: "");
app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
{
ClientId = "MYCLIENTID.apps.googleusercontent.com",
ClientSecret = "MYCLIENTSECRET"
});
我一直在这个问题上拉头发,任何帮助都将不胜感激
我没有找到解决问题的方法>
Google SSO的客户端SDK(Android,iOS等)具有相同的机制,当用户成功地登录Google并返回App Google SDK返回一次使用给客户的client token,称为 Auth Code
,如果将此代码发送到Google OAuth2 API终点(https://accounts.google.com/o/oauth2/token),则使用以下格式$"grant_type=authorization_code&code={YOUR AUTH CODE}&client_id={YOUR CLIENT ID (set in google dev console)}&client_secret={YOUR CLIENT SECRET (set in google dev console)}"
您会收到一个访问令牌,您可以访问Google Services(Google drive,gmail,gmail,gmail,gmail,gmail,gmail,gmail,gmail,gmailETC)。
因此,您可以直接调用该API(使用httpclient),如果您收到该access_token,则表明用户已成功登录到Google,因此您可以检查他们在本地DB中是否有帐户,请通过调用<来登录它们>您的本地令牌端点,否则注册用户(使用默认密码),然后调用您的令牌端点(我知道在API应用程序中调用您的API端点有点脏,但是您知道我的情况,因为没有人回答这个问题,所以我别无选择)来登录它们>
这是我的代码
在我的帐户控制器中
[AllowAnonymous]
public async Task<ActionResult> LoginWithGoogle(string email, string ac)
{
var appType = StoreContext.GetApplicationType();
var requestStoreFrontType = Request.Headers["StoreFrontType"];
#region removing mobile header (for google)
//if not removing, google Api will reject our request
Request.RemoveHeaderIfExist("StoreFrontType");
Request.RemoveHeaderIfExist("StoreFrontVersion");
Request.RemoveHeaderIfExist("Client_Token");
Request.RemoveHeaderIfExist("Content-Type");
#endregion
var _googleService = new GoogleExternalLoginService();
var registerResult = _googleService.LoginExternal(ac, appType);
if (registerResult.Status == ServiceResultStatus.Success)
{
var pass = defaultPass;
var registermodel = new RegisterViewModel();
registermodel.AcceptPolicy = true;
registermodel.Email = email;
registermodel.EnableNewsLetter = true;
registermodel.Password = pass;
string getTokenUrl = "";
var existedUser = UserManager.FindByEmail(registermodel.Email);
if (existedUser == null)
{
var owinUser = new ApplicationUser { UserName = email, Email = email, RegistrationType = GetUserRegTypeByExternalProviderName("Google") };
var result = await UserManager.CreateAsync(owinUser, registermodel.Password);
if (!result.Succeeded)
return new DSJson(null, registerResult.Message, registerResult.Status);
getTokenUrl = Request.Url.Scheme + "://" + Request.Url.Authority
+"/Token";
}
else
{
ECS.Cache.CacheFacade.Store(ac, email, DateTime.Now.AddMinutes(2));
getTokenUrl = Request.Url.Scheme + "://" + Request.Url.Authority
+ "/Token" + $"?IsSSO=1&AuthCode={ac}";
}
var getTokenParams = new List<KeyValuePair<string, string>>();
getTokenParams.Add(new KeyValuePair<string, string>("grant_type", "password"));
getTokenParams.Add(new KeyValuePair<string, string>("username", registermodel.Email));
getTokenParams.Add(new KeyValuePair<string, string>("password", registermodel.Password));
var headers = new Dictionary<string, string>();
//headers.Add("StoreFrontType", StoreContext.StoreFrontType);
//headers.Add("StoreFrontVersion", StoreContext.StoreFrontVersion);
//headers.Add("Client_Token", StoreContext.ClientToken);
var tokenResponse = HttpHelper.CallService<object>(getTokenUrl, "Post", null, getTokenParams.ToArray(), headers).Data;
return new DSJson(tokenResponse, isResultDataInServiceResult: true);
}
return new DSJson(null, "operation failed, auth_code is invalid", ServiceResultStatus.Fail);
}
请注意,在您的请求中有任何额外的自定义标头时,我必须在将请求发送到Google Endpoint之前,在将请求发送到Google端点之前必须删除所有客户的标题。
这是我在操作中使用的loginexternal方法代码
public ServiceResult<ExternalTokenView> LoginExternal(string requestCode,ApplicationType appType)
{
var res = new ServiceResult<ExternalTokenView>();
var getTokenParams = new List<KeyValuePair<string, string>>();
getTokenParams.Add(new KeyValuePair<string, string>("grant_type", "authorization_code"));
getTokenParams.Add(new KeyValuePair<string, string>("code", requestCode));
getTokenParams.Add(new KeyValuePair<string, string>("client_id", ClientIds[appType.ToString()]));
if (appType == ApplicationType.AppAndroid)
getTokenParams.Add(new KeyValuePair<string, string>("client_secret", ClientSecrets[appType.ToString()]));
var getTokenUrl = TokenEndpoint;
var response = HttpHelper.CallService<ExternalTokenView>(getTokenUrl, "Post", null, rowData: getTokenParams.ToArray());
res.Data = response.Data;
res.Status = response.Status;
return res;
}
请注意,对于iOS客户端,您不应该发送客户端秘密(仅适用于Android客户端)。
最后,这是我的令牌端点代码
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
// برای کاربرانی که قبلا اکانت داشته اند که پسوردشان متفاوت با پسورد پیش فرض برای
//external logins
//است
if (!string.IsNullOrEmpty(HttpContext.Current.Request["IsSSO"]))
{
var authCode = HttpContext.Current.Request["AuthCode"];
if (!string.IsNullOrEmpty(authCode))
{
var emailInCache = ECS.Cache.CacheFacade.Get<string>(authCode);
if (!string.IsNullOrEmpty(emailInCache))
{
user = await userManager.FindByEmailAsync(emailInCache);
Cache.CacheFacade.Remove(authCode);
}
}
}
else
{
context.SetError("invalid_grant", "ایمیل/موبایل یا رمز عبور نا معتبر است");
return;
}
}
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager,
OAuthDefaults.AuthenticationType);
ClaimsIdentity cookiesIdentity = await user.GenerateUserIdentityAsync(userManager,
CookieAuthenticationDefaults.AuthenticationType);
AuthenticationProperties properties = CreateProperties(user.UserName);
properties.Dictionary.Add(new KeyValuePair<string, string>("UserRegisterationType",((byte)user.RegistrationType).ToString()));
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
context.Validated(ticket);
}
您可能已经注意到我将authcode值存储在LoginWithGoogle
操作中,并在GrantResourceOwnerCredentials
方法中读取CACHE的值,以便在我们没有密码时能够授予用户令牌Gmail帐户)。但是,从缓存中读取Auth代码的原因是,例外过程只能用于来自Google和其他用户必须通过正常过程(使用其用户名和密码的代币)
希望它有帮助