My WPF desktop application (C#) is attempting to read the user's Outlook emails through the Microsoft Graph API. I am stuck in the authentication process; I've already received an authentication code and now I'm trying to get an access token from Azure but keep getting a HTTP 400 error code when sending out the request for the access token:
/**** Auth Code Retrieval ****/
string authCodeUrl = "https://login.microsoftonline.com/common/oauth2/authorize";
authCodeUrl += "?client_id" = clientId;
authCodeUrl += "&redirect_uri=" + redirectUri;
authCodeUrl += "&response_type=code";
authCodeUrl += "&resource=https%3A%2F%2Fgraph.microsoft.com%2F";
Process.start(authUrl); // User logs in, we get the auth code after login
string code = "......"; // Hidden for this post
/**** Access Token Retrieval ****/
string tokenUrl = "https://login.microsoftonline.com/common/oauth2/token"
string content = "grant_type=authorization_code";
content += "&client_id=" + clientId;
content += "&resource=https%3A%2F%2Fgraph.microsoft.com%2F";
content += "&code=" + code;
content += "&redirect_uri=" + redirectUri;
WebRequest request = WebRequest.Create(tokenUrl);
request.ContentType = "application/x-www-form-urlencoded";
byte[] data = Encoding.UTF8.GetBytes(content);
request.ContentLength = data.Length;
request.Method = "POST";
try
{
using (Stream stream = request.GetRequestStream())
{
stream.Write(data, 0, data.Length);
}
WebResponse response = request.GetResponse(); // This throws exception
}
catch (Exception error) // This catches the exception
{
Console.WriteLine(error.Message); // Outputs 400, bad request
}
The above is the code used to retrieve the auth code followed by the attempt to retrieve the access token. We do not have a client_secret because secrets are only for Web applications and this is a native desktop WPF application. I have read that this isn't an issue though. I have followed many tutorials and official docs online, mainly the official Graph authorization doc and I still cannot figure out what I am doing wrong. Any help would be greatly appreciated, thank you.
I used fiddler to debug the request and I found the full error message: The user or administrator has not consented to use the application. I googled this message for a bit and found some stack articles and github issue threads that lead me to the solution: my request had been using "common", in the base URL, as the tenant ID when actually I needed to use my Azure tenant ID which I acquired through this answer on stack. My new base URL for the authentication requests now looks like:
https://login.microsoftonline.com/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/oauth2/authorize
where "xxxx-....xxx" would be replaced by your Azure tenant id!
If you don't use a client secret, then you need to configure your tenant to support implicit grant flow. You can follow the directions from this blog post to perform / validate the configuration. This requires using the Azure management portal to download, modify the app manifest, and upload it.
Alternatively, and possibly a better strategy, is to switch your code over to using the converged v2.0 authentication endpoints. It allows management of your application using the new app registration portal and nicely supports implicit flow and dynamic scopes. You can find more information about the actual authentication flow here. It isn't far from what you are doing now and requires only a few small tweaks.
If you are still having issues after this, please reach out again. A fiddler / network trace would be very helpful. Also, the detailed message inside the exception would also be very helpful.
Related
I am able generate access token with docusign site by using link https://developers.docusign.com/oauth-token-generator
But when try to get access token in our system using c# code then getting message (The remote server returned an error: (400) Bad Request.)
I follow the authenticate process mentioned in below link.
https://developers.docusign.com/esign-rest-api/guides/authentication/oauth2-code-grant
I able to get authentication code. I used this authentication code to hit API (https://account-d.docusign.com/oauth/token).
Below is my code sample
string integrationKey = "key removed";
string secretKey = "key removed";
var httpWebRequest = (HttpWebRequest)WebRequest.Create("https://account-d.docusign.com/oauth/token");
httpWebRequest.ContentType = "application/x-www-form-urlencoded";
httpWebRequest.Method = "POST";
string apiStoreConsumer = "removed";
httpWebRequest.Headers.Add("Authorization", "Basic " + apiStoreConsumer);
using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
{
string input = "authorization_code&authorization_code= <authentication code goes here>;
streamWriter.Write(input);
streamWriter.Flush();
streamWriter.Close();
}
WebResponse response = (HttpWebResponse)httpWebRequest.GetResponse();
Query:
Why am I getting 400 error?
Do we have any expiry time for access token, if yes then how long?
Does authentication code get change for every request?
Please help me on this.
Thank You!
I recommend you use a library for OAuth in .NET/C#.
If you want to see how this is done, please clone this repo.
The issue is that you need to first get a code and then exchange it for a token. There are 2 steps involved if you do this manually.
The first step requires you to authenticate the user in a browser before you can call any API.
During that step you need to pass in your integration key and redirect back to your URL.
Once redirected back you'll receive a code that can be exchanged for an access token using the API call you had talks about.
List item
I have created a Google App Script REST - Application (starting with "script.google.com/"), that works with HTTP-requests.
The application works fine when it is available to 'everyone, even anonymous' but when I set it available to my domain only [EDIT:] OR "only myself" from the publish/deploy as WebApp[/EDIT], I can only access the web app with browser and signing in but not with http request.
I have tried requesting an authorization token with both Google OAuth Playground and an android application based on a Xamarin Auth Tutorial.
Both methods have resulted me a working authorization token that I can copy+paste to an other platform an confirm it is working with a request to https://wwww.googlapis.com/plus/v1/people/me.
I can access the Web app with browser and signing in. Now when I call my script with http request I get the following result:
"<HTML> <HEAD> <TITLE>Unauthorized</TITLE> </HEAD> <BODY BGCOLOR="#FFFFFF" TEXT="#000000"> <H1>Unauthorized</H1> <H2>Error 401</H2> </BODY> </HTML>"
I have tried to call the Web App with another App Script:
var headers = {
"authorization": "Bearer [access_token]",
};
var params = {
method:"GET",
contentType:"application/json",
muteHttpExceptions:true,
headers:headers,
};
var url = "https://script.google.com/[rest_of_the_script_url]";
var response = UrlFetchApp.fetch(url, params);
Also I have tried calling the Web App with C# OAuth2Request (Taken from the Xamarin tutorial):
var url = "https://script.google.com/[rest_of_the_script_url]";
var request = new OAuth2Request("GET", new Uri(url), null, account );
Also I have tried C# HttpWebRequest:
string accessToken = "Bearer [access_token]";
string url = "[https://script.google.com/[rest_of_the_script_url]";
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.create(url);
request.Method = "GET";
request.ContentType = "application/json";
request.Headers.Add("Authorization", accessToken);
var response = request.getResponse();
All previous methods have the same result: "(401) Unauthorized".
For scopes I have set:
https://www.googleapis.com/auth/plus.me
https://www.googleapis.com/auth/userinfo.email
My WebApp does not require any scopes according to it's properties.
[EDIT:] Also to make sure it does not I did set a doGet() method as simple as possible:
function doGet(e)
{
return ContentService.CreateTextOutput("success");
}
This question has been asked before, but some have found the solution and some have not. Also I did not success with the answers either.
I think my first attempt covers this one.
I tried to translate the Java answer to C#
Ok, thanks for reading down here, wish some one can help me out with this as I'm running out of ideas (and time, eventually).
EDIT:
Though the issue has resolved and turned out to be a scope-issue I am answering the questions in the comments below, in case this question might be of any help to anyone in the future.
I was able to get this to work with an access token authorized with the https://www.googleapis.com/auth/drive.file scope in the Google OAuth Playground.
That doesn't seem quite right in my opinion, but it's the least permissive scope I got to work.
I am trying to connect to a web service from Lockheed Martin located here. I have looked at other examples and am using the following code to try and establish a connection. All I want to know at this point is if I have established a connection and been authorized but I repeatedly get an exception saying
Unauthorized at System.Net.HttpWebRequest.GetResponse()
. Am I setting up the web request and response correctly? Is there a different method that would simply let me know if I've successfully connected?
try
{
//Connect to the Lockheed Martin web client
WebRequest client = WebRequest.Create("https://www.elabs.testafss.net/Website2/ws");
string username = "username";
string password = "password";
string credentials = Convert.ToBase64String(ASCIIEncoding.ASCII.GetBytes(username + ":" + password));
client.Headers.Add("Authorization", "Basic " + credentials);
WebResponse response = client.GetResponse();
Console.WriteLine(((HttpWebResponse)response).StatusDescription);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
Probably your user / password is incorrect, looking at the documentation of the web service, your code is reproducing the exactly same hash that is in following sample:
Authentication - Basic Auth
Authentication is performed using the Basic Auth protocol. An authorization header is supplied with every web service request. This is sometimes called pre-emptive authentication. The header looks like this:
Authorization: Basic Vendor_ID:Vendor_Password
where the Vendor_ID:Vendor_Password string is converted to Base64.
Example
Authorization: Basic JoesFlightServices:SecretPW
Converted to Base64:
Authorization: Basic Sm9lc0ZsaWdodFNlcnZpY2VzOlNlY3JldFBX
Note that conversion to Base64 does not ensure the information will be private. We use HTTPS to encrypt the entire HTTP message including the headers.
Source
I am struggling to call APIs hosted in Google Cloud https://apis-explorer.appspot.com/apis-explorer/?base=https%3A%2F%2Finnovative-glass.appspot.com%2F_ah%2Fapi#p/mirror/v1/
Up to my understanding, APIs are exposed as REST service. I need to make rest service call from .net application.
I have done OAuth Authentication. I am passing access_token as per the guidance given https://developers.google.com/accounts/docs/OAuth2WebServer
My Code:
UriBuilder uriBuilder = new UriBuilder("https://innovative-glass.appspot.com/_ah/api/mirror/v1/timeline");
string userId = Session["userId"] as string;
var state = Utils.GetStoredCredentials(userId);
NameValueCollection queryParameters = HttpUtility.ParseQueryString(uriBuilder.Query);
queryParameters.Set("access_token", state.AccessToken);
uriBuilder.Query = queryParameters.ToString();
var request = (HttpWebRequest)WebRequest.Create(uriBuilder.ToString());
request.Method = "GET";
var response = (HttpWebResponse)request.GetResponse();
I am getting UnAuthorized exception.
Is my understanding is right? Am I doing right way?
It seems that cloud endpoints don't use the Google API default of accepting access_token as query parameter. According to the Discovery document the equivalent query parameter is oauth_token so it should work with:
queryParameters.Set("oauth_token", state.AccessToken);
Alternatively (which in my opinion is the better solution) you can also set the Authorization header of the request instead of adding the token as query parameter:
request.Headers.Add("Authorization", "Bearer " + state.AccessToken);
Where are you getting the AccessToken from, and are you sure it is still a valid token? Remember that access tokens expire about an hour after being generated, although can be revoked earlier.
Clicking on the sign-in button at innovative-glass.appspot.com gives me an origin mismatch error, so it looks like you may also have a configuration issue.
Have you been able to get it to work using just the API Explorer?
I'm working with google's OAuth api for web server applications, specifically asp.net mvc, and i'm able to get to the point where google returns an authorization code for a certain oauth request. At that point, I'm trying to obtain the access token using the following code:
public ActionResult GetOAuthToken()
{
HttpWebRequest myReq = (HttpWebRequest)WebRequest.Create(OAuthConfig.getTokenUrl(Request.QueryString["code"].ToString()));
myReq.Method = "POST";
myReq.Host = "accounts.google.com";
myReq.ContentType = "application/x-www-form-urlencoded";
WebResponse resp = myReq.GetResponse();
return View();
}
The OAuthConfig is just a class I wrote that contains a method getTokenUrl(), which returns a url with parameters such as code, client_secret, client_id etc. for the url: https://accounts.google.com/o/oauth2/token. I've debugged and checked that there's nothing wrong with this url.
I keep getting the following error: The remote server returned an error: (411) Length Required.
I don't know what to specify for the content length, or if there's something else that i need to include to fix this error?
Have you tried to have a look at Google-API .NET Client?
If you debug you will see Google uses "length" as an internal property when sending access-token request. you can try to fix it on your own, but you can use THEIR class in order to send the token request; If you use their class you do not have to worry about internal such as length...