I want to implement a REST-api in C#. I found that WCF Webapi can do that.
My first question is how I can give only authenticated users access to my api?
And the second question is if the client to be authenticated is a Android-device, how do I do the HTTP request to authenticate?
Thanks!
em...
We did similar things, we use basic authentication+HTTPS,
that means the user name and password will be passed along each request, in the http header.
Thus in your web service, you can authenticate then, if it is from not valid user, then kick them out.
Or alternatively you can generate a GUID for each of your client, ask then to pass the GUID back to the search along with each http request, authenticate the GUID.
on Android device , when you send out the http request , add an http header
Authorization:Basic ****
quite easy , here is a codesnipet on android
String baseUrl = this.getValue(ServiceBaseUrlKey);</i>
DefaultHttpClient client = new ConnectionManager().getHttpClient();//create a httpclient
HttpGet request = new HttpGet();
request.setURI(new URI(baseUrl + "Path"));
//TODO need to wrap up how to apply the basic authentication.
UsernamePasswordCredentials credentials = new UsernamePasswordCredentials("UserName", "****");
request.addHeader(new BasicScheme().authenticate(credentials, request));
request.addHeader("Content-Type","Application/JSON");
HttpResponse response = client.execute(request);
Related
I have 2 application one is 'dot net mvc' and 'dot net core web api'.
Steps:
if request done from mvc to api, api will again create a request to the mvc app with token it get in request as follow:
using (var client = new HttpClient() { BaseAddress = baseAddress })
var message = new HttpRequestMessage(HttpMethod.Get, "api/Auth");
....
}
So, if the MVC will authenticate that token and return the answer that the person has same token in cookie.
API is also checking the base address which contains the MVC app URL, so no other app can do the request to API and if any do then it will unauthenticated it.
It is working in local and production server, but not in the staging.
Can anyone suggest me something
I have checked the base URL it is correct. it is sending the request to "api/Auth" to MVC correctly.
Your question is not clear to me. Maybe, you should add the access token to httpClient RequestHeaders.
httpClient.DefaultRequestHeaders.Add("Authorization", accessToken);
UPDATE
I was able to get a working request posted. The third-party API has us sending the Token (which is basically a Guid) as a bearer token. Azure appears to do some sort of pre-validation on this. When I swapped out the GUID with a true randomly generated bearer token, it worked.
I do still wonder if there's a way to disable this check-in Azure. The "bad" Bearer token works for GET requests but fails for POST/PUT requests.
Summary of the Application
We have Azure Functions (i.e., Time Trigger, Orchestrator, Activities) that look for items in an on-prem queue table in SQL and then POST it to a third-party API via JSON.
The third-party API requires an Authorization header with the POST request.
Technical Overview
dotnet core 3.1
azure function runtime ~3
Additional Information
This codebase worked fine during UAT back in April-May of this year. It then sat idle until we rebooted the project a couple of weeks ago.
Outbound requests are not proxied through APIM. They're sent directly to the third-party API
Application Insights is configured for the Azure Function
What works
All of the GET requests. No issues at all.
What doesn't work
POST requests. I proxied the requests to a beeceptor to see exactly what was being received. When the Authorization header is included most of the headers are stripped (I.e., Content-Type, Content-Length) and the Body of the request is blank.
If I removed the Authorization header then all headers and body are received as expected.
Question
I can only assume at this point that some Azure service, pre-flight check, security policy is intercepting the Authorization header thinking it's intended for "itself", but I have absolutely no idea what it could be. I've been on Google now for days.
Simplified Version of Code
using var client = new HttpClient();
client.DefaultRequestHeaders.Clear();
// Request params are dynamic and a helper method builds the full request path
var path = PathBuilder(queueItem.RequestParams, queueItem.Request.UrlPath);
// This can change in code not shown if the request is sending files
var contentType = "application/json";
client.BaseAddress = new Uri(queueItem.Request.Client.BaseApiUrl);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.CacheControl = new CacheControlHeaderValue { NoCache = true };
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", queueItem.Request.Client.AuthToken);
// queueItem.Data is JSON
HttpContent json = new StringContent(queueItem.Data, Encoding.UTF8, contentType);
return await client.PostAsync(path, json);
Also...
I've confirmed the JSON body is valid
The code did work and has remain unchanged
Given all that you’ve tried, it might be a long shot, but have you tried to add the token like:
client.DefaultRequestHeaders.TryAddWithoutValidation(“Authorization”, “bearer token here…”);
and then check whether the try succeeded or not?
I have a Postman request sent by a partner/client that requires Oauth2 to hit their web service endpoint. Here is the authorization section:
I need to re-create this request in C#. Should be easy, just click on Code link, and grab the sample code in C# - RestSharp. Two problems:
1- When I execute this in Postman, I get The SAML2 token is not valid because its validity period has ended. So then I click the Get New Access Token button, and I get challenged for credentials:
(With the Client ID and Client Secret, shouldn't I be able to bypass this challenge?)
2- Ultimately I need to be able to run this request in my c# app. When I click the Code link in Postman it gives a nice C# example, but the problem with the sample code generated by Postman is that it assumes the bearer token has already been obtained, and just plops that into the source sample. But the obtaining of the bearer token is a very important piece that Postman omits. Here is my specific example, copied from Postman:
var client = new RestClient("https://myurl-here:7148/foo/ODataV4/WS3_stuff?Company='abc123'");
client.Timeout = -1;
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/json");
request.AddHeader("Authorization", "Bearer ey...massive-string...Zb");
request.AddHeader("Cookie", "ApplicationGatewayAffinity=ab721.more.d6c1a341bc; ApplicationGatewayAffinityCORS=ab..more...bc");
request.AddParameter("application/json", "{a-bunch-of-json-here}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);
What are the pieces I'm missing? How can I get the C# equivalent of whatever Postman is doing to obtain the bearer and those Cookie values? And I thought there's a way for applications to achieve all the Oauth handshaking without sending the user to that MS window as in screenshot above - but how is it done?
ClientID and ClientSecret are there to identify your application not the user. They are sent to the Authorization Server so that the server knows that it can issue access tokens to this application. User authentication is another thing. You will always have to authenticate the user in order to get an access token which allows to access that user's data.
Unless you only need to authenticate your application and want to access data which does not belong to any user. In that case you need a client credentials flow, an OAuth flow which allows your application to get an access token.
Postman is a tool for making calls to APIs, it just generates the code which enables you to make the API call. Have a look at some C# OAuth clients (or maybe something for your framework, if you're using one). Those clients will enable you to easily generate new access tokens. Still, if you need a user's access token remember that you will need your users to open a browser. If you're developing a backend app, which does not serve any pages then you can have a look at the OAuth device flow, which enables you to authenticate users on a different device than your app runs.
As for the cookies - make sure whether you really need them. If you're calling an API chances are that those cookies are not required to make the request.
Some Background Information
I am building a game in Unity, using C#. Since I am using Firebase and the ready-made Unity Firebase SDK won't work in my case1, I have resorted to interfacing with Firebase's REST API through C#'s HttpClient class (which comes with System.Net.Http).
I am currently struggling with the Firebase Authentication APIs. For those unfamiliar with OAuth APIs, when I call the Sign Up or Sign In endpoints, I get both an ID Token and a Refresh Token. ID Tokens expire; refresh tokens do not. When the ID Token expires, I must call another endpoint, called the Token Exchange, to get a new ID token using my refresh token.
1 There is a rumored Google Identity Toolkit C# library, but the first result returned by that search I found it by leads to a 404 error instead of documentation. 😢
What Does Work
Following the Firebase guides and the underlying Identity Toolkit guides, I have been successful so far interfacing the token exchange endpoint with a cURL command from bash:
curl 'https://securetoken.googleapis.com/v1/token?key=[MY FIREBASE API KEY]' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data 'grant_type=refresh_token&refresh_token=[MY REFRESH TOKEN]'
of course, I replace [MY FIREBASE API KEY] with my Firebase Web API key, and [MY REFRESH TOKEN] with the refresh token returned from the sign in/sign up endpoints.
However, despite my many attempts, I have not been able to replicate this cURL command in C#!
My Failed Attempts
1.
Here's my original code that didn't work.
public async Task<bool> ExchangeToken()
{
FormUrlEncodedContent body = new FormUrlEncodedContent(new Dictionary<string, string>() {
{"grant_type", "refresh_token" },
{"refresh_token", Uri.EscapeDataString(user.refreshToken) }
});
HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Post, "https://securetoken.googleapis.com/v1/token?key=" + apiKey);
message.Content = body;
message.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/x-www-form-urlencoded"));
HttpResponseMessage res = await client.SendAsync(message);
}
Unfortunately, I get this 401 (Unauthorized) error response:
{
"error": {
"code": 401,
"message": "Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.",
"status": "UNAUTHENTICATED"
}
}
This is quite strange, considering that I get a 2XX (OK) response from what should be an equivalent cURL command...
2.
Thanks to a very nice website I just discovered, I was able to "convert" my cURL command into C# code. However, this did not work. I got the exact same error as attempt #1.
public async Task<bool> ExchangeToken()
{
using (var request = new HttpRequestMessage(new HttpMethod("POST"), "https://securetoken.googleapis.com/v1/token?key=[I WOULD INSERT MY KEY HERE]"))
{
request.Content = new StringContent("grant_type=refresh_token&refresh_token=[INSERT REFRESH TOKEN HERE]", Encoding.UTF8, "application/x-www-form-urlencoded");
var res = await client.SendAsync(request);
}
}
Possible Leads
All of my other requests to all of the other endpoints work. There are two notable differences: 1) the API is technically not Firebase's, but Google Identity Toolkit's. 2) This is the only endpoint that I'm using that uses a Content-Type header of application/x-www-form-urlencoded instead of application/json.
My Question / TL;DR
How do I interface with the Google Identity Toolkit API's Token Exchange endpoint using C#?
(Though I'm currently using the HttpClient class, I'm totally open to other solutions! They just have to be compatible with Unity3D.)
Thanks in advance!
Google wants you to be using Firebase for auth. While Firebase does not have a .NET client SDK shipped by Google, there are other options.
If you don't use Firebase and perform the requisite tasks with oAuth/OpenID Connect endpoints yourself, that works too. There is a Google .NET SDK that helps you with this...a little bit . The SDK lists Google APIs that are "supported" by the SDK; Google Cloud Identity v1 and v1beta APIs are both on the list. So you could call Google Cloud Identity endpoints via this .NET SDK. You do have to understand the implications of various auth flows whereas in Firebase much of that machinery is abstracted away from you as the app developer.
After playing around with the code a little more, I remembered that I was setting default authorization on my HttpClient:
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", idToken);
This was included for all of the other API calls, as they require authentication with the ID Token. However, this seemed to be confusing the token exchange endpoint; it must 1) look for an authorization header, then 2) use the refresh token supplied in the message contents.
How to fix this issue
Instead of setting the default header on your HttpClient (Which is supposed to be reused for all of your HTTP requests), do the following:
var req = new HttpRequestMessage(/* HttpMethod enum value, endpoint URL/IP */);
req.Headers.Authorization = new AuthenticationHeaderValue("Bearer", user.idToken); //Set the authorization per-request, not globally
var res = await client.SendAsync(req);
Then just simply call the Token Exchange endpoint!
From the Firebase REST API Documentation:
You can refresh a Firebase ID token by issuing an HTTP POST request to the securetoken.googleapis.com endpoint.
https://securetoken.googleapis.com/v1/token?key=[API_KEY]
Just provide a refresh_token parameter in the body (along with the mandatory grant_type with value refresh_token). DO NOT provide an authorization header.
Hope this helps anyone with the same problem!
As an alternative, you can also use https://github.com/google/apis-client-generator to generate the c# client from the discovery docs. I had some naming conflicts when I generated mine, but after resolving those it works.
I have an on-premise Dynamics CRM (2016) that is configured with ADFS (3.0). When a user want's to Login, they get redirected to the ADFS login page and the user enter their Windows AD credentials.
From a .net core application I need to make request to the CRM api using HttpClient. When I try to send the credentials like I normally would for a Windows Auth CRM it doesnt work. I get a 401 Unauthorized. Like below.
HttpClient client = new HttpClient(new HttpClientHandler() { Credentials = new NetworkCredential("myuser", "mypassword", "mydomain") });
var result = client.GetAsync("https://mycrmaddress/api/data/v8.0/accounts");
I also tried using Adal to retrieve a token and attach it as a bearer token to the request but I'm unable to get a token with adal. When I try I receive the following:
The authorization server does not support the requested 'grant_type'. The authorization server only supports 'authorization_code'
ADFS 3.0 doesn't support this flow.
I cannot upgrade to ADFS 4.0 so I would like to know what are my options to make an authenticated call to CRM api (without prompting a login window as this application is a service).
Is there any configuration I can do on ADFS so my first example work? Or is it possible to do it with Adal even if it's ADFS 3.0? Or any other solution...
I found the answer to my question. It's kinda hackish, but I tested it myself and it works. As a temporary solution this will do the trick.
Details are available here: https://community.dynamics.com/crm/f/117/t/255985
ADFS 3.0 supports the Authorization Code flow and this what we will use in this case.
We need to retrieve an authorization code. Normally at this steps a windows is prompted to the user to enter its credentials. By doing a POST and sending the user/password it's possible to retrieve an authorization code.
{authProvider} - ADFS Uri - something like
https://adfs.mycompany.com/adfs/oauth2/
{ClientId} - The Guid used to
by your infrastructure team to add your application to ADFS
{RedirectUri} - The IFD Uri for dynamics - should match the redirect
Url used to by your infrastructure team to add your application to
ADFS
username - The User set up on ADFS and in Dynamics
password - The password for the above user
Then we make the following call with these information using HttpClient.
var uri = $"{authProvider}authorize?response_type=code&client_id={clientId}&resource={redirectUri}&redirect_uri={redirectUri}";
var content = new FormUrlEncodedContent(new[] {
new KeyValuePair<string,string>("username",username),
new KeyValuePair<string,string>("password",password),
});
var responseResult = _httpManager.PostAsync(uri, content).Result;
The response content will be an html page (Remember normally this flow prompts a login page to the user). In this page there will be a form that contains the authorization code. using a library like HtmlAgilityPack retrieve the token. This is the hackish part of the solution.
Now that we have an authorization code we need to retrieve an access token.
For that we need to make the following call
var uri = $"{authProvider}token";
var content = new FormUrlEncodedContent(new[] {
new KeyValuePair<string,string>("grant_type","authorization_code"),
new KeyValuePair<string,string>("client_id",clientId),
new KeyValuePair<string,string>("redirect_uri",redirectUri),
new KeyValuePair<string,string>("code",code)
});
var response = await _httpManager.PostAsync(uri, content);
The response content will be a json string that will contain the access token.
With the access token, make the call to CRM rest API.
You will need to attach the token to the HttpClient in the header as a bearer token.
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer",token);
httpClient.DefaultRequestHeaders.Add("OData-MaxVersion", "4.0");
httpClient.DefaultRequestHeaders.Add("OData-Version", "4.0");
From now on you can make calls to CRM api and you will be authorized. However be carefull normally access token are short lived. You will either need to increase their lifetime or request a new token everytime it's expired.