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?