ThinkTecture Identity Model Get User based on Token - c#

I am using the sweet Indentity Model library from Thinktecture. I looked high and low and am probably about to ask a dumb question. Basically, we have a Web Forms application which I am adding ASP.net Web api capabilities to it. I installed the Identity code samples and gleened what I think I needed to get it working for session token I am able to get a token and send back on future calls but in my API controller the user object does not seem to be set. I had assumed the framework would interrogate the token if provided and automatically set the Principal based on the token data and mark principal as authenticated? Is this an invalid assumption and is this something that I must set myself?
Basically I am using the token for Authorization and Authentication but need to pull off UserID from the token/session for additional business rules.
Again if dumb question feel free to haze me. I am testing using Unit Test if that has any impact on any recommendations.
Code from Unit Test:
1) Get Token, successfully validates user using our business logic and returns token successfully:
private string GetToken(string username, string password)
{
Uri _baseAddress = new Uri(Thinktecture.Samples.Constants.WebHostBaseAddress);
var client = new HttpClient { BaseAddress = _baseAddress };
client.SetBasicAuthentication(username, password);
var response = client.GetAsync("token").Result;
response.EnsureSuccessStatusCode();
var tokenResponse = response.Content.ReadAsStringAsync().Result;
var json = JObject.Parse(tokenResponse);
var token = json["access_token"].ToString();
var expiresIn = int.Parse(json["expires_in"].ToString());
var expiration = DateTime.UtcNow.AddSeconds(expiresIn);
return token;
}
2) HTTP HELPER METHOD:
private async Task<HttpResponseMessage> Post<T>(string path, T data, string Token)
{
//var handler = new HttpClientHandler {};
//using (var client = new HttpClient(handler))
using (var client = new HttpClient())
{
if (!String.IsNullOrEmpty(Token))
{
client.SetToken("Session", Token);
//client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Session", Token);
}
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.BaseAddress = new Uri("http://localhost/");
var response = await client.PostAsJsonAsync(path, data);
response.EnsureSuccessStatusCode(); // Throw on error code.
return response;
}
}
3) Calling Code:
string Token = GetToken("UserName", "Password");
removed proprietry code here....
Task<HttpResponseMessage> result = Post<GetCustomerDetailsRequest>("api/Account/GetCustomerProfile", GetCustomerDetailsRequest, Token);

Related

How to call a web api that has Oauth 2.0

Hi so we have an external web api we want to call to get data out. It is using oauth 2.0. Can somebody please explain how we would go about doing this in .NET either vb.net or c#. I have in the past created api, however this one seems very complicated. Firstly you have to be signed into their oauth web page they have which generates some cookies, using these cookies by syncing them up in postman we can see the data, however we need this to be within our .net app. Can somebody please help how we go about this. Some code would be useful.
Thanks
This is how usually OAuth 2 authentication works.
You basically log in with username and password (optional second factor) and then you receive a token, the so called Json Web Token or JWT (it holds encrypted information about your user, your access roles or groups you are member of as well as some timestamp which is the expiration time of the token).
In every subsequent request you make to the server, you pass this token in the request header (or in your case as cookie).
Example code:
Login request:
HttpRequestMessage httpRequest = new HttpRequestMessage(HttpMethod.Post, new Uri(_baseUrl, "token"));
string body = JsonConvert.SerializeObject(new
{
Username = _userName,
Password = _password,
secondFactor = secondFactor
});
httpRequest.Content = new StringContent(body, Encoding.UTF8, "application/json");
var response = await client.SendAsync(httpRequest);
var responseContent = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
{
TokenResult r = JsonConvert.DeserializeObject<TokenResult>(responseContent);
if (!string.IsNullOrWhiteSpace(r.token))
{
_token = r.token;
_tokenValidity = r.expirationDate;
_refreshToken = r.refreshToken;
_refreshTokenValidity = r.refreshTokenExpirationDate;
return _token;
}
else
{
throw new Exception($"Failed to get token from server.\r\n{responseContent}");
}
}
Now you use the _token in subsequent requests in the request header:
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _token);
using HttpResponseMessage response = await client.GetAsync(new Uri(_baseUrl, relativePath));
if (response.IsSuccessStatusCode)
{
using var stream = await response.Content.ReadAsStreamAsync();
stream.Position = 0;
using var reader = new StreamReader(stream);
reader.ReadToEnd();
}
Please note, that usually the token has a certain lifetime after which it is basically useless. Some APIs offer a refresh token with which a new token can be requested without the user having to log in again with username and password, but that's beyond the scope of this question.
You said you have to use the token as cookie? Well there are APIs which work like this but personally I've never seen one like this, which is why I can't you help very much, but it shouldn't be much more than putting the token you got into a cookie with a certain name.
Hope this helps.
Not sure what you are asking. I have a controller code where I use web api call to authenticate user. You can use your own model to pass the data. If your web api expects token for request, then you might have to get the token first to give a call to any method. Hope this helps.
OktaUserDetailsModel Model = new OktaUserDetailsModel();
Model.username = model.UserName;
Model.password = model.Password;
using (var httpClient = new HttpClient())
{
HttpContent inputContent = new StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(Model), System.Text.Encoding.UTF8, "application/json");
HttpResponseMessage response = httpClient.PostAsync(ConfigurationManager.AppSettings["OktaAPIuri"], inputContent).Result;
if (response.IsSuccessStatusCode)
{
string strResponse = (new JavaScriptSerializer()).Deserialize<string>(response.Content.ReadAsStringAsync().Result);
if (strResponse.ToUpper() == "TRUE")
return OktaSingleSignOnLogin(astrReturnUrl, model.UserName);
else
return ErrorPage();
}
else
{
return ErrorPage();
}
}

How to use access tokens to get authorization to access REST APIs

I have an HttpClient that I am using for a REST API. I get the access token from the server but I do not have permission to use the REST APIs. The response is Error: Unauthorized
First, Using getAccessToken() method, I get the access token.
public static async Task<string> getAccessToken()
{
var client = new HttpClient();
client.DefaultRequestHeaders.Add("Referer", "http://admin.altrabo.com/");
var tokenClient = new TokenClient()
{
client_Id= -1,
username= "admin",
password= "Main#dm!n",
grant_Type= "Main#dm!n",
externalProvider= 1,
};
HttpResponseMessage response = await client.PostAsJsonAsync<TokenClient>("https://api.altrabo.com/api/v1/token", tokenClient);
var json = JsonSerializer.Deserialize<AccessToken>(response.Content.ReadAsStringAsync().Result);
return json.access_token;
}
which returns the access token.
Then, using the verifyToken() method, I verify the access token.
public static async Task<string> verifyToken(string access_token)
{
var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + access_token);
HttpResponseMessage response = await client.GetAsync("https://api.altrabo.com/api/v1/verifyToken");
return response.ReasonPhrase;
}
returns OK
But when I want to get access to API, I encounter Error: Unauthorized.
For example when I want to get the list of airports:
public static async Task<string> ListAirports()
{
var client = new HttpClient();
return await client.GetStringAsync("https://api.altrabo.com/api/v1/BaseData/GetAirports?pageSize=1000&pageNumber=1");
}
The API documentation is available at
https://www.getpostman.com/collections/51cd9e7f5f6ebafa8c48‎
Add the token to the request to get the airports, in the same manner you do, as when you verify the token.
public static async Task<string> ListAirports(string access_token)
{
var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + access_token);
return await client.GetStringAsync("https://api.altrabo.com/api/v1/BaseData/GetAirports?pageSize=1000&pageNumber=1");
}
you have to check the documentation of the rest-api for what authorization-method the api requires. Maybe its username/password or something.
https://username:token#your-api.com

OpenID Connect Authentication Successful. Now what?

I'm writing a windows service in C# that needs to authenticate with an API and make some calls. I'm able to authenticate successfully with this API I'm talking to, but I can't seem to figure out how to use the response. The response looks like this:
{"access_token":"Es-Zjs_LI0tcXyLe3aEfgKPNLHN7CwyUhTss-cTld1A","expires_in":1800,"token_type":"Bearer","scope":"example","auth_state":1,"company":"examplecompany"}
I can get the access token out of that string if I want, but no matter how I pass it to a request, I get a 401 error. This is what my current iteration looks like:
string results = "";
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer",token);
var request = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = new Uri("https://example.ca/endpoint"),
//Headers =
//{
// { "authorization", "Bearer"},
//},
};
try
{
using (var response = await client.SendAsync(request))
{
response.EnsureSuccessStatusCode();
var body = await response.Content.ReadAsStringAsync();
results = body;
}
}
catch (Exception ex)
{
results = "ERROR: " + ex.Message;
}
return results;
Where "token" is the string "Es-Zjs_LI0tcXyLe3aEfgKPNLHN7CwyUhTss-cTld1A" in this example. I had previously tried stitching the access_token value as a string to the "Bearer" string in the commented out section in the middle there. What am I doing wrong? Do I need to make a JwtSecurityToken out of the response?
AuthenticationResult authResult = await daemonClient.AcquireTokenForClient(new[] { MSGraphScope })
.ExecuteAsync();
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authResult.AccessToken);
I've used the authResult.AccessToken. Not sure if it works in your scenario. The return type in my case was Microsoft.Identity.Client.AuthenticationResult type when I retrieved the token for a Graph API that I was using.
Be aware that the token you have received ("Es-Zjs_LI0tcXyLe3aEfgKPNLHN7CwyUhTss-cTld1A") is a reference token and not a JWT-token. Make sure your API accepts that type of token.
To use the token effectively in production then I would consider using the various helper methods found in the IdentityModel library and especially the Worker application helpers.
While I understand it's largely situational depending on what API you're trying to connect to, for me the solution was to use this method to pass in the authentication token:
request.Headers.TryAddWithoutValidation("Authorization", "Bearer " + token);

Resetting a user's password using Microsoft Graph

I'm trying to write a web portal that users can use to reset their own Azure AD password. Because of the requirements of my client, the Azure AD SSPR is not an option.
To achieve this I'm using Microsoft Graph. According to the documentation, it is possible to reset a users password using Microsoft Graph if you have User.ReadWrite.All or Directory.AccessAsUser.All permissions.
Then the permissions documentation, the remarks it states that even if you have the Directory.ReadWrite.All permissions you won't be able to reset a users password.
I've done a test to see if this will work but I get an HTTP 403 Forbidden response.
The code I'm using is:
string ResourceUrl = "https://graph.windows.net/";
string AuthorityUrl = "https://login.microsoftonline.com/companyxxx.onmicrosoft.com/oauth2/authorize/";
//Create a user password cradentials.
var credential = new Microsoft.IdentityModel
.Clients
.ActiveDirectory
.UserPasswordCredential("username#xxxx.com", "passwordxxx");
// Authenticate using created credentials
var authenticationContext = new AuthenticationContext(AuthorityUrl);
var authenticationResult = authenticationContext
.AcquireTokenAsync(ResourceUrl, "xxxxxxxx-3017-4833-9923-30d05726b32f", credential)
.Result;
string jwtToken = authenticationResult.AccessToken;
var cred = new Microsoft.Rest
.TokenCredentials(authenticationResult.AccessToken, "Bearer");
HttpClient client = new HttpClient();
var queryString = HttpUtility.ParseQueryString(string.Empty);
queryString["api-version"] = "1.6";
client.DefaultRequestHeaders
.Accept
.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", jwtToken);
var uri = "https://graph.windows.net/xxxxxxxx-18fe-xxxx-bb90-d62195600495/users/xxxxxxxx-aa58-4329-xxxx-b39af07325ee?" + queryString;
//var content = new StringContent("{\"passwordProfile\": {\"password\": \"Test123456\", \"forceChangePasswordNextLogin\": true }}");
var response = client.PatchAsync(new Uri(uri), content, jwtToken);
The PatchAsync method is an extension method as below:
public static class HttpClientExtensions
{
public static async Task<HttpResponseMessage> PatchAsync(this HttpClient client,
Uri requestUri, HttpContent iContent, string jwtToken)
{
var method = new HttpMethod("PATCH");
var request = new HttpRequestMessage(method, requestUri)
{
Content = iContent,
};
request.Content.Headers.ContentType =
new MediaTypeHeaderValue("application/json");
request.Headers.Authorization =
new AuthenticationHeaderValue("Bearer", jwtToken);
HttpResponseMessage response = new HttpResponseMessage();
try
{
response = await client.SendAsync(request);
}
catch (TaskCanceledException e)
{
Console.WriteLine("ERROR: " + e.ToString());
}
return response;
}
}
Could someone please clarify if this is possible using the credentials grant flow with a username and password for authentication. If so how do I achieve this?
You're mixing up Microsoft Graph and Azure AD Graph API. These are two different APIs and calls to one are not interchangeable with the other.
You are correct in that you need to use the Directory.AccessAsUser.All scope for this activity. This scope allows the API to do anything to the AAD that the signed in user would be able to do themselves (i.e. change their own password).
Once you have a valid access_token for the user with Directory.AccessAsUser.All permission, you can update the user's passwordProfile:
PATCH https://graph.microsoft.com/v1.0/me
Content-type: application/json
{
"passwordProfile" : {
"forceChangePasswordNextSignIn": true,
"password": "password-value"
}
}

Handling JWT expiration in .NET MVC-application

I have an ASP.NET MVC-application which is storing a JWT-token and a refresh token from my Web API in Session. My question is what to do when the JWT-token expires and it is time to refresh it. As I see it my two options are:
Try to make a request to the Web API using the JWT-token and if it returns 401 Unauthorized, try refreshing the JWT-token.
Using a timer to automatically refresh the JWT-token before it expires.
What are advantages of using either of these two methods, and how can I programatically implement them in an easy way? For example, do I have to use a try and catch for every call to the API if i use option 1?
I decided to go with option 2 in order to minimize the number of calls to the API. I then created a base controller class with a HttpClient factory method, which also checks if the JWT is about to expire:
public HttpClient GetHttpClient(string baseAdress)
{
var client = new HttpClient();
client.BaseAddress = new Uri(baseAdress);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
string token;
if (Session["access_token"] != null)
{
var jwthandler = new JwtSecurityTokenHandler();
var jwttoken = jwthandler.ReadToken(Session["access_token"] as string);
var expDate = jwttoken.ValidTo;
if (expDate < DateTime.UtcNow.AddMinutes(1))
token = GetAccessToken().Result;
else
token = Session["access_token"] as string;
}
else
{
token = GetAccessToken().Result;
}
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
Session["access_token"] = token;
return client;
}

Categories

Resources