authContext.AcquireTokenSilentAsync throwing error - azure

I have taken reference from this git project. This project has code to connect and get information about User Profile.
While running the project i saw i am not able to get successful result from below code-
result = await authContext.AcquireTokenSilentAsync(graphResourceId, credential,
new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));
Here is full code-
// Get the access token from the cache
string userObjectID =
ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier")
.Value;
string authority = String.Format(CultureInfo.InvariantCulture, aadInstance, tenant);
//AuthenticationContext authContext = new AuthenticationContext(authority,
// new NaiveSessionCache(userObjectID));
ClientCredential credential = new ClientCredential(clientId, appKey);
AuthenticationContext authContext = new AuthenticationContext(authority, true);
// AcquireTokenSilentAsync
result = await authContext.AcquireTokenSilentAsync(graphResourceId, credential,
new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));
// Call the Graph API manually and retrieve the user's profile.
string requestUrl = String.Format(
CultureInfo.InvariantCulture,
graphUserUrl,
HttpUtility.UrlEncode(tenantId));
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUrl);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
HttpResponseMessage response = await client.SendAsync(request);
// Return the user's profile in the view.
if (response.IsSuccessStatusCode)
{
string responseString = await response.Content.ReadAsStringAsync();
profile = JsonConvert.DeserializeObject<UserProfile>(responseString);
}
I tired replacing UserIdentifierType.UniqueId with UserIdentifierType.RequiredDisplayableId but still not able to get successful result.
After trying all day long, i am not able to figure it out the solution of this.
I really appreciate any help.

Related

How to request OAuth2 token to kong plugin

i am following this tutorial https://medium.com/#far3ns/kong-oauth-2-0-plugin-38faf938a468 and when i request the tokens with
Headers: Content-Type:application/json
Host:api.ct.id
Body:
{
“client_id”: “CLIENT_ID_11”,
“client_secret”: “CLIENT_SECRET_11”,
“grant_type”: “password”,
“provision_key”: “kl3bUfe32WBcppmYFr1aZtXxzrBTL18l”,
“authenticated_userid”: “oneone#gmail.com”,
“scope”: “read”
}
it returns
{
"error_description": "Invalid client authentication",
"error": "invalid_client"
}
no matter what i tried i couldn't fix it, any idea how to make it work properly
You need to create kong developer and it will give you client_id and client_secret_Id. Use those values in generating auth token.
Here is the working c# code.
Option 1
public static string GetOAuthToken(string url, string clientId, string clientSecret, string scope = "all", string grantType = "client_credentials")
{
try
{
string token = "";
if (string.IsNullOrWhiteSpace(url)) throw new ArgumentException("message", nameof(url));
if (string.IsNullOrWhiteSpace(clientId)) throw new ArgumentNullException("message", nameof(clientId));
if (string.IsNullOrWhiteSpace(clientSecret)) throw new ArgumentNullException("message", nameof(clientSecret));
var oAuthClient = new RestClient(new Uri(url));
var request = new RestRequest("Authenticate", Method.POST);
request.AddHeader("Content-Type", "application/json");
var credentials = new
{
grant_type = grantType,
scope = scope,
client_id = clientId,
client_secret = clientSecret
};
request.AddJsonBody(credentials);
var response = oAuthClient?.Execute(request);
var content = response?.Content;
if (string.IsNullOrWhiteSpace(content)) throw new ArgumentNullException("message", nameof(clientSecret));
token = content?.Trim('"');
return token;
}
catch (Exception ex)
{
throw new Exception(ex.Message,ex);
}
}
Option 2
var httpClient = new HttpClient()
var creds = $"client_id={client_id}&client_secret{client_secret}&grant_type=client_credentials";
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/x-www-form-urlencoded"));
var content = new StringContent(creds, Encoding.UTF8, "application/x-www-form-urlencoded");
var response = httpClient.PostAsync("https://myorg/oauth/oauth2/cached/token", content).Result;
var OAuthBearerToken = response.Content.ReadAsStringAsync().Result;

How to validate token

I have integrated Azure AD with my application. Now, I want to use the following code to validate token. But, when I validate Microsoft graph token, I get an error : IDX10501: Signature validation failed. Unable to match key.
My code
public JwtSecurityToken Validate(string token)
{
string stsDiscoveryEndpoint = "https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration";
ConfigurationManager<OpenIdConnectConfiguration> configManager = new ConfigurationManager<OpenIdConnectConfiguration>(stsDiscoveryEndpoint, new OpenIdConnectConfigurationRetriever());
OpenIdConnectConfiguration config = configManager.GetConfigurationAsync().Result;
TokenValidationParameters validationParameters = new TokenValidationParameters
{
ValidateAudience = false,
ValidateIssuer = false,
IssuerSigningKeys = config.SigningKeys,
ValidateLifetime = false
};
JwtSecurityTokenHandler tokendHandler = new JwtSecurityTokenHandler();
SecurityToken jwt;
var result = tokendHandler.ValidateToken(token, validationParameters, out jwt);
return jwt as JwtSecurityToken;
}
To resolve this issue, make sure that the algorithm for the JWT matches with the configuration of your middleware.
Refer to this article and change configurationManager as below:
var issuer = "https://https://login.microsoftonline.com/common/v2.0";
var configurationManager = new ConfigurationManager<OpenIdConnectConfiguration>(
issuer + "/.well-known/oauth-authorization-server",
new OpenIdConnectConfigurationRetriever());

Update Azure AD Application keys or secrets using Microsoft Graph API - BadRequest Error

I am trying to patch password credentials for an application using Microsoft Graph API beta endpoint for application resource type.
https://graph.microsoft.com/beta/applications/{applicationId}
The content variable is a JSON-serialized representation of something like this:
[{
"customKeyIdentifier":null,
"endDateTime":"2019-11-19T23:16:24.2602448Z",
"keyId":"47fde652-8b60-4384-b630-8e5f8f6e24b1",
"startDateTime":"2018-11-19T23:16:24.2602448Z",
"secretText":"SomeGeneratedPassword",
"hint":null
}]
Calling code is this:
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri("https://graph.microsoft.com");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authHeaderValue.Result.AccessToken);
client.DefaultRequestHeaders
.Accept
.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var method = new HttpMethod("PATCH");
var requestUri = $"https://graph.microsoft.com/beta/applications/{applicationId}";
var content = GeneratePasswordCredentials(passwordHint);
var request = new HttpRequestMessage(method, requestUri)
{
Content = new StringContent(
content,
System.Text.Encoding.UTF8,
"application/json")
};
request.Headers
.Accept
.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var resultApi = await client.SendAsync(request);
response = await resultApi.Content.ReadAsStringAsync();
}
Auth appears to be working fine, but the response is this (inner error removed for brevity):
{
"error": {
"code": "BadRequest",
"message": "Empty Payload. JSON content expected.",
}
}
What is wrong with the above code?
The body content format should be
{
"passwordCredentials":
[
{"customKeyIdentifier":"YWJjZA==",
"startDateTime":"2018-11-20T02:37:07.3963006Z",
"endDateTime":"2019-11-20T02:37:07.3963006Z",
"secretText":"The passwords must be 16-64 characters in length",
"keyId":"aeda515d-dc58-4ce6-a452-3bc3d84f58a3",
"hint":"xxx"}
]
}
The following the demo code to Generate PasswordCredentials body content
public static string GeneratePasswordCredentials(string passwordHint)
{
var passwordCredential = new JObject
{
new JProperty("customKeyIdentifier",Encoding.UTF8.GetBytes(passwordHint)),
new JProperty("startDateTime",DateTime.UtcNow),
new JProperty("endDateTime", DateTime.UtcNow.AddYears(1)),
new JProperty("secretText", "The passwords must be 16-64 characters in length"),
new JProperty("keyId", Guid.NewGuid().ToString()),
new JProperty("hint", passwordHint)
};
JArray jArray = new JArray
{
passwordCredential
};
var jsonObject = new JObject
{
new JProperty("passwordCredentials",jArray)
};
var json = JsonConvert.SerializeObject(jsonObject);
return json;
}
Note: The request url should be $"https://graph.microsoft.com/beta/applications/{ApplicationObjectId}"
The issue is with JSON string specified for Update Application Microsoft Graph API. It's missing which property you're trying to update for the application. I've added "passwordCredentials" property and given it the JSON as a collection. See jsonContent variable at the very beginning of my code.
/*Only change here from original JSON is to add the passwordCredentials node*/
{
"passwordCredentials":[
{
"customKeyIdentifier": null,
"endDateTime": "2019-11-19T23:16:24.2602448Z",
"keyId": "47fde652-8b60-4384-b630-8e5f8f6e24b1",
"startDateTime": "2018-11-19T23:16:24.2602448Z",
"secretText": "SomeGeneratedPassword",
"hint": null
}
]
}
I started with your code and the 400 bad response error reproduced for me as well.
Below is the final working code and now I get back a 204 response status. I can also see the new key added to Application keys collection from Azure Portal > App Registrations > My app > Settings > Keys
string jsonContent = "{\"passwordCredentials\":[{\"customKeyIdentifier\":null,\"endDateTime\":\"2019-11-19T23:16:24.2602448Z\",\"keyId\":\"47fde652-8b60-4384-b630-8e5f8f6e24b1\",\"startDateTime\":\"2018-11-19T23:16:24.2602448Z\",\"secretText\":\"somegeneratedpassword\",\"hint\":null}]}";
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri("https://graph.microsoft.com");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authHeaderValue.Result.AccessToken);
client.DefaultRequestHeaders
.Accept
.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var method = new HttpMethod("PATCH");
var requestUri = $"https://graph.microsoft.com/beta/applications/{applicationId}";
// I have commented out this method and passed in my JSON instead.
//var content = GeneratePasswordCredentials(passwordHint);
var content = jsonContent;
var request = new HttpRequestMessage(method, requestUri)
{
Content = new StringContent(
content,
System.Text.Encoding.UTF8,
"application/json")
};
request.Headers
.Accept
.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var resultApi = client.SendAsync(request).GetAwaiter().GetResult();
//response = await resultApi.Content.ReadAsStringAsync();
var response = resultApi.Content.ReadAsStringAsync().GetAwaiter().GetResult();
}

How to use cookies with Azure AD on behalf of flow to get an access token to another resource

I have two applications using the same azure active directory. App A and App B.
App A uses
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
ClientId = Configuration["Authentication:AzureAd:ClientId"],
Authority = Configuration["Authentication:AzureAd:AADInstance"] + Configuration["Authentication:AzureAd:TenantId"],
ClientSecret = Configuration["Authentication:AzureAd:ClientSecret"],
CallbackPath = Configuration["Authentication:AzureAd:CallbackPath"],
ResponseType = OpenIdConnectResponseType.CodeIdToken,
GetClaimsFromUserInfoEndpoint = true,
SignInScheme = "Cookies",
SaveTokens = true,
Events = new OpenIdConnectEvents
{
OnAuthorizationCodeReceived = OnAuthorizationCodeReceived,
}
});
And i acquire an access to application B api service resource by acquiring the token with:
private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedContext context)
{
string userObjectId = (context.Ticket.Principal.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier"))?.Value;
ClientCredential clientCred = new ClientCredential(Configuration["Authentication:AzureAd:ClientId"], Configuration["Authentication:AzureAd:ClientSecret"]);
AuthenticationContext authContext = new AuthenticationContext(Configuration["Authentication:AzureAd:AADInstance"] + Configuration["Authentication:AzureAd:TenantId"]);
AuthenticationResult authResult = await authContext.AcquireTokenByAuthorizationCodeAsync(
context.ProtocolMessage.Code, new Uri(context.Properties.Items[OpenIdConnectDefaults.RedirectUriForCodePropertiesKey]), clientCred, Configuration["Authentication:AzureAd:GraphResourceId"]);
I'm also using cookies to sign in into app A with:
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AuthenticationScheme = "Cookies",
AutomaticAuthenticate = true,
AutomaticChallenge = true,
SlidingExpiration = true,
ExpireTimeSpan = TimeSpan.FromHours(1),
Events = new CookieAuthenticationEvents()
{
OnSignedIn = OnSignedIn,
OnSigningIn = OnSigningIn,
OnValidatePrincipal = OnValidatePrincipal
}
});
/* Account Controller SignIn() */
return Challenge(
new AuthenticationProperties {
AllowRefresh = true,
IsPersistent = true,
RedirectUri = "/" }, OpenIdConnectDefaults.AuthenticationScheme);
Now my problem is similar to others where my access token is expiring, but my signin cookie to app a is still valid so the user appears to be logged in fine, although they have no token in the cache.
I've followed suit of other questions and looked to my Cookie event of
Task OnValidatePrincipal(CookieValidatePrincipalContext arg) {
var http = new HttpClient();
var uri = "https://login.microsoftonline.com/<tenant>/oauth2/token";
var client_id = "<my_client_id>";
var scope = "https://graph.microsoft.com/mail.read";
var refresh_token = "<saved_refresh_token_in_cookie_if_SaveTokens = true>";
var redirect_uri = "https://localhost:20352/";
var grant_type = "refresh_token";
var client_secret = "<client_secret_from_azure>";
var body = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("client_id", client_id),
new KeyValuePair<string, string>("scope", scope),
new KeyValuePair<string, string>("refresh_token", refresh_token),
new KeyValuePair<string, string>("redirect_uri", redirect_uri),
new KeyValuePair<string, string>("grant_type", grant_type),
new KeyValuePair<string, string>("client_secret", client_secret)
};
var content = new FormUrlEncodedContent(body);
var result = http.PostAsync(uri, content).Result;
var stringContent = result.Content.ReadAsStringAsync().Result;
JObject jobject = JObject.Parse(stringContent);
var token = jobject["access_token"].Value<string>();
Problem here is I don't know how to get this token back into the default TokenStore that the adal AuthenticationContext uses. We have code deeper that needs to pull from:
_authenticationResult = await authContext.AcquireTokenSilentAsync(_authConfigOptions.AzureAd.WebserviceAppIdUri.ToString(), credential, new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));
Is there a way I can get a new resource access token back into the tokenstore for users App B api calls, without a valid token / refresh token 'On Behalf of User' flow?
If you lose the access token and refresh token, you must redirect the user to Azure AD to authenticate again. They may be still authenticated there so they just get redirected back to your app along with the authorization code.
In one of my projects I made an exception filter that does this:
public void OnException(ExceptionContext filterContext)
{
//If the error is a silent token acquisition exception from ADAL..
if(filterContext.Exception is AdalSilentTokenAcquisitionException)
{
//Instead of the usual procedure, return a 401 which triggers the OpenIdConnect middleware redirection
filterContext.Result = new HttpUnauthorizedResult();
filterContext.ExceptionHandled = true;
}
}
So if the exception is thrown where silent token acquisition fails, just swallow the error and change the result into a 401, which triggers the OpenIdConnect middleware to send the user to Azure AD.
Since you have AutomaticAuthenticate=true, it should do this.

Azure forbidden exception trying to connect programmatically

I have the following code:
var authority = "https://login.microsoftonline.com/XXX-XXXX-XXXXX-XXXXXX/oauth2/authorize";
var clientId = "YYY-YYYY-YYYYY-YYYYYY";
var appKey = "xxxxxxxxxxxxx";
var resource = "https://management.core.windows.net/";
var authContext = new AuthenticationContext(authority, true);
var clientCredential = new ClientCredential(clientId, appKey);
var result = authContext.AcquireToken(resource, clientCredential);
var token = result.AccessToken;
var creds = new TokenCloudCredentials("ZZZ-ZZZZ-ZZZZZ-ZZZZZZ", token);
var client = new WebSiteManagementClient(creds);
var data = client.WebSites.Get("eastuswebspace", "some-site", new WebSiteGetParameters());
I am getting this error on the client.WebSites.Get() call:
The server failed to authenticate the request. Verify that the
certificate is valid and is associated with this subscription
But I am not using a certificate, I am using AuthenticationContext.
What am I doing wrong?

Resources