I'm attempting to call Identity Server 4 with Flurl
Im getting a 400 back from the ID server.
The credentials etc work when I make the call with PostMan or with the methods available in IdentityModel.Client.
I am unsure about the Post i.e. PostUrlEncodedAsync
As you can see I have tried various combinations.
This.. does not work
var address = "http://localhost:8027/connect/token";
var x = await address
.WithBasicAuth("clientName", "secretValue")
.SetQueryParams(
new
{
GrantType = "client_credentials",
Scope = "requiredScope"
}
)
.PostUrlEncodedAsync(new { });
//.SendAsync(HttpMethod.Post, null, CancellationToken.None);
//.SendAsync(HttpMethod.Post, y);
This.. does.
var apiClientCredentials = new ClientCredentialsTokenRequest()
{
Address = "http://localhost:8027/connect/token",
ClientId = "clientName",
ClientSecret = "secretValue",
Scope = "requiredScope"
};
var client = new HttpClient();
var tokenResponse = await client.RequestClientCredentialsTokenAsync(apiClientCredentials);
if (tokenResponse.IsError)
{
}
I can see that deep doen inside 'RequestClientCredentialsTokenAsync' it does parameters.Add to add grant type and scope hence I have added those as params.
Have also tried adding the client id and secret to the query params.
Same 400 message.
Any help greatly appreciated.
I believe the difference is that IdentityModel knows to serialize those ClientCredentialsTokenRequest property names like ClientId and GrantType to snake-case, i.e. client_id and grant_type, as required by OAuth2. When you hand Flurl an anonymous object, it just serializes the property names exactly as-is. To make it work with Flurl you can either create a class and decorate it with Json.NET serialization attributes, or (what I typically do) keep it simple take some liberties with C# property name conventions:
.SetQueryParams(
new
{
grant_type = "client_credentials",
scope = "requiredScope"
}
Related
I'm trying to create an httpclient in Blazor Server side which would create the least amount of configuration effort every time I call my webapi.
Essentially I would like achieve the following:
Named HTTPClient I can automatically call when I call a function in my webapi.
The webapi requires a bearer token, which I get by calling AcquireTokenSilent
Would be great if I don't have to specify the httpclient when I call the api
The webapi has been added as a service reference, so there is scaffold classes created under the namespace myapp.server.api
To start this off, I created the following in startup:
services.AddHttpClient<myapp.server.api.swaggerClient>(c =>
{
c.BaseAddress = new Uri("https://api.myapp.com/");
AzureADB2COptions opt = new AzureADB2COptions();
Configuration.Bind("AzureAdB2C", opt);
IConfidentialClientApplication cca =
ConfidentialClientApplicationBuilder.Create(opt.ClientId)
.WithRedirectUri(opt.RedirectUri)
.WithClientSecret(opt.ClientSecret)
.WithB2CAuthority(opt.Authority)
.WithClientName("myWebapp")
.WithClientVersion("0.0.0.1")
.Build();
IHttpContextAccessor pp;
string signedInUserID = context.User.FindFirst(ClaimTypes.NameIdentifier).Value;
new MSALStaticCache(signedInUserID, pp.HttpContext).EnablePersistence(cca.UserTokenCache);
var accounts = cca.GetAccountsAsync().Result;
AuthenticationResult result = null;
result = cca.AcquireTokenSilent(opt.ApiScopes.Split(' '), accounts.FirstOrDefault()).ExecuteAsync().Result;
c.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", result.AccessToken);
});
My hope is to be able to call my api in my views in this way:
myapp.server.api.swaggerClient t = new myapp.server.api.swaggerClient();
currentCount = t.WeatherForecastAsync().Result.FirstOrDefault().Summary;
calling a new instance of swaggerclient requires me to specify an httpclient, so my hopes is to inject the httpclient I am configuring on a global level for that type can be injected automatically.
The pieces I need help with:
Given that I have specified my httpclient scoped to a specific type, would it call automatically if I call a function in my webapi? (Does not seem to fire when debugging)
To get the bearer token, I need to get the current userID, which is in the authstateprovider... seeing that this is in Startup, is getting it from DI even possible?
Any easy way to inject the httpclient on the constructor of my webapi classes? would I be able to get the httpclient in the constructor so that I essentially have a parameterless constructor not asking for httpclient?
Concerning your first question, inject the Web API HttpClient like this in your view:
#inject myapp.server.api.swaggerClient MyClient
and then in the code block:
currentCount = MyClient.WeatherForecastAsync().Result.FirstOrDefault().Summary;
You should be able to debug the code inside AddHttpClient.
I am writing an IdentityServer4 implementation and using the Quickstart project described here.
When you define an ApiResource (using InMemory classes for now) it looks like IdentityServer creates a Scope with the same name as the resource. For example
public static IEnumerable<ApiResource> GetApiResources()
{
return new List<ApiResource>
{
new ApiResource("api", "My API")
};
}
will create a Scope called "api" (this is done in the ApiResource constructor). If I add "api" as an allowed Scope on my Client object (using InMemoryClients for a proof of concept) and request this api Scope in the scope query string parameter in my auth request from my JavaScript client I get an invalid_scope error message.
I found by following this documentation you can add Scopes to the ApiResource through the Scopes property like so
new ApiResource
{
Name = "api",
DisplayName = "Custom API",
Scopes = new List<Scope>
{
new Scope("api.read"),
new Scope("api.write")
}
}
So now if I instead define my ApiResource like this and request the Scopes api.read and api.write (and add them to the AllowedScopes property on the Client Object) then everything works fine EXCEPT the consent page which shows duplicate Scopes. It shows api.read 2 times and api.write 2 times. See the consent screen here
The Client configuration is as follows:
new Client
{
ClientId = "client.implicit",
ClientName = "JavaScript Client",
AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true,
RedirectUris = { "http://localhost:3000/health-check" },
PostLogoutRedirectUris = { "http://localhost:3000" },
AllowedCorsOrigins = { "http://localhost:3000" },
AllowedScopes = {
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"customApi.read", "customApi.write"
}
}
Why is this happening? Am I doing something obviously wrong?
Update:
Here a portion of the discovery document that shows the Scopes are only listed once...
It looks like the problem is with the Quickstart UI... or with the Scope.cs class depending on how you look at it. Specifically, in the method and line shown in the class ConsentService.cs
The following code
vm.ResourceScopes = resources.ApiResources.SelectMany(x => x.Scopes).Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray();
is not filtering out the duplicates. That is, even if two Scopes have the same name they are not considered equal. So if GetHashCode and Equals were overridden in Scope.cs (which is in IdentityServer4 - not the Quickstart) then it would solve this problem. In that case SelectMany would return a unique set. This is because the ApiResources property is implemented as a HashSet. Alternatively, you could write your own logic to make this return a unique set of Scopes. This is how I solved the problem. I wrote something very similar to Jon Skeet's answer in this post that filtered out the duplicate Scopes.
The problem lies within IdentityService4 code in the implementation of InMemoryResourcesStore.FindApiResourcesByScopeAsync and was fixed with this commit. You can use the dev branch where it's included since June 22th 2017, but it was never released in any of the NuGET packages targeting .NET Standard 1.4, which is very annoying.
I created an issue and requested it to get patched:
https://github.com/IdentityServer/IdentityServer4/issues/1470
For fixing the view, i added the line marked with Todo to ConsentService.cs
var resources = await _resourceStore.FindEnabledResourcesByScopeAsync(request.ScopesRequested);
if (resources != null && (resources.IdentityResources.Any() || resources.ApiResources.Any()))
{
// TODO: Hotfix to cleanup scope duplication:
resources.ApiResources = resources.ApiResources.DistinctBy(p => p.Name).ToList();
return CreateConsentViewModel(model, returnUrl, request, client, resources);
}
This solves the display problem, but the scope will still be included multiple times in the access token which makes it bigger since it squares the scope count for that API. I had 3 scopes, so each one was included 3 times, adding 6 unneeded scope copies. But at least it's usable until it get's fixed.
There was a bug that was just fixed in 1.5 that addresses this: https://github.com/IdentityServer/IdentityServer4/pull/1030. Please upgrade and see if that fixes the issue for you. Thanks.
I can send a parameter from as3 to asp. And I can get a value from db. But unfortunatelly I cant combine both of them. Is it possible to send a ID parameter from as3 to asp where I want to make a sql query on a db. Then query result will return back to the as3. Users can login with their id number. And they can see their own datas on the as3 application. My sample codes are given:
I can send values with these codes:
var getParams:URLRequest = new URLRequest("http://www***********/data.asp");
getParams.method = URLRequestMethod.POST;
var paras:URLVariables = new URLVariables();
paras.parameter1 = ""+userID;
getParams.data = paras;
var loadPars:URLLoader = new URLLoader(getParams);
loadPars.addEventListener(Event.COMPLETE, loadCompleted);
loadPars.dataFormat = URLLoaderDataFormat.VARIABLES;
loadPars.load(getParams);
function loadCompleted(event:Event):void
{
trace("sent")
}
I can get values from db with these codes:
var urlLoader:URLLoader =new URLLoader();
urlLoader.load(new URLRequest("http://www***********/data.asp"));
urlLoader.dataFormat = URLLoaderDataFormat.VARIABLES;
urlLoader.addEventListener(Event.COMPLETE, onXMLLoad);
function onXMLLoad(event:Event):void
{
var loader:URLLoader = URLLoader(event.target);
var scrptVars:URLVariables = new URLVariables(loader.data +"");
returnParameter= scrptVars.LINK0;
high.HighScore.text = returnParameter + "";
}
What is the logic of combining them?
Sory for my English level :)
To combine the second one into the first, you just need to read the URLLoader's data property (which is the response from the server) on the loadCompleted method (same as you're doing in the onXMLLoad method):
function loadCompleted(event:Event):void
{
trace("sent and received", loadPars.data);
high.HighScore.text = loadPars.data.LINK0;
}
The COMPLETE event for a URLLoader fires once the request has received a response. If your server adds data to that response, it can be found in the data property of the URLLoader.
So to summarize, sending and receiving can be done all in one operation with one URLLoader. The data you send to the server, is found in the URLRequest object passed to the URLLoader, the data that comes back from that request, is found in the data property of the URLLoader object (but only after the COMPLETE event fires).
I am developing a small REST app in slim framework. In that, users password is send as encrypted in the request body as xml or json. I want to de-crypt that password in a callable function and update the request body so that in the actual call back function we can validate the password without de-cryptng. I want to do those steps as follows:
$decrypt = function (\Slim\Route $route) use ($app) {
// Decrypt password and update the request body
};
$update = function() use ($app) {
$body = $app->request()->getBody();
$arr = convert($body);
$consumer = new Consumer($arr);
if ($consumer->validate()) {
$consumer->save();
$app->response()->status(201);
} else {
.....
}
}
$app->put('/:consumer_id', $decrypt, $update);
We can modify the body like following way:
$env = $app->environment;
$env['slim.input_original'] = $env['slim.input'];
$env['slim.input'] = 'your modified content here';
Courtsey: ContentTypes middleware
You say you want decrypt the password and update the request body. If you're encrypt the password at client side, i would rather decrypt the password in a server side layer like API service (or something that consume the business layers like a controller in mvc).
I do believe that this decryption process should belong to your application instead of doing it outside before consuming your code. I don't know how you encrypt but if you use server side programming to generate a new hash in those requests, for me that's even a better reason to do it inside the library.
That's how i handle this type of tasks, i try to use only the frameworks for consuming libraries and not handling any logic.
However if you want to do this, you could transform the request body and save it in a new location for services that need to decrypt the password.
I use Middleware for almost every code i need to write specifically to Slim layers. I only passe functions consuming classes that act as API layers and are abstracted from Slim. For your case, use a Middleware to keep this logic in his own place.
class DecriptPasswordRequest extends \Slim\Middleware
{
public function call()
{
$decriptedRoutes = array('login', 'credentials');
$app=$this->app;
$container = $app->container;
$currentRoute = $app->router()->getCurrentRoute();
if ($app->request->getmethod() == 'POST' && in_array($currentRoute, $decriptedRoutes){
$body = $app->request->post();
if (!isset($body['password'])){
throw new Exception('Password missing');
}
$provider = new ClassThatDecryptPassword();
$body['password'] = $provider->decrypt($body['password']);
}
$container['bodydecripted'] = $body;
$this->next->call();
}
}
I have the following routes:
/projects/{projectName}
and
/projects/{projectName}/Wall/{wallName}
Now I'd like to have that all GETs be allowed but PUT, POST, DELETE should only be allowed by project members i.e. users members of that project. I have a special class that given a user id and project name I can get the status of the user's membership - something like MyEnroler.getRole(userId, projectName) - where the userId is part of the request header and the projectName is taken from the URI.
I've tried a number of things but doesn't work. Here's the idea:
public class RoleMethodAuthorizer extends Authorizer {
#Override
protected boolean authorize(Request req, Response resp) {
//If it's a get request then no need for further authorization.
if(req.getMethod().equals(Method.GET))
return true;
else
{
String authorEmail = req.getClientInfo().getUser().getIdentifier();
String projectName = req.getAttributes().get("project").toString();
Role userRole = MyEnroler.getRole(authorEmail, projectName);
//forbid updates to resources if done by non-members of project
if(userRole.equals(MyEnroler.NON_MEMBER))
return false;
//for everybody else, return true
return true;
}
}
}
Now simply doing the following completely fails when creating inbound root in the Application:
Router projectRouter = new Router(getContext());
RoleMethodAuthorizer rma = new RoleMethodAuthorizer();
//Guard declaration here. Then setNext Restlet
guard.setNext(projectRouter);
projectRouter.attach("/projects/{project}",rma);
Router wallRouter = new Router(getContext());
wallRouter.attach("/Wall/{wallName}", WallResource.class);
rma.setNext(wallRouter);
//return guard;
So a request to /projects/stackoverflow/Wall/restlet fails. The URL is never found. I'm guessing since it's trying to match it with the projectRouter. Well I tried the various modes (MODE_BEST_MATCH or MODE_FIRST/NEXT_MATCH) to no avail.
Nothing seems to work. Conceptually this should work. I'm only intercepting a call and just being transparent to the request, but don't know how things are working on the inside.
I could move the authorizer just after the guard, but I'd lose access to the request attribute of projectName - I don't wish to parse the URL myself to search for the projectName since the URL pattern could change and would break the functionality - i.e. require 2 changes instead of one.
Any ideas how to achieve this?
I would use the standard RoleAuthorizer class to supply the list of allowed roles, along with your custom enroller probably split into two I would then add a custom Filter class that does something like this to call your Enrolers.
protected int beforeHandle(final Request request, final Response response) throws ResourceException {
final String projectName = (String) request.getAttributes().get("projectName");
// Check that a projectName is supplied, should not have got this far otherwise but lets check.
if (projectName == null || projectName.isEmpty()) {
throw new ResourceException(Status.CLIENT_ERROR_NOT_FOUND);
}
if (Method.GET.equals(request.getMethod())){
new ReadEnroler(projectName).enrole(request.getClientInfo());
}else{
new MutateEnroler(projectName).enrole(request.getClientInfo());
}
return super.beforeHandle(request, response);
}
the enrolers would then set the appropriate values in the clientInfo.getRoles() Collection when enrole was called.