0

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 = "[email protected]"; 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?

3 Answers 3

3

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.

Sign up to request clarification or add additional context in comments.

Comments

1

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.

3 Comments

I cannot set my clock to sync with NTP, because it's part of a domain.
My system's time the same as on www.worldtimeserver.com. My timezone is GTM+1. Do I need to specify this somewhere in my code?
No need to specify it in your code. Your code should get it from your system.
0

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!!

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.