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?
Related
string userAssignedClientId = "abcderfgh-1234-1234-b17b-12345678900";
var credential = new DefaultAzureCredential(new DefaultAzureCredentialOptions { ManagedIdentityClientId = userAssignedClientId });
var blobClient = new CloudTableClient(new Uri("https://myaccount.blob.core.windows.net/mycontainer/myblob"), Microsoft.Azure.Cosmos.Table.StorageCredentials(credential));
Error: Argument 2: cannot convert from 'Azure.Identity.ManagedIdentityCredential' to 'Microsoft.Azure.Cosmos.Table.StorageCredentials'
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;
The following code, when pointed to a real Azure storage account will successfully return the blob content:
var path = $"{container}/{blob}";
var rfcDate = DateTime.UtcNow.ToString("R");
var headers = "GET\n\n\n\n\n\n\n\n\n\n\n\n" +
"x-ms-blob-type:Block\n" +
$"x-ms-date:{rfcDate}\n" +
$"x-ms-version:{ServiceVersion}\n" +
$"/{AccountName}/{path}";
var uri = new Uri(BlobEndpoint + path);
var request = new HttpRequestMessage(HttpMethod.Get, uri);
request.Headers.Add("x-ms-blob-type", "Block");
request.Headers.Add("x-ms-date", rfcDate);
request.Headers.Add("x-ms-version", ServiceVersion);
string signature = "";
using (var sha = new HMACSHA256(System.Convert.FromBase64String(AccountKey)))
{
var data = Encoding.UTF8.GetBytes(headers);
signature = System.Convert.ToBase64String(sha.ComputeHash(data));
}
var authHeader = $"SharedKey {AccountName}:{signature}";
request.Headers.Add("Authorization", authHeader);
using (var client = new HttpClient())
{
var response = await client.SendAsync(request);
return await response.Content.ReadAsStringAsync();
}
However, if I configure it to use the Azure emulator where:
AccountName = devstoreaccount1
AccountKey = Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==
BlobEndpoint = http://127.0.0.1:10000/
ServiceVersion = 2009-09-19
I always get 404. I'm using Azure Storage Emulator v4.6. Is the code or config incorrect or is this not supported with the emulator?
There are two issues with your code:
Blob Service in Storage Emulator listens at http://127.0.0.1:1000 however the base URI is http://127.0.0.1:1000/devstoreaccount1.
In computation of Signature String (header variable in your code), account name must appear twice. This is because the account name is part of your resource's URI path (URL for the blob would be http://127.0.0.1:1000/devstoreaccount1/container-name/blob-name).
Based on these, please try the following code:
static async Task<string> ReadBlobFromDevStorage()
{
var container = "temp";
var blob = "test.txt";
var ServiceVersion = "2009-09-19";
var AccountName = "devstoreaccount1";
var BlobEndpoint = "http://127.0.0.1:10000/devstoreaccount1";
var path = $"{container}/{blob}";
var AccountKey = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==";
var rfcDate = DateTime.UtcNow.ToString("R");
var headers = "GET\n\n\n\n\n\n\n\n\n\n\n\n" +
"x-ms-blob-type:Block\n" +
$"x-ms-date:{rfcDate}\n" +
$"x-ms-version:{ServiceVersion}\n" +
$"/{AccountName}/{AccountName}/{path}";
var uri = new Uri(BlobEndpoint + "/" + path);
var request = new HttpRequestMessage(HttpMethod.Get, uri);
request.Headers.Add("x-ms-blob-type", "Block");
request.Headers.Add("x-ms-date", rfcDate);
request.Headers.Add("x-ms-version", ServiceVersion);
string signature = "";
using (var sha = new HMACSHA256(System.Convert.FromBase64String(AccountKey)))
{
var data = Encoding.UTF8.GetBytes(headers);
signature = System.Convert.ToBase64String(sha.ComputeHash(data));
}
var authHeader = $"SharedKey {AccountName}:{signature}";
request.Headers.Add("Authorization", authHeader);
using (var client = new HttpClient())
{
var response = await client.SendAsync(request);
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine(content);
return content;
}
}
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.
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.