I have an MVC5 application in which I use Owin to authentiate users,
now I'm trying to create a Windows 8 app for this application. But I can't find the best why to implement authentication. I found two possible solutions "Web Authentication Broker" and "Credential Locker" but I'm not sure yet
using (var client = new HttpClient())
{
HttpResponseMessage response = null;
var requestContent = string.Format("UserLogin={0}&UserPassword={1}", userName, password);
HttpContent content = new StringContent(requestContent);
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
Task task = Task.Run(async () =>
{
response = await client.PostAsync(new Uri((string)ApplicationData.Current.LocalSettings.Values["LoginApiUrl"]), content);
});
task.Wait(); // Wait
var responseData = await response.Content.ReadAsStringAsync();
var deserializedResponse = JsonConvert.DeserializeObject<Dictionary<string, string>>(responseData);
if (deserializedResponse.ContainsKey("success") && Convert.ToBoolean(deserializedResponse["success"]))
{
LoadData();
}
}
Related
We have an API that utilizes a service account (work account type - not a personal account) to do the following:
Using our Service Account, Gets a Token from our Azure AD with scopes: ["user.read", "User.Read.All", "Files.ReadWrite.All", "ChatMessage.Send", "Chat.Create", "Chat.ReadWrite"]
Use that token to upload a file to our SharePoint. -> This succeeds
Use that token to Get the UserID from email (UserPrincipalNames map in our AD, so this is not a concern) -> This succeeds
Use that token to Create a new chat/Get the existing chat between the user & our service account (using POST /v1.0/chats) -> This returns 401 Unauthorized
Send the message from service account to user (using POST /v1.0/chats/{chatID returned from step 4}/messages)
The users we are attemping to send these messages to are all in our Azure AD.
We are unsure how the previous requests with this token succeed, and the Create/Get Chat fails with a 401. We have confirmed that the token is correctly being set in the authorization header (we are using the exact same process as the previous requests).
Also, we have these API Permissions set in our Azure AD for this application:
If we use jwt.io to examine the token, we do see the scope is set in the token being set in the Authorization header of the request.
Additionally the audience is set to Graph API:
Here is our code:
using Microsoft.Graph;
using Newtonsoft.Json;
using OurAPI.Helpers.IHelpers;
using OurAPI.Models;
using OurAPI.Repositories.IRepositories;
using System.Text;
namespace OurAPI.Repositories
{
public class TeamsRepository : ITeamsRepository
{
private readonly IConfiguration _config;
private readonly ITokenHelper _tokenHelper;
private readonly IHttpClientFactory _httpClientFactory;
public TeamsRepository(IConfiguration config, ITokenHelper tokenHelper, IHttpClientFactory httpClientFactory)
{
_config = config;
_tokenHelper = tokenHelper;
_httpClientFactory = httpClientFactory;
}
public async Task<object> GeneratePDFAndSendToTeams(TeamsPostRequest request)
{
// Authenticate
string token = await _tokenHelper.GetMicrosoftGraphAccessToken();
// Convert from Base64 to Memory Stream
var file = GeneratePDF(request.Base64);
// Upload PDF to SharePoint
var sharepointFile = await UploadFile(file, request, token);
// Get UserID from email
var user = await GetUser(request.User, token);
// Create a new chat with service account or Retrieve Chat if already exists
var chat = await GetChat(user.Id, token);
// Post to chat between service account and requesting user
var chatMessage = await SendToTeams(sharepointFile, chat, token);
return chat;
}
public async Task<object> GetChats(string token)
{
using var request = new HttpRequestMessage(HttpMethod.Get, "https://graph.microsoft.com/v1.0/users/serviceaccount#domain.com/chats");
request.Headers.Add("authorization", "Bearer " + token);
request.Headers.Add("accept", "application/json");
var httpClient = _httpClientFactory.CreateClient();
var response = await httpClient.SendAsync(request);
response.EnsureSuccessStatusCode();
var contents = await response.Content.ReadAsStringAsync();
var chats = JsonConvert.DeserializeObject(contents);
return chats;
}
public async Task<User> GetUser(string userEmail, string token)
{
using var request = new HttpRequestMessage(HttpMethod.Get, "https://graph.microsoft.com/v1.0/users/" + userEmail);
request.Headers.Add("authorization", "Bearer " + token);
request.Headers.Add("accept", "application/json");
var httpClient = _httpClientFactory.CreateClient();
var response = await httpClient.SendAsync(request);
response.EnsureSuccessStatusCode();
var contents = await response.Content.ReadAsStringAsync();
var user = JsonConvert.DeserializeObject<User>(contents);
return user;
}
public async Task<Chat> GetChat(string userID, string token)
{
var chatRequest = new Models.ChatRequest(userID);
chatRequest.ChatType = "oneOnOne";
var chatMember = new ChatMember();
chatMember.Type = "#microsoft.graph.aadUserConversationMember";
chatMember.Roles = new List<string>() { "owner" };
chatMember.User = $"https://graph.microsoft.com/beta/users('{userID}')";
var serviceAccount = new ChatMember();
serviceAccount.Type = "#microsoft.graph.aadUserConversationMember";
serviceAccount.Roles = new List<string>() { "owner" };
serviceAccount.User = "https://graph.microsoft.com/beta/users('{OurServiceAccountID}')";
chatRequest.Members = new List<ChatMember> { chatMember, serviceAccount };
using var request = new HttpRequestMessage(HttpMethod.Post, "https://graph.microsoft.com/v1.0/chats");
request.Headers.Add("authorization", "Bearer " + token);
request.Headers.Add("accept", "application/json");
string content = JsonConvert.SerializeObject(chatRequest);
request.Content = new StringContent(content, Encoding.UTF8, "application/json");
var httpClient = _httpClientFactory.CreateClient();
var response = await httpClient.SendAsync(request);
response.EnsureSuccessStatusCode();
var contents = await response.Content.ReadAsStringAsync();
var chat = JsonConvert.DeserializeObject<Chat>(contents);
return chat;
}
// POST /chats/{chat-id}/messages
public async Task<ChatMessage> SendToTeams(DriveItem sharePointFile, Chat chat, string token)
{
var chatMessageRequest = new Models.ChatMessageRequest(sharePointFile);
using var request = new HttpRequestMessage(HttpMethod.Post, "https://graph.microsoft.com/v1.0/chats/" + chat.Id + "/messages");
request.Headers.Add("authorization", "Bearer " + token);
request.Headers.Add("accept", "application/json");
request.Content = new StringContent(JsonConvert.SerializeObject(chatMessageRequest), Encoding.UTF8, "application/json");
var httpClient = _httpClientFactory.CreateClient();
var response = await httpClient.SendAsync(request);
//response.EnsureSuccessStatusCode();
var contents = await response.Content.ReadAsStringAsync();
var chatMessageResponse = JsonConvert.DeserializeObject<ChatMessage>(contents);
return chatMessageResponse;
}
public async Task<DriveItem> UploadFile(MemoryStream file, TeamsPostRequest teamsPostRequest, string token)
{
string siteID = "{ourSiteID}";
string PTparentID = "{ourParentID}"
string custNameForFile = teamsPostRequest.CustomerName;
using var request = new HttpRequestMessage(HttpMethod.Put, _config["Graph:BaseUrl"] + "/sites/" + siteID + "/drive/items/" + PTparentID + ":/" + custNameForFile + teamsPostRequest.PickTicketNo + ".pdf:/content");
request.Headers.Add("authorization", "Bearer " + token);
request.Headers.Add("accept", "application/json");
request.Content = new StreamContent(file);
var httpClient = _httpClientFactory.CreateClient();
var response = await httpClient.SendAsync(request);
response.EnsureSuccessStatusCode();
var contents = await response.Content.ReadAsStringAsync();
var driveItem = JsonConvert.DeserializeObject<DriveItem>(contents);
return driveItem;
}
private static MemoryStream GeneratePDF(string base64)
{
byte[] bytes = Convert.FromBase64String(base64);
MemoryStream ms = new(bytes);
return ms;
}
}
}
Here is the request we are making to get the token:
public async Task<string> GetMicrosoftGraphAccessToken()
{
string authority = "https://login.microsoftonline.com/{ourTenantID}/";
string[] scopes = new string[] { "user.read", "User.Read.All", "Files.ReadWrite.All", "ChatMessage.Send", "Chat.Create", "Chat.ReadWrite" };
IPublicClientApplication app = PublicClientApplicationBuilder.Create(_config["AzureAD:ClientId"])
.WithAuthority(authority)
.Build();
var securePassword = new SecureString();
foreach (char c in _config["MicrosoftGraph:Password"])
{
securePassword.AppendChar(c);
}
AuthenticationResult result = await app.AcquireTokenByUsernamePassword(scopes, _config["MicrosoftGraph:Username"], securePassword)
.ExecuteAsync();
return result.AccessToken;
}
We are at a lose for what could be going on here. Any ideas would be greatly appreciated!
It helps to see the details of the token.
Copy the token, paste it at https://jwt.ms and inspect the details.
You’re probably interested in the scopes.
I guess the account you’re using should be one of the participants of the chat, this cannot be used to create chats between other users.
https://learn.microsoft.com/en-us/graph/api/chat-post you seem to be doing the right request.
I am using a library for communicate with a crypto api.
In Console Application every thing is ok about async post.
But in webforms it goes to sleep and nothing happens!
Just loading mode.
Here is the method :
private async Task<WebCallResult<T>> PostAsync<T>(string url, object obj = null, CancellationToken cancellationToken = default(CancellationToken))
{
using (var client = GetHttpClient())
{
var data = JsonConvert.SerializeObject(obj ?? new object());
var response = await client.PostAsync($"{url}", new StringContent(data, Encoding.UTF8, "application/json"), cancellationToken);
var content = await response.Content.ReadAsStringAsync();
// Return
return this.EvaluateResponse<T>(response, content);
}
}
private HttpClient GetHttpClient()
{
var handler = new HttpClientHandler()
{
AllowAutoRedirect = false
};
var client = new HttpClient(handler);
client.BaseAddress = new Uri(this.EndpointUrl);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
return client;
}
In line var response = web site goes to loading mode.
No error - Nothing - Just Loading.
How can i fix this problem in webforms?
In Console Application it is working fine.
Maybe i should change something in Web.config!!!
Here is the library
I am trying to create a C# console application to download project details from a website which supports REST OAuth 2.0. How do I make a request/response call to the website using the Access Token?
Here is my code:
public string token = "4bjskfa2-b37d-6244-8413-3358b18c91b6";
public async Task GetProjectsAsync()
{
try
{
HttpClient client = new HttpClient();
var projects = "https://app.rakenapp.com/api/v2/projects?" + token;
client.CancelPendingRequests();
HttpResponseMessage output = await client.GetAsync(projects);
if (output.IsSuccessStatusCode)
{
string response = await output.Content.ReadAsStringAsync();
project proj = JsonConvert.DeserializeObject<project>(response);
if (proj != null)
{
Console.WriteLine(proj.name); // You will get the projects here.
}
}
}
catch (Exception ex)
{
//catching the exception
}
}
you need to add a header to your request:
string url = "https://app.rakenapp.com/api/v2/projects";
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authorizationToken);
HttpResponseMessage response = await httpClient.GetAsync(url);
var contents = await response.Content.ReadAsStringAsync();
var model = JsonConvert.DeserializeObject<project>.contents);
return model;
}
I'm working with Windows Phone Runtime API.
I declare a timer, which every 2 seconds does async http connection in Listen method.
Timer t = new Timer(Listen, null, 0, 2000);
Listen method:
private async void Listen(object state)
{
string url = "http://www.mywebpage.com?data=my_data";
string responseBodyAsText = null;
var response = new HttpResponseMessage();
var httpClient = new HttpClient();
try
{
response = await httpClient.GetAsync(new Uri(url));
responseBodyAsText = await response.Content.ReadAsStringAsync();
}
catch
{
//...
}
Debug.WriteLine(responseBodyAsText);
httpClient.Dispose();
}
My problem is that responseBodyAsText contains always the same data (given the same uri) and not as I would expect different data according to my external actions (modifying web page or different results with the same uri).
Does HttpClient remembers content during liftime of application? How can I solve this problem?
HttpClient does have caching on by default. You can turn it off by passing it an HttpBaseProtocolFilter:
var filter = new HttpBaseProtocolFilter
{
CacheControl.ReadBehavior = HttpCacheReadBehavior.MostRecent,
CacheControl.WriteBehavior = HttpCacheWriteBehavior.NoCache;
}
Side note: You could also, instead of a Timer, use Task.Delay to achieve the timer behavior (it internally uses one):
private async Task ListenAsync()
{
while (someCondition)
{
string url = "http://www.mywebpage.com?data=my_data";
string responseBodyAsText = null;
var response = new HttpResponseMessage();
using (var httpClient = new HttpClient())
{
try
{
response = await httpClient.GetAsync(new Uri(url));
responseBodyAsText = await response.Content.ReadAsStringAsync();
}
catch
{
//...
}
Debug.WriteLine(responseBodyAsText);
await Task.Delay(2000);
}
}
Before Twitter switched to OAuth2, I was using the following query:
string atomTweetSearchURL = string.Format("http://search.twitter.com/search.atom?q={0}", searchText);
This no longer works, so now I'm trying to switch to OAuth2. I manage to successfully retrieve a token, but once I've got this, I seem to be unable to actually perform the search. Here's the latest incarnation of what I've tried:
var searchUrl = string.Format("https://api.twitter.com/1.1/search/tweets.json?q={0}&access_token={1}&token_type={2}", srchStr, twitAuthResponse.access_token, twitAuthResponse.token_type);
WebRequest srchRequest = WebRequest.Create(searchUrl);
using (var response2 = await srchRequest.GetResponseAsync())
{
Stream stream = response2.GetResponseStream();
using (StreamReader sr = new StreamReader(stream))
{
string jsonResponse = await sr.ReadToEndAsync();
}
}
This gives me a 400 - bad request.
I've also tried building the request like this:
System.Net.Http.HttpClient srchRequest = new System.Net.Http.HttpClient();
string authHdr = string.Format(srchHeaderFormat, twitAuthResponse.token_type, twitAuthResponse.access_token);
srchRequest.DefaultRequestHeaders.Add("Authorization", authHdr);
There's a massive quantity of articles out there detailing how to do this, but none of them seem to work correctly with WinRT. Can anyone point me in the right direction?
EDIT
Here's my code to get the token:
var oAuthConsumerKey = key;
var oAuthConsumerSecret = secret;
var oAuthUri = new Uri("https://api.twitter.com/oauth2/token");
var authHeaderFormat = "Basic {0}";
var authHeader = string.Format(authHeaderFormat,
Convert.ToBase64String(Encoding.UTF8.GetBytes(Uri.EscapeDataString(oAuthConsumerKey)
+ ":" +
Uri.EscapeDataString((oAuthConsumerSecret)))
));
var req = new HttpClient();
req.DefaultRequestHeaders.Add("Authorization", authHeader);
HttpRequestMessage msg = new HttpRequestMessage(new HttpMethod("POST"), oAuthUri);
msg.Content = new HttpStringContent("grant_type=client_credentials");
msg.Content.Headers.ContentType = new Windows.Web.Http.Headers.HttpMediaTypeHeaderValue("application/x-www-form-urlencoded");
HttpResponseMessage response = await req.SendRequestAsync(msg);
TwitAuthenticateResponse twitAuthResponse;
using (response)
{
string objectText = await response.Content.ReadAsStringAsync();
twitAuthResponse = JSonSerialiserHelper.Deserialize<TwitAuthenticateResponse>(objectText);
}
With the 1.1 API you don't pass the access token as part of the url, you need to include it as the Authorization header as "Bearer access_token" so you were almost there!
EDIT
To do this in the Windows.Web.Http namespace the following works:
private static async Task SearchTweets(AuthenticationResponse twitAuthResponse)
{
string srchStr = "tweet";
var client = new HttpClient();
var searchUrl = string.Format("https://api.twitter.com/1.1/search/tweets.json?q={0}", srchStr);
var uri = new Uri(searchUrl);
client.DefaultRequestHeaders.Authorization = new HttpCredentialsHeaderValue("Bearer", twitAuthResponse.AccessToken);
var response2 = await client.GetAsync(uri);
string content = await response2.Content.ReadAsStringAsync();
}
Or with System.Net.Http use the following:
This code will run the search for srchStr using the access token you already acquired as you showed in the first example:
var client = new HttpClient();
var searchUrl = string.Format("https://api.twitter.com/1.1/search/tweets.json?q={0}", srchStr);
var uri = new Uri(searchUrl);
client.DefaultRequestHeaders.Add("Authorization", string.Format("Bearer {0}", twitAuthResponse.access_token));
HttpResponseMessage response = await client.GetAsync(uri);
Task<string> content = response.Content.ReadAsStringAsync();
EDIT
This is a strange one, I tested your code and you're right it does throw an exception when attempting to add the Auth header, however the code I had used for grabbing the Access Token is almost identical but uses the System.Net.Http methods rather than the Windows.Web.Http ones that you use and it works, so I'll provide my code here, maybe this is a bug in the framework, or someone else can provide some more insight! This also uses the JSON.NET library which can be found on NuGet.
private static async Task SearchTweets(AuthenticationResponse twitAuthResponse)
{
string srchStr = "tweet";
var client = new HttpClient();
var searchUrl = string.Format("https://api.twitter.com/1.1/search/tweets.json?q={0}", srchStr);
var uri = new Uri(searchUrl);
client.DefaultRequestHeaders.Add("Authorization", string.Format("Bearer {0}", twitAuthResponse.AccessToken));
HttpResponseMessage response2 = await client.GetAsync(uri);
string content = await response2.Content.ReadAsStringAsync();
}
private async void GetAuthenticationToken()
{
var client = new HttpClient();
var uri = new Uri("https://api.twitter.com/oauth2/token");
var encodedConsumerKey = WebUtility.UrlEncode(TwitterConsumerKey);
var encodedConsumerSecret = WebUtility.UrlEncode(TwitterConsumerSecret);
var combinedKeys = String.Format("{0}:{1}", encodedConsumerKey, encodedConsumerSecret);
var utfBytes = System.Text.Encoding.UTF8.GetBytes(combinedKeys);
var encodedString = Convert.ToBase64String(utfBytes);
client.DefaultRequestHeaders.Add("Authorization", string.Format("Basic {0}", encodedString));
var data = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("grant_type", "client_credentials")
};
var postData = new FormUrlEncodedContent(data);
var response = await client.PostAsync(uri, postData);
AuthenticationResponse authenticationResponse;
using (response)
{
if (response.StatusCode != System.Net.HttpStatusCode.OK)
throw new Exception("Did not work!");
var content = await response.Content.ReadAsStringAsync();
authenticationResponse = JsonConvert.DeserializeObject<AuthenticationResponse>(content);
if (authenticationResponse.TokenType != "bearer")
throw new Exception("wrong result type");
}
await SearchTweets(authenticationResponse);
}
}
class AuthenticationResponse
{
[JsonProperty("token_type")]
public string TokenType { get; set; }
[JsonProperty("access_token")]
public string AccessToken { get; set; }
}