Receiving BAD REQUEST when called Okta API from C# MVC application - c#

I am creating a sample application (i.e., a proof of concept) for creating users with the Okta platform. I am using API calls but consistently receiving "BAD REQUEST" when running the C# MVC application from Visual Studio 2013 update 5 to my Okta development instance. I'm wondering if the problem is between CORS and a local app?
Here is what I have so far:
Tested out the API calls using Postman to my dev environment and the calls work (i.e., users get created in my Okta dev admin environment)
Created an API Token and call it with a prefix of "SSWS" in the Authorization header
Using an HttpClient and .PostAsJsonAsync() method to make the API call
My application code works as expected when calling a GET with the API call /api/v1/users?limit=25 and .GetAsync()
Using the following Api call: /api/v1/users?activate=false (create a user with password; this works in Postman, but not in the MVC app)
Used http://json2csharp.com/ to create C# classes that conform to Okta's JSON hierarchy (obtained from Okta's Postman API libraries)
Using the classes above, the JSON displayed in Visual Studio's Text Viewer (obtained while stepping through the code) works with a POST call when pasted into Postman
HttpResponse contains the error message "The request body was not well-formed"
Here is the code used for creating and serializing (with Json.NET) the C# classes:
RootObject root = new RootObject();
root.profile = new Profile();
root.profile.firstName = model.FirstName;
root.profile.lastName = model.LastName;
root.profile.email = model.Email;
root.profile.login = model.UserName;
root.credentials = new Credentials();
root.credentials.password = new OktaTest.Models.Password();
root.credentials.password.value = model.Password;
string rootJson = JsonConvert.SerializeObject(root);
This produces the following JSON (this contains dummy data):
{"profile":{"firstName":"Test","lastName":"User","email":"user#test.org","login":"user#test.org"},"credentials":{"password":{"value":"Testing123"}}}
Here is the line of code that makes the POST call:
HttpResponseMessage responseMessage = await client.PostAsJsonAsync(url, rootJson);
Here is the line that sets the Accept header:
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
Since I'm able to use the JSON in Postman, and since that JSON comes out as valid when using JSONLint, I'm thinking that the problem is not with the JSON but something around security between my local app and the development environment. Should this test only be run from a hosted application so that a site can be explicitly assigned in the CORS section of the Okta admin environment? At this point, and I'm still researching and experimenting, I'm not sure what I'm missing, but I think I'm close.
Any advice or guidance would be greatly appreciated! Thank you!

I recommend you to use the Okta C# SDK which you can add to your application using the Okta.Core.Client NuGet package.
A sample console app shows how to use it to create Okta users: https://github.com/oktadeveloper/okta-sdk-dotnet-console-user-create
I hope this helps!

Related

Call Dynamics Web API in C# Plugin

I have a Business Process Flow in Microsoft Dynamics to handle creation of a new client. When the process finishes, I am attaching a workflow that kicks of an action which calls a plugin to do some custom processing. I am following this article to set up this process.
In my plugin, I have a call to the Dynamics Web API (see code below). When I hit the responseMessage = client.GetAsync(url).Result; line, my plugin exits without returning an error. When I am debugging in the Plugin Registration Tool, the Plugin Registration Tool crashes and has to be restarted. When I look at the network traffic, this call appears to be failing with a 401 - Unauthorized error.
When I try this same HttpClient call from a console app, the call succeeds. I've tried a few different credentials for authentication without success. I've also tried calling the GetAsync function a few different ways. Is this error related to the async or authentication methods? What am I missing here?
HttpClient client = new HttpClient(new HttpClientHandler() { Credentials = new NetworkCredential("admin", "password", "DOMAIN") });
client.BaseAddress = new Uri(Helpers.GetSystemUrl(COHEN.APIConnector.Application.Dynamics));
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Add("OData-MaxVersion", "4.0");
client.DefaultRequestHeaders.Add("OData-Version", "4.0");
HttpResponseMessage responseMessage;
string url = "ccseq_clients";
responseMessage = client.GetAsync(url).Result;
Update
We are using Dynamics 2016 On-Premise.
The reason we are using the Web API is that this call occurs in a library we have developed that will interface between multiple systems. The library is used within a plugin, but can also be used outside of a plugin for other applications. The code sample we provided is the chunk of code that is failing in the library. We pulled the chunk of code directly into the plugin to see if the error was related more to the library or the plugin.
We noticed that the client.GetAsync(url).Result call was actually returning a correct value. We missed seeing in our network traffic that two calls failed before a third succeeded and returned the correct value (See screenshot below). The odd thing is that when we debug this line of code, stepping over the line of code causing the Plugin Registration Tool to crash while the network traffic shows we returned the correct values. If we set a breakpoint after this line of code, then the Plugin Registration Tool does not crash and we have a correct value in our response. This behavior changes when we went back to debugging in the library rather than directly in the plugin. In the library, the Plugin Registration Tool always crashes when hitting this line regardless of where we set our breakpoints even though the network traffic still shows a successful response.
Update 2
It would appear that my library is successfully making multiple different calls to the Web API so I'm thinking the issue is more that the Plugin Registration Tool can't handle the Web API call more than the call not actually working.
I believe the issue was with the Plugin Registration Tool profiling a plugin execution. My follow up question is here.
This blog states that we can make Web Api work in CRM on-premise (non-IFD) plugin by using WebClient with DefaultCredentials.
using (WebClient client = new WebClient())
{           
client.UseDefaultCredentials = true;
    byte[] responseBytes = client.DownloadData(new Uri("<your web api url>/accounts(3C2D2712-E43F-E411-9402-005056AB452C)"));
    string response = Encoding.UTF8.GetString(responseBytes);
    // parse the json response
}
Make sure you specify the UseDefaultCredentials as true to make the web api call under the context of the user the plugin is running.
We may struggle more with Online plugins because of Sandbox + Adal library + AAD tokens combo as we are trying in a non-interactive plugin code unlike other interface where we can challenge the user by prompt.
Moreover Web api is more useful for cross platform, outside CRM context integration. Inside the platform execution you can use Org service to achieve more.

Handling credentials when accessing SharePoint data from .Net application

I am trying to access some SharePoint lists from a .Net (C#) web application. I googled the topic and it seemed the consensus was to use WCF Data Service (steps outlined below). After I get the data, I display it in a jQuery datatable, using Ajax web service call to grab the data.
Firt step was to run DataSvcUtil.exe to create a DataService.cs file that contains the client data service classes that are needed to access a data service from a .NET Framework client application. Then added references to Microsoft.Data.OData.dll and Microsoft.Data.Services.Client.dll.
I created a web service API (below). Run locally, from within VS 2017, or from IIS on application server, I get all the data, albeit in 80 seconds (way too long for 16,000 records). However when I ran this from a browser, I got status code 500 error and found out it was due to credential issue.
I was setting credentials to CredentialCache.DefaultCrdentilas (later changed to DefaultNetworkCredentials) but had to use NetworkCrdentials(UserID, pwd, domain).
[WebMethod]
[ScriptMethod(UseHttpGet = false, ResponseFormat = ResponseFormat.Json)]
public string GetCompletedAssessments()
{
SPService_DataContext dc = new SPService_DataContext(new Uri("http://my_sp_site_.com/sites/nvx/SPService/_vti_bin/listdata.svc/"));
dc.Credentials = System.Net.CredentialCache.DefaultCredentials;
// Had to change the above to this to get it to work
dc.Credentials = new System.Net.NetworkCredential(MyUserID, MyPassword, DomainName);
var source = dc.CompletedAssessments;
string JSONresult = JsonConvert.SerializeObject(source);
return JSONresult;
}
However, I was just testing it using my credentials which is not feasible. Is there anything I can do using the method of accessing data I am using, or should I use some other way of accessing the data that does not require providing credentials?
Application is on a server in same domain as the server SharePoint in on but in a different location.
If you want the application to read the data, you must supply the credentials.
You could try to use REST or JSOM to fetch the data from the client. That way the users credentials are used and the result will be security trimmed.
REST example: http://my_sp_site_.com/sites/nvx/SPService/_api/web/lists/GetByTitle('Pages')/items
If you add an Accept header with application/json;odata=verbose, the result is in JSON. Otherwise its in XML.
SharePoint REST documentation: https://learn.microsoft.com/en-us/sharepoint/dev/sp-add-ins/working-with-lists-and-list-items-with-rest

HTTP Request in Xamarin.Forms

I'm building a Xamarin.Forms App for Android and iOS which is supposed to consume an API. The API is kind of built in a REST spirit, I'm hosting the API on a local IIS Express server and my Android emulator has no problem finding it (tested on various endpoints and I can also display the api-doc).
I'm trying to get in touch with a http://IPOfMyComputer/api/Stuffs endpoint which gives me a JSON list of Stuff. Using Postman I successfully get all the stuff I needed, a valid auth token in that case, to build a proper HTTP GET request. I'm using the Microsoft.Net.Http NuGet package to build this request as it seemed to be the proper tool to do the job. I'm doing this request building process in Xamarin.Forms "Main" project (not the iOS or Android one, the other one which I don't know how to name which is automatically by Visual Studio).
My code looks like this:
var rootUri = "http://IPOfMyComputer"
var requestedUri = "/api/Stuffs"
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer",
"blablabla");
var response = await client.GetAsync(rootUri + requestedUri);
And this gives me an error "NotSupportedException" on the last instruction.
I'm clueless here so some help would be really appreciated.
Edit
The proper error message is
"System.InvalidCastException: Specified cast is not valid."
I found the source of that error:
Turns out it wasn't directly linked with the GetAsync method but with some mess in the code related with async/await use.
There was a bunch of crappy code everywhere. I dropped it all to go back to a regular MVVM logic and I put the async/await logic into methods invoked by eventhandlers (instead of trying to "create" an async builder for a class...).

PowerBI Embedded "Provision Token" generation error

I am trying to follow this PowerBI embedded example and am getting an error:
'PowerBIToken' does not contain a definition for 'CreateProvisionToken'
This MSDN article describes the CreateProvisionToken() method.
I installed latest Microsoft.PowerBI.Core and Microsoft.PowerBI.Api using NuGet package manager
included Microsoft.PowerBI.Security and Microsoft.PowerBI.Api.V1 in my project.
I also tried changing CreateProvisionToken() part to the following:
var credentials = new TokenCredentials(accessKey, "AppKey");
var client = new PowerBIClient(credentials);
var devToken = PowerBIToken. CreateDevToken(reportID, workspaceID);
using (client)
{
var embedToken = PowerBIToken.CreateReportEmbedToken(
collectionName,
workspaceID,
reportID);
var embedTokenString = embedToken.Generate(accessKey);
}
Which generates a token which "looks about right", but using this token in the example html in the end of the article results in an error This content is not available when rendering the report.
What am I missing?
The latest version of Power BI embedded simplifies the authentication mechanism by only support one embedded token, no other tokens anymore from Power BI blog:
https://powerbi.microsoft.com/en-us/blog/what-s-new-and-what-s-next-for-power-bi-embedded-july-2016/
We have simplified the way developers authenticate their calls to Power BI. From today on, app tokens will only be used to authenticate embedding requests and other client side requests that may be added in the future. All calls to our REST APIs will be authenticated using our API keys directly. This eliminates the need to generate app tokens each time you want to call the REST API.
You need follow up the example from Microsoft:
https://github.com/Azure-Samples/power-bi-embedded-integrate-report-into-web-app/blob/master/EmbedSample/Controllers/DashboardController.cs

Google Contacts API version 3.0 (.NET) - OAuth2.0 CRUD Operations

I have setup up a .NET console application that will do the following:
Access the Google Contacts API for my personal Google Account
Perform basic CRUD (create, read, update, delete) operations to these contacts
I believe executing the CRUD operations will be straight forward using the following documentation:
https://developers.google.com/google-apps/contacts/v3/#about_authorization_protocols
However, my problems are occurring when trying to authenticate my connection using OAuth2.0.
I am using the Google.GData.Contacts .NET NUGET Package using the code from the following example:
https://code.google.com/p/google-gdata/source/browse/trunk/clients/cs/samples/oauth2_sample/oauth2demo.cs?r=1159
OAuth2Parameters parameters = new OAuth2Parameters() {
ClientId = clientId,
ClientSecret = clientSecret,
RedirectUri = redirectUri,
Scope = scopes
};
string url = OAuthUtil.CreateOAuth2AuthorizationUrl(parameters);
Console.WriteLine("Authorize URI: " + url);
parameters.AccessCode = Console.ReadLine();
OAuthUtil.GetAccessToken(parameters);
As this code was last updated in 2012, I'm worried it might no longer be relevant for my task.
Major Questions:
I'm not sure exactly what the parameters.AccessCode value is. Where does this value come from?
Based upon my use case am I using the correct OAuth2.0 approach? You can can setup authentication for a Service Account, Web Application, or Native Application. The above code implies Native Application
Is there a better way to handle my task?
The access code should be used only for WebServer Applications.
I'm not sure if your console app will run in an environment where a browser can be launched or not.
If yes (your app is running in a environment where a URL can be launched), use the documentation for Installed Applications.
If browser is not present, you can use the documentation for Applications on limited-input devices.
If the library you're targeting to use (NUGET, whatever) has no support for this flow, don't worry: it's easy to write custom code. You can use any library able to do HTTP/HTTPS requests, like or Windows.Web.Http.HttpClient (Windows 8 and later).
Additionally, you can use contact import services like CloudSponge, which offers a .Net API and support for other contact sources (in case you want expand your address book support in the future).
AccessCode comes from the URL created in that Line above. See here:
string url = OAuthUtil.CreateOAuth2AuthorizationUrl(parameters);
url contains a URL now for your browser. Fill it in Chrome/Firefox/etc and confirm the google request to get the AccessCode. It will look like this "4/bAQT1qf66vpkmfU8xDkoVZmedvVoYDH6KovzcHcA5pc" but will change every time you call the URL.
Console.WriteLine("Authorize URI: " + url);
now you can see the url but you might not be able to copy it from your console. But if you get it into your browser, it will result in a google dialog to get your AccessCode.

Categories

Resources