Reputation: 31
I copied the code from post:
Google OAuth2 Service Account Access Token Request gives 'Invalid Request' Response
and tried to get my own OAuth2.0 / JWT authentication to work. But no matter what I try to do I keep getting the exception
System.Exception: The remote server returned an error: (400) Bad Request.
Here is my code:
public void Authenticate()
{
string clientId = "...apps.googleusercontent.com";
string clientSecret = "...";
string emailAddress = "...@developer.gserviceaccount.com";
string publicKeyFingerprints = "...";
string certificateFilename = "Google Analytics - OAuth 2.0 - ...-privatekey.p12";
// certificate
var certificate = new X509Certificate2(certificateFilename, clientSecret);
// header
var header = new { typ = "JWT", alg = "RS256" };
// claimset
var times = GetExpiryAndIssueDate();
var claimset = new
{
iss = emailAddress,
scope = "https://www.googleapis.com/auth/analytics.readonly",
aud = "https://accounts.google.com/o/oauth2/token",
iat = times[0],
exp = times[1],
};
// encoded header
var headerSerialized = JsonConvert.SerializeObject(header);
var headerBytes = Encoding.UTF8.GetBytes(headerSerialized);
var headerEncoded = Base64UrlEncode(headerBytes);
// encoded claimset
var claimsetSerialized = JsonConvert.SerializeObject(claimset);
var claimsetBytes = Encoding.UTF8.GetBytes(claimsetSerialized);
var claimsetEncoded = Base64UrlEncode(claimsetBytes);
// input
var input = headerEncoded + "." + claimsetEncoded;
var inputBytes = Encoding.UTF8.GetBytes(input);
// signiture
var rsa = certificate.PrivateKey as RSACryptoServiceProvider;
var cspParam = new CspParameters
{
KeyContainerName = rsa.CspKeyContainerInfo.KeyContainerName,
KeyNumber = rsa.CspKeyContainerInfo.KeyNumber == KeyNumber.Exchange ? 1 : 2
};
var aescsp = new RSACryptoServiceProvider(cspParam) { PersistKeyInCsp = false };
var signatureBytes = aescsp.SignData(inputBytes, "SHA256");
var signatureEncoded = Base64UrlEncode(signatureBytes);
// jwt
var jwt = headerEncoded + "." + claimsetEncoded + "." + signatureEncoded;
_logger.DebugFormat("JWT: {0}", jwt);
var r = WebRequest.Create("https://accounts.google.com/o/oauth2/token") as HttpWebRequest;
r.Method = "POST";
r.ContentType = "application/x-www-form-urlencoded";
var post = string.Format("{0}={1}&{2}={3}",
"grant_type", HttpUtility.UrlEncode("urn:ietf:params:oauth:grant-type:jwt-bearer", Encoding.UTF8),
"assertion" , jwt);
var result = SendHttpRequest(r, post);
_logger.Debug(result);
}
private static int[] GetExpiryAndIssueDate()
{
var utc0 = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
var issueTime = DateTime.Now;
var iat = (int)issueTime.Subtract(utc0).TotalSeconds;
var exp = (int)issueTime.AddMinutes(55).Subtract(utc0).TotalSeconds;
return new[] { iat, exp };
}
private static string Base64UrlEncode(byte[] input)
{
var output = Convert.ToBase64String(input);
output = output.Split('=')[0]; // Remove any trailing '='s
output = output.Replace('+', '-'); // 62nd char of encoding
output = output.Replace('/', '_'); // 63rd char of encoding
return output;
}
private string SendHttpRequest(HttpWebRequest request, string json)
{
string result = string.Empty;
try
{
_logger.DebugFormat("HttpRequest: {0} {1}", request.Method, request.RequestUri);
foreach (string header in request.Headers)
{
_logger.DebugFormat("Header[{0}]: {1}", header, request.Headers[header]);
}
_logger.DebugFormat("Body: {0}", json);
byte[] body = Encoding.UTF8.GetBytes(json);
request.ContentLength = body.Length;
using (Stream requestStream = request.GetRequestStream())
{
requestStream.Write(body, 0, body.Length);
}
HttpWebResponse response = request.GetResponse() as HttpWebResponse;
using (Stream receiveStream = response.GetResponseStream())
{
using (StreamReader readStream = new StreamReader(receiveStream, Encoding.UTF8))
{
result = readStream.ReadToEnd();
}
}
_logger.DebugFormat("...done, result={0}", result);
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
return result;
}
Does anyone have an idea of what's wrong with this code?
Upvotes: 0
Views: 4356
Reputation: 1
I have faced the same problem
return 400 - Bad request
error : invalid_grant
error description : Bad request
the main reason behind this was incorrect timezone or date and time in the server which is requesting the token change it according to your timezone it will work fine
It worked for me!!
Upvotes: 0
Reputation: 31
I got the code working:
var utc0 = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
var issueTime = DateTime.Now.ToUniversalTime();
I added .ToUniversalTime() and now I got my access token.
Upvotes: 3
Reputation: 3018
Please look at the body of the response. That typically has more detail on the reason behind the 400 error.
If you copied the code from that post, did you verify that the date/time on your server is correct? This is the most common cause of errors when doing signatures as part of the JWT. Even a few seconds count -- verify your clock is synced to NTP and/or matches time.gov. Also make sure timezone is correct.
Upvotes: 1