C# 带有服务器到服务器身份验证的Google OAuth2返回;无效的“授权”;
我试图按照概述的步骤获取访问令牌,以便与带有OAuth2的Google日历API一起使用。在尝试组合并签署jwt之后,我总是得到400个“错误请求”响应,错误为“无效的授权” 我已经非常仔细地遵循了这些步骤,并且仔细地检查了每一行多次。我也仔细阅读了我能找到的每一篇关于这个主题的帖子。多年来,我一直在网上寻找解决方案,现在我已经写下了我的第一个问题 我已经尝试过的常见建议解决方案: 1) 我的系统时钟与ntp时间同步 2) 我使用的是iss的电子邮件,而不是客户ID 3) 我的发布时间和到期时间以UTC为单位 4) 我确实研究了access_type=offline参数,但它似乎不适用于这种服务器到服务器的场景 5) 我没有指定prn参数 6) 其他各种杂事 我知道有一些Google库可以帮助管理这一点,但我有理由解释为什么我需要自己签署jwt而不使用提供的库来实现这一点。此外,到目前为止,我看到的许多问题和示例似乎使用accounts.google.com/o/oauth2/auth作为基本url,而我上面链接的文档似乎指定请求转到www.googleapis.com/oauth2/v3/token(因此,许多现有问题可能适用于不同的场景)。无论如何,我完全被难住了,不知道还能尝试什么。这是我的C#代码,编辑了一些特定的字符串C# 带有服务器到服务器身份验证的Google OAuth2返回;无效的“授权”;,c#,oauth-2.0,google-api,google-oauth,jwt,C#,Oauth 2.0,Google Api,Google Oauth,Jwt,我试图按照概述的步骤获取访问令牌,以便与带有OAuth2的Google日历API一起使用。在尝试组合并签署jwt之后,我总是得到400个“错误请求”响应,错误为“无效的授权” 我已经非常仔细地遵循了这些步骤,并且仔细地检查了每一行多次。我也仔细阅读了我能找到的每一篇关于这个主题的帖子。多年来,我一直在网上寻找解决方案,现在我已经写下了我的第一个问题 我已经尝试过的常见建议解决方案: 1) 我的系统时钟与ntp时间同步 2) 我使用的是iss的电子邮件,而不是客户ID 3) 我的发布时间和到期时间
public static string GetBase64UrlEncoded(byte[] input)
{
string value = Convert.ToBase64String(input);
value = value.Replace("=", string.Empty).Replace('+', '-').Replace('/', '_');
return value;
}
static void Main(string[] args)
{
DateTime baseTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
DateTime now = DateTime.Now.ToUniversalTime();
int ticksIat = ((int)now.Subtract(baseTime).TotalSeconds);
int ticksExp = ((int)now.AddMinutes(55).Subtract(baseTime).TotalSeconds);
string jwtHeader = @"{""typ"":""JWT"", ""alg"":""RS256""}";
string jwtClaimSet = string.Format(@"{{""iss"":""************-********************************@developer.gserviceaccount.com""," +
@"""scope"":""https://www.googleapis.com/auth/calendar.readonly""," +
@"""aud"":""https://www.googleapis.com/oauth2/v3/token"",""exp"":{0},""iat"":{1}}}", ticksExp, ticksIat);
byte[] headerBytes = Encoding.UTF8.GetBytes(jwtHeader);
string base64jwtHeader = GetBase64UrlEncoded(headerBytes);
byte[] claimSetBytes = Encoding.UTF8.GetBytes(jwtClaimSet);
string base64jwtClaimSet = GetBase64UrlEncoded(claimSetBytes);
string signingInputString = base64jwtHeader + "." + base64jwtClaimSet;
byte[] signingInputBytes = Encoding.UTF8.GetBytes(signingInputString);
X509Certificate2 pkCert = new X509Certificate2("<path to cert>.p12", "notasecret");
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)pkCert.PrivateKey;
CspParameters cspParam = new CspParameters
{
KeyContainerName = rsa.CspKeyContainerInfo.KeyContainerName,
KeyNumber = rsa.CspKeyContainerInfo.KeyNumber == KeyNumber.Exchange ? 1 : 2
};
RSACryptoServiceProvider cryptoServiceProvider = new RSACryptoServiceProvider(cspParam) { PersistKeyInCsp = false };
byte[] signatureBytes = cryptoServiceProvider.SignData(signingInputBytes, "SHA256");
string signatureString = GetBase64UrlEncoded(signatureBytes);
string finalJwt = signingInputString + "." + signatureString;
HttpClient client = new HttpClient();
string url = "https://www.googleapis.com/oauth2/v3/token?grant_type=urn%3aietf%3aparams%3aoauth%3agrant-type%3ajwt-bearer&assertion=" + finalJwt;
HttpResponseMessage message = client.PostAsync(url, new StringContent(string.Empty)).Result;
string result = message.Content.ReadAsStringAsync().Result;
}
公共静态字符串GetBase64UrlEncoded(字节[]输入)
{
字符串值=Convert.tobase64字符串(输入);
value=value.Replace(“=”,string.Empty).Replace(“+”,“-”).Replace(“/”,“"”);
返回值;
}
静态void Main(字符串[]参数)
{
DateTime baseTime=新的日期时间(1970,1,1,0,0,0,0,DateTimeKind.Utc);
DateTime now=DateTime.now.ToUniversalTime();
int ticksIat=((int)now.Subtract(baseTime.TotalSeconds);
int ticksepp=((int)now.AddMinutes(55).Subtract(baseTime.TotalSeconds);
字符串jwtHeader=@“{”类型“:”JWT“,”alg“:”RS256“}”;
string jwtClaimSet=string.Format(@“{”iss“:”***************-**************************************************@developer.gserviceaccount.com“)+
@“范围”:https://www.googleapis.com/auth/calendar.readonly""," +
@“澳元”:https://www.googleapis.com/oauth2/v3/token","exp":{0},"iat":{1}},ticksepp,ticksIat);;
byte[]headerBytes=Encoding.UTF8.GetBytes(jwtHeader);
字符串base64jwtHeader=GetBase64UrlEncoded(headerBytes);
byte[]claimSetBytes=Encoding.UTF8.GetBytes(jwtClaimSet);
字符串base64jwtClaimSet=GetBase64UrlEncoded(claimSetBytes);
字符串signingInputString=base64jwtHeader+“”+base64jwtClaimSet;
byte[]signingInputBytes=Encoding.UTF8.GetBytes(signingInputString);
X509Certificate2 pkCert=新的X509Certificate2(“.p12”,“notasecret”);
RSACryptoServiceProvider rsa=(RSACryptoServiceProvider)pkCert.PrivateKey;
CspParameters cspParam=新的CspParameters
{
KeyContainerName=rsa.CspKeyContainerInfo.KeyContainerName,
KeyNumber=rsa.CspKeyContainerInfo.KeyNumber==KeyNumber.Exchange?1:2
};
RSACryptoServiceProvider cryptoServiceProvider=新的RSACryptoServiceProvider(cspParam){PersistKeyInCsp=false};
byte[]signatureBytes=cryptoServiceProvider.SignData(signingInputBytes,“SHA256”);
字符串signatureString=GetBase64UrlEncoded(signatureBytes);
字符串finalJwt=signingInputString+“”+signatureString;
HttpClient=新的HttpClient();
字符串url=”https://www.googleapis.com/oauth2/v3/token?grant_type=urn%3aietf%3aparams%3aoauth%3agrant-类型%3ajwt承载和断言=“+finalJwt;
HttpResponseMessage=client.PostAsync(url,new StringContent(string.Empty)).Result;
字符串结果=message.Content.ReadAsStringAsync().result;
}
这是使用我在谷歌账户上设置的谷歌“服务账户”,生成的私钥及其对应的.p12文件直接使用
有人用这种方法工作吗?我将非常感谢任何帮助 您正在过帐到令牌端点,但参数作为查询字符串的一部分发送。您应该在帖子正文中以URL表单编码值的形式发送参数。例如:
var params = new List<KeyValuePair<string, string>>();
params.Add(new KeyValuePair<string, string>("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer"));
params.Add(new KeyValuePair<string, string>("assertion", finalJwt));
var content = new FormUrlEncodedContent(pairs);
var message = client.PostAsync(url, content).Result;
var params=new List();
添加(新的KeyValuePair(“grant_类型”,“urn:ietf:params:oauth:grant类型:jwt承载”);
添加(新的KeyValuePair(“断言”,finalJwt));
var内容=新FormUrlEncodedContent(成对);
var message=client.PostAsync(url、内容).Result;
尝试此操作以获取访问令牌-
String serviceAccountEmail = "xxxxxxx.gserviceaccount.com";
String keyFilePath = System.Web.HttpContext.Current.Server.MapPath("~/Content/Security/file.p12"); ////.p12 file location
if (!File.Exists(keyFilePath))
{
Console.WriteLine("An Error occurred - Key file does not exist");
return null;
}
string[] scopes = new string[] {
CloudVideoIntelligenceService.Scope.CloudPlatform, ///CloudVideoIntelligence scope
YouTubeService.Scope.YoutubeForceSsl, ///Youtube scope
TranslateService.Scope.CloudTranslation ///Translation scope
};
var certificate = new X509Certificate2(keyFilePath, "notasecret", X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = scopes
}.FromCertificate(certificate));
var token = Google.Apis.Auth.OAuth2.GoogleCredential.FromServiceAccountCredential(credential).UnderlyingCredential.GetAccessTokenForRequestAsync().Result;//retrive token
return token;
谢谢你的快速回复!这确实解决了问题。错过了一件多么尴尬的事。。。