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"
}
}
Related
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();
}
}
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);
I'm trying to use Azure App Insights API using https://dev.applicationinsights.io/
Got the metrics data using this "https://api.applicationinsights.io/v1/apps/889f31e2-8281-4d8b-bb80-53a9f83d4a09/metrics/requests/duration?interval=PT1H", Passed the api key through headers.
But not able to get the Query:
Tried this:
https://api.applicationinsights.io/v1/apps/889f31e2-8281-4d8b-bb80-53a9f83d4a09/Query/traces | where operation_Id contains 62c5a318-c2cf-434e-a102-0e6efc0a84e7?interval=P7D
Getting Status Code as NotFound.
Any suggestions, Am I missing something?
According to my research, if you want to do query with Azure Application insight rest api, the URL should be like as below
https://api.applicationinsights.io/v1/apps/{appId}/query?query={1}×pan={2}
For example
var client = new HttpClient();
var appId = "";
var query = "traces | where operation_Id contains '33f491236bb412419002b006e1c3058b'";
var timespan = "P7D";
var apiKey=""
string url = string.Format("https://api.applicationinsights.io/v1/apps/{0}/query?query={1}×pan={2}",
appId,
query,
timespan);
var request = new HttpRequestMessage();
request.Method = HttpMethod.Get;
request.Headers.Add("x-api-key", apiKey);
request.RequestUri = new Uri(url);
using (var response = await client.SendAsync(request)) {
var str = await response.Content.ReadAsStringAsync();
Console.WriteLine(JsonConvert.DeserializeObject<JObject>(str));
Console.Read();
}
For more details, please refer to the document. Besides, you also can try the API vai the explorer.
Update
If you want to Azure AD auth to access Azure application insights API, please refer to the following steps
Register Azure AD application in your tenant
Configure API permissions
Create a client secret for the application
Configure assign contributor to the AD application in your Application Insights resource
call the api
// install sdl MSAL.NET to get access token
var client = new HttpClient();
var appId = "";
var query = "traces | where operation_Id contains '33f491236bb412419002b006e1c3058b'";
var timespan = "P7D";
var apiKey=""
string url = string.Format("https://api.applicationinsights.io/v1/apps/{0}/query?query={1}×pan={2}",
appId,
query,
timespan);
string ClientId = "<your ad application clientID>";
string ClientSecret = "<your ad application ClientSecret>";
string tenant = "";
var app = ConfidentialClientApplicationBuilder.Create(ClientId)
.WithClientSecret(ClientSecret)
.WithAuthority(new Uri($"https://login.microsoftonline.com/{tenant}"))
.Build();
string[] scopes = new string[] { "https://api5.applicationinsights.io/.default" };
var result =await app.AcquireTokenForClient(scopes).ExecuteAsync();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
var request = new HttpRequestMessage();
request.Method = HttpMethod.Get;
request.RequestUri = new Uri(url);
using (var response = await client.SendAsync(request)) {
var str = await response.Content.ReadAsStringAsync();
Console.WriteLine(JsonConvert.DeserializeObject<JObject>(str));
Console.Read();
}
For more details, please refer to the document
I want to skip AWS Cognito's Hosted UI for login/authorization. However, when I try to consume the login end point to fetch authorization-code, I'm slapped with MethodNotAllowed response. As per AWS documentation, the login end-point accepts "Get" requests only. Based on my research on this topic, I figured it was possible to use "Post" method with login credentials for the login end point (Thanks to AWS documentation).
Can someone help please me figure out the issue?
AWS Pool Settings:
AWS Pool Settings
C# Code:
I'm using RestSharp as the HTTP client.
private static void CognitoOAuthSignIn(string username, string password)
{
var CLIENT_ID = "<client_id>";
var RESPONSE_TYPE = "code";
var REDIRECT_URI = "https://www.google.com";
var SCOPE = "openid";
var AUTH_DOMAIN = "https://<domain_name>.auth.us-east-1.amazoncognito.com";
var USERNAME = username;
var PASSWORD = password;
RestClient client = null;
// 1. Get XSRF Code
var csrfRequestUrl = $"{AUTH_DOMAIN}/oauth2/authorize?response_type={RESPONSE_TYPE}&client_id={CLIENT_ID}&redirect_uri={REDIRECT_URI}&scope={SCOPE}";
var csrfRequest = new RestRequest(Method.GET);
client = new RestClient(csrfRequestUrl);
client.CookieContainer = new CookieContainer();
IRestResponse csrfResp = client.Execute(csrfRequest);
var cookie = client.CookieContainer.GetCookieHeader(new Uri(AUTH_DOMAIN));
var code = cookie.Split(';')[0].Substring(11);
// 2. Make login request
var loginRequestUrl = $"{AUTH_DOMAIN}/login?client_id={CLIENT_ID}&response_type={RESPONSE_TYPE}&scope={SCOPE}&redirect_uri={REDIRECT_URI}";
client = new RestClient(loginRequestUrl);
client.DefaultParameters[0].Value = "*/*"; // Setting "Accept" header
client.AddDefaultHeader("Content-Type", "application/x-www-form-urlencoded");
client.AddDefaultHeader("Accept-Encoding", "gzip,deflate");
client.AddDefaultHeader("Accept-Language", "en-US");
client.AddDefaultHeader("Cache-Control", "no-cache");
client.AddDefaultHeader("Cookie", $"csrf-state=; csrf-state-legacy=; XSRF-TOKEN={code}");
var authCodeRequest = new RestRequest(Method.POST);
authCodeRequest.AddParameter("_csrf", code, ParameterType.GetOrPost);
authCodeRequest.AddParameter("username", USERNAME, ParameterType.GetOrPost);
authCodeRequest.AddParameter("password", PASSWORD, ParameterType.GetOrPost);
authCodeRequest.RequestFormat = DataFormat.None;
IRestResponse authCodeResp = client.Execute(authCodeRequest);
Console.WriteLine(authCodeResp.StatusCode); //returns MethodNotAllowed
}
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);