I have Identity Server 4 hosted in my service. It exposes various endpoints, and I would like to access token endpoint from same service.
It does not expose any interface, so I just could inject it as a dependency, hence I have to make an http request.
Now I set urls of HttpClient like this:
var req = _contextAcessor.HttpContext.Request;
var baseAddr = $"{req.Scheme}://{req.Host}";
client.BaseAddress = new Uri(baseAddr);
var addr = _urlHelperProvider.GetUrlHelper().Content("~/connect/token");
I don't like it because here localhost is not even used. But service may be bound to different port on localhost, and I have no idea how to get the list of urls passed to UseUrls method.
Ideally, I'd like to have instance of the client bound to my service similar how it works with WebApplicationFactory.CreateClient method.
Was hoping to find an answer here but got it working eventually
So hopefully this might help someone
when you create your client, it might is often helpful to set the host with https
It should be equal to whatever host you have by default when running your application locally
_factory = new WebApplicationFactory<Startup>().WithWebHostBuilder(BuildHost);
Client = _factory.CreateClient(new WebApplicationFactoryClientOptions()
{
BaseAddress = new System.Uri("https://localhost:5031")
});
Then your request is simply:
var parameters = new Dictionary<string, string> { { "grant_type", "client_credentials" },
{ "scope", "SomeScope" },
{ "client_id", "YourClientId" }, { "client_secret", "yoursecret" } };
var encodedContent = new FormUrlEncodedContent(parameters);
var response = await Client.PostAsync("/connect/token", encodedContent);
Have tested it locally and it works
Please let me know if you still have any issues with your setup,
should be able to help
Related
I am trying to push a commit I made on my local repository to a remote counterpart, hosted on a private Azure DevOps server, using LibGit2Sharp programmatically.
As per the Azure documentation, the HTTPS OAuth enabled Personal Access Token needs to sent with the request in a custom Authentication header as 'Basic' with the Base64 encoded token:
var personalaccesstoken = "PATFROMWEB";
using (HttpClient client = new HttpClient()) {
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",
Convert.ToBase64String(Encoding.ASCII.GetBytes($":{personalaccesstoken}")));
using (HttpResponseMessage response = client.GetAsync(
"https://dev.azure.com/{organization}/{project}/_apis/build/builds?api-version=5.0").Result) {
response.EnsureSuccessStatusCode();
}
}
The LibGit2Sharp.CloneOptions class has a FetchOptions field which in turn has a CustomHeaders array that can be used to inject the authentication header during the clone operation, like the following (as mentioned in this issue):
CloneOptions cloneOptions = new() {
CredentialsProvider = (url, usernameFromUrl, types) => new UsernamePasswordCredentials {
Username = $"{USERNAME}",
Password = $"{ACCESSTOKEN}"
},
FetchOptions = new FetchOptions {
CustomHeaders = new[] {
$"Authorization: Basic {encodedToken}"
}
}
};
Repository.Clone(AzureUrl, LocalDirectory, cloneOptions);
And the clone process succeeds (I tested it as well as checked the source code :) )
However, the LibGit2Sharp.PushOptions does not have any such mechanism to inject authentication headers. I am limited to the following code:
PushOptions pushOptions = new()
{
CredentialsProvider = (url, usernameFromUrl, types) => new UsernamePasswordCredentials
{
Username = $"{USERNAME}",
Password = $"{PASSWORD}"
}
};
This is making my push operation fail with the following message:
Too many redirects or authentication replays
I checked the source code for Repository.Network.Push() on Github.
public virtual void Push(Remote remote, IEnumerable<string> pushRefSpecs, PushOptions pushOptions)
{
Ensure.ArgumentNotNull(remote, "remote");
Ensure.ArgumentNotNull(pushRefSpecs, "pushRefSpecs");
// Return early if there is nothing to push.
if (!pushRefSpecs.Any())
{
return;
}
if (pushOptions == null)
{
pushOptions = new PushOptions();
}
// Load the remote.
using (RemoteHandle remoteHandle = Proxy.git_remote_lookup(repository.Handle, remote.Name, true))
{
var callbacks = new RemoteCallbacks(pushOptions);
GitRemoteCallbacks gitCallbacks = callbacks.GenerateCallbacks();
Proxy.git_remote_push(remoteHandle,
pushRefSpecs,
new GitPushOptions()
{
PackbuilderDegreeOfParallelism = pushOptions.PackbuilderDegreeOfParallelism,
RemoteCallbacks = gitCallbacks,
ProxyOptions = new GitProxyOptions { Version = 1 },
});
}
}
As we can see above, the Proxy.git_remote_push method call inside the Push() method is passing a new GitPushOptions object, which indeed seems to have a CustomHeaders field implemented. But it is not exposed to a consumer application and is being instantiated in the library code directly!
It is an absolute necessity for me to use the LibGit2Sharp API, and our end-to-end testing needs to be done on Azure DevOps repositories, so this issue is blocking me from progressing further.
My questions are:
Is it possible to use some other way to authenticate a push operation on Azure from LibGit2Sharp? Can we leverage the PushOptions.CredentialsProvider handler so that it is compatible with the auth-n method that Azure insists on?
Can we cache the credentials by calling Commands.Fetch by injecting the header in a FetchOptions object before carrying out the Push command? I tried it but it fails with the same error.
To address the issue, is there a modification required on the library to make it compatible with Azure Repos? If yes, then I can step up and contribute if someone could give me pointers on how the binding to the native code is made :)
I will provide an answer to my own question as we have fixed the problem.
The solution to this is really simple; I just needed to remove the CredentialsProvider delegate from the PushOptions object, that is:
var pushOptions = new PushOptions();
instead of,
PushOptions pushOptions = new()
{
CredentialsProvider = (url, usernameFromUrl, types) => new UsernamePasswordCredentials
{
Username = $"{USERNAME}",
Password = $"{PASSWORD}"
}
};
¯\(ツ)/¯
I don't know why it works, but it does. (Maybe some folks from Azure can clarify it to us.)
It turns out that this works on windows (push options with no credentials provider). Perhaps because somewhere a native call the OS resolves the credentials using some other means. But in Linux / container environment, the issue persists.
"There was a problem pushing the repo: remote authentication required but no callback set"
I think as you mentioned, minimally the CustomHeaders implementation must be exposed for this to work.
Image of error on console
I have an API Gateway that uses IAM authorization. I have a C# application that I'm hoping to call the API with. I started with a GetMethodRequest but I don't see anyway to set the PathPart parameter.
var userId = _remoteCredentials.UserId;
var key = _remoteCredentials.Key;
var client = new AmazonAPIGatewayClient(userId, key, Amazon.RegionEndpoint.USEast2);
GetMethodRequest getMethodRequest = new GetMethodRequest();
getMethodRequest.HttpMethod = HttpMethod.Get.ToString();
getMethodRequest.ResourceId = "4abcde";
getMethodRequest.RestApiId = "aasfasdfs";
var task = Task.Run(async () => await client.GetMethodAsync(getMethodRequest).ConfigureAwait(false));
I was expecting something like the Test-AGInvokeMethod in the Powershell SDK which allows me to set the query string and the path.
$response = Test-AGInvokeMethod
-RestApiId aasfasdfs
-ResourceId 4abcde
-HttpMethod GET
-PathWithQueryString '/etl/upload_url'
Any help is greatly appreciated.
EDIT Below is something of a solution that I ended up using the AWS4RequestSigner is a library that I found on Github
var signer = new AWS4RequestSigner(userId, key);
var destinationUrl = string.Format("https://ad9vxabc123.execute-api.us-east-2.amazonaws.com/dev/etl/summary/latest?tms_id={0}&model_id={1}", _tmsId, _modelId);
var request = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = new Uri(destinationUrl),
};
var signed = Task.Run(async () => await signer.Sign(request, "execute-api", "us-east-2").ConfigureAwait(false));
var signedResult = signed.Result;
The AmazonAPIGatewayClient is for managing your API Gateway e.g. adding new stages or deleting API keys.
You're looking to invoke a method on your API Gateway, like Test-AGInvokeMethod does.
To invoke your API gateway, you need to call the deployed API endpoint using a HTTP client.
.NET's in-built HttpClient is a good start.
I have stateless web applications deployed to Azure App Service and have ARR Affinity disabled in the Application Settings. Is it possible to leave this disabled, but enable it for specific requests?
I came across a post by benjaminperkins from 2016 that indicated it was possible to disable this for individual requests by adding a "Arr-Disable-Session-Affinity" header, but I would like the reverse of this, "Arr-Enable-Session-Affinity".
I would like to be able to make requests to individual instances to warm-up an in-memory cache following a remote operation. I understand how to build a URL request to an App Server web app instance when ARRAffinity is enabled, but this won't work for me as I do not want to enable this globally.
Here is an example of what I would like to do:
ServiceClientCredentials serviceCreds =
await ApplicationTokenProvider.LoginSilentAsync(this.config.ADTenant, this.config.ADApplicationId, this.config.ADKey);
ResourceManagementClient resourceClient = new ResourceManagementClient(serviceCreds);
resourceClient.SubscriptionId = this.config.SubscriptionId;
WebSiteManagementClient webClient = new WebSiteManagementClient(serviceCreds);
webClient.SubscriptionId = this.config.SubscriptionId;
string urlPath = "custompath/";
SiteInner site =
webClient.WebApps.List().Where(a => a.Id == "webapp id").FirstOrDefault();
IList<SiteInstanceInner> instances =
webClient.WebApps.ListInstanceIdentifiers(site.ResourceGroup, site.Name).ToList();
SiteInstanceInner instance = instances.FirstOrDefault();
// these are initialized as a singleton, inline here as an example
HttpClientHandler handler = new HttpClientHandler() { UseCookies = false };
HttpClient httpClient = new HttpClient(handler, disposeHandler: false);
httpClient.Timeout = new TimeSpan(0, 0, 30);
webClient = await GetWebsiteClientIfNull(webClient);
Uri url = new Uri($"http://{site.DefaultHostName}/{urlPath}");
HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Get, url);
message.Headers.Add("Arr-Enable-Session-Affinity", bool.TrueString);
message.Headers.Add("Cookie", $"ARRAffinity={WebUtility.UrlEncode(instance.Name)};");
HttpResponseMessage response = await webClient.HttpClient.SendAsync(message);
Is there a way to do this when ARRAffinity is disabled on the App Service?
i am using Instasharper (private Instagram API).
my app is Instagram bot for follow and unfollow ,
and i read some where that for one ip there is limitation,
that we can only use 5 account login for one IP in Instagram.
but i have 300-500 user.
should i use proxy?
i dont know how to create proxy for each user and use it.
this is how we create instagram private api and use it
var api = new InstaApiBuilder()
.UseLogger(new SomeLogger())
.SetUser(new UserCredentials(...You user...))
.UseHttpClient(httpHandlerWithSomeProxy)
.Build();
i should create proxy and use it in httpclient
.UseHttpClient(httpHandlerWithSomeProxy)
can u give me some links or helps
tanks
I have solved something similar before by creating an http client with a custom message handler.
Am I right in thinking the parameter to UseHttpClient is an HttpClient?
If so,
e.g.
public class MyHttpMessageHandler : System.Net.Http.HttpMessageHandler
{
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
//...(your implementation here)
}
}
...then:
MyHttpMessageHandler myMessageHandler = new MyHttpMessageHandler();
HttpClient httpHandlerWithSomeProxy = new HttpClient(myMessageHandler);
var api = new InstaApiBuilder()
.UseLogger(new SomeLogger())
.SetUser(new UserCredentials(...You user...))
.UseHttpClient(httpHandlerWithSomeProxy)
.Build();
of course you can pass parameters to MockHttpMessageHandler's constructor if you want to give it some data.
i do this and i think its right way
and i should this for every user
but how can i find proxy adress and port. can anybody explain that to me
tanks
InstaApi _instaApi;
var userSession = new UserSessionData
{
UserName = "",
Password = ""
};
HttpClientHandler handler = new HttpClientHandler()
{
Proxy = new WebProxy("http://127.0.0.1:8888"),
UseProxy = true,
};
_instaApi = new InstaApiBuilder()
.SetUser(userSession)
.UseHttpClientHandler(handler)
.Build();
I'm trying to execute an SSRS report in .NET Core.
Since .NET Core doesn't let you add service references, you have to use the WCF Connected Service to add a reference to the WSDL so it can generate .NET Core compatible code. This is what I did for ReportExecution2005.asmx (SQL Server 2016 if it matters).
I tried using the following to authenticate against the service:
var rsExec = new ReportExecutionServiceSoapClient(ReportExecutionServiceSoapClient.EndpointConfiguration.ReportExecutionServiceSoap,
new EndpointAddress("http://server/ReportServer/ReportExecution2005.asmx"))
{
ClientCredentials =
{
Windows =
{
AllowedImpersonationLevel = TokenImpersonationLevel.Impersonation,
ClientCredential = new NetworkCredential("username", "password")
}
}
};
Also tried setting the Username object instead of Windows object, but either way the result is the following error:
MessageSecurityException: The HTTP request is unauthorized with client authentication scheme 'Anonymous'. The authentication header received from the server was 'NTLM'.
Looking at Fiddler, the code isn't passing the credentials along.
This is the code that got generated off the WSDL
public ReportExecutionServiceSoapClient(EndpointConfiguration endpointConfiguration, System.ServiceModel.EndpointAddress remoteAddress)
: base(ReportExecutionServiceSoapClient.GetBindingForEndpoint(endpointConfiguration), remoteAddress)
{
this.Endpoint.Name = endpointConfiguration.ToString();
ConfigureEndpoint(this.Endpoint, this.ClientCredentials);
}
static partial void ConfigureEndpoint(System.ServiceModel.Description.ServiceEndpoint serviceEndpoint, System.ServiceModel.Description.ClientCredentials clientCredentials);
I may be mistaken, but isn't this calling the private method ConfigureEndpoint with the ClientCredentials object before the ClientCredentials object has even been set?
I'm not seeing any other way to configure the ClientCredentials or call ConfigureEndpoint, so how exactly are you supposed to authenticate? The other constructors are basically the same thing, except for one which takes in a Binding instead of an EndpointConfiguration. Any ideas?
After fighting with this for a day, I found an approach that seems to work, by using the only constructor that does not immediately call ConfigureEndpoint as pointed out in the question. If I create a binding that specifies NTLM, and I pass that binding along with a manually created endpoint, it works:
var binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportCredentialOnly)
{
Security =
{
Transport = new HttpTransportSecurity {ClientCredentialType = HttpClientCredentialType.Ntlm}
}
};
var reportService = new CssbiReportService.ReportExecutionServiceSoapClient(binding,
new EndpointAddress("http://myserver/ReportServer/ReportExecution2005.asmx"));
This is working for me in .NET Core.
Edit: update the code for .NET Core
Unfortunately, I don't have SSRS here to test the code right now.
But, try this code (no error check):
// parameters of report (if any)
ParameterValue[] parameters = {new ParameterValue {Name = "ApontamentoID", Value = "364"}};
// connect to the service
ReportExecutionServiceSoapClient webServiceProxy =
new ReportExecutionServiceSoapClient(
ReportExecutionServiceSoapClient.EndpointConfiguration.ReportExecutionServiceSoap,
"http://report_server_url/ReportExecution2005.asmx?wsdl");
// logon the user
await webServiceProxy.LogonUserAsync("username", "password", null);
// ask for the report
await webServiceProxy.LoadReportAsync("/report_path", null);
await webServiceProxy.SetExecutionParametersAsync(parameters, null);
// you can use RenderStreamRequest too
RenderRequest request = new RenderRequest("pdf", null);
RenderResponse response = await webServiceProxy.RenderAsync(request);
// save to the disk
System.IO.File.WriteAllBytes(#"c:\temp\output.pdf", response.Result);
// logoff the user
await webServiceProxy.LogoffAsync();
// close
await webServiceProxy.CloseAsync();
var binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportCredentialOnly)
{
Security =
{
Transport = new HttpTransportSecurity {
ClientCredentialType = HttpClientCredentialType.Ntlm
}
}
};
yourClient = ReportExecutionServiceSoapClient(rsBinding, rsEndpointAddress) {
ClientCredentials =
{ ...
^^^ This for NTLM.
Also, I was getting read-only errors trying to set some properties on the client after it had been created. In case it helps someone, properties must all be set at client-creation time to avoid this as per "yourClient" above.
I had the same problem, for me the following addition was helpful:
ReportExecutionServiceSoapClient rsClient = new ReportExecutionServiceSoapClient(rsBinding, rsEndpointAddress);
rsClient.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;