Add Roles in IdentityServer Response object - asp.net-web-api

I'm using an Identity Server 3. I'm getting the Bearer Token from the IdentityServer3 only if the user has privilege otherwise return back as Un-Authorized.
De-Coded version of AccessToken is
{
"iss": "https://localhost:1234/core",
"aud": "https://localhost:1234/core/resources",
"exp": 1489060441,
"nbf": 1489056841,
"client_id": "app1",
"scope": [
"openid",
"profile",
"email",
"roles",
"app1"
],
"sub": "93f7aab4-5469-4c85-8e73-5dcd859ed2a8",
"auth_time": 1489056776,
"idp": "idsrv",
"amr": [
"password"
]
}
My Expectation is
{
"iss": "https://localhost:1234/core",
"aud": "https://localhost:1234/core/resources",
"exp": 1489060441,
"nbf": 1489056841,
"client_id": "app1",
"scope": [
"openid",
"profile",
"email",
"roles",
"app1"
],
"roles": [
"Admin"
],
"sub": "93f7aab4-5469-4c85-8e73-5dcd859ed2a8",
"auth_time": 1489056776,
"idp": "idsrv",
"amr": [
"password"
]
}
Client:
new Client
{
ClientId = #"APP1",
ClientName = #"APP Implicit Client",
Enabled = true,
Flow = Flows.Implicit,
RequireConsent = true,
AllowRememberConsent = true,
RedirectUris = new List<string> {"http://localhost:5775/callback/"},
PostLogoutRedirectUris = new List<string> {"http://localhost:5775/logout"},
AllowedCorsOrigins = new List<string>{ "http://localhost:5775/" },
AllowedScopes =
new List<string>
{
Constants.StandardScopes.OpenId,
Constants.StandardScopes.Profile,
Constants.StandardScopes.Email,
Constants.StandardScopes.Roles,
"app1"
},
AccessTokenType = AccessTokenType.Jwt
}
User:
new InMemoryUser
{
Username = "User1",
Password = "Password123!",
Subject = "1",
Claims = new List<Claim>
{
new Claim(Constants.ClaimTypes.GivenName, "Bala"),
new Claim(Constants.ClaimTypes.FamilyName, "Balamanigandan"),
new Claim(Constants.ClaimTypes.Email, "balamanigandan.b#gmail.com"),
new Claim(Constants.ClaimTypes.Role, "Admin")
}
}
My WebAPI has a method with the following decoration [Authorize(Roles = "Admin")], which needs the Bearer token of Role "Admin"
Kindly assist me how to add a Role in this Token to access the [Authorize(Roles = "Admin")] methods in WebAPI.

try setting Scope called "Roles" and ScopeClaim called "Admin" and make ScopeTypeas "Resource" if you want that in "access_token" and i see you already have claims user claims as "Role" change that as "Roles"
Example Scope
public static Scope Roles
{
get
{
return new Scope
{
Name = "Roles",
Type = ScopeType.Resource,
Emphasize = true,
IncludeAllClaimsForUser = true,
Claims = new List<ScopeClaim>
{
new ScopeClaim("Admin",true)
}
};
}
}

Related

How to access identity provider (idp) claim from .net Core web API in IdentityServer4?

In my .Net Core web API protected by IdentityServer4, I need to decide what identity provider (Google, Windows, or local, for instance) authenticated the user. So far, I am not sure how to do that.
If I search for idp claim from access_token in a controller, as shown below, I can see the claim value correctly
var accessToken = await HttpContext.GetTokenAsync("access_token");
var token = new JwtSecurityTokenHandler().ReadJwtToken(accessToken);
var claim = token.Claims.First(c => c.Type == "idp").Value;
But if I try to find it using AuthorizationHandlerContext in a non-controller class in API as following, as shown in code below, it is not there
var identity = context.User.Identity as ClaimsIdentity;
if (identity != null)
{
IEnumerable<Claim> claims = identity.Claims;
// var v = identity.FindFirst("idp").Value;
}
So looks like that idp is indeed in the token, it just not accessible from the non-controller class where it is needed. How do I get idp from non-controller class in API?
UPDATE - 1
Here is my ConfigureService in my API
public void ConfigureServices(IServiceCollection services)
{
IdentityModelEventSource.ShowPII = true; // test only
services.AddControllers();
services.AddControllers()
.AddNewtonsoftJson(
options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
});
services.Configure<QLHostOptions>(Configuration.GetSection(QLHostOptions.Host));
services.AddAuthentication("Bearer").AddJwtBearer("Bearer", options =>
{
options.Authority = Configuration.GetSection(QLHostOptions.Host).Get<QLHostOptions>().IdentityGateway;
options.SaveToken = true;
// test only
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false
};
}).AddOpenIdConnect(options =>
{
options.ClaimActions.Remove("aud");
});
services.AddTransient<IAuthorizationPolicyProvider, QLPolicyProvider>();
services.AddTransient<IAuthorizationHandler, QLPermissionHandler>();
services.AddTransient<gRPCServiceHelper>();
}
UPDATE-2
Changed ...Remove("idp") to inside AddJwtBearer, as Tory suggested, but it doesn't take it (see screenshot below):
and here is the access token from API
"eyJhbGciOiJSUzI1NiIsImtpZCI6IjBFM0Y2MkRGMTdFQUExQURFRTc1NDQzQzQ0M0YxRkU2IiwidHlwIjoiYXQrand0In0.eyJuYmYiOjE2NDAwMzExMDcsImV4cCI6MTY0MDAzNDcwNywiaXNzIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6NjAwNSIsImF1ZCI6Imh0dHBzOi8vbG9jYWxob3N0OjYwMDUvcmVzb3VyY2VzIiwiY2xpZW50X2lkIjoibXZjIiwic3ViIjoiZGM0YWI1OGMtNGVjMC00ZTAyLWIxM2YtYzEyYzk1MzJlNzcyIiwiYXV0aF90aW1lIjoxNjQwMDMxMTA2LCJpZHAiOiJHb29nbGUiLCJBc3BOZXQuSWRlbnRpdHkuU2VjdXJpdHlTdGFtcCI6IjJEQ0hXNVRER1E3NDNSUEpOWE43SVJIWlRIVllIUTRJIiwibmFtZSI6IkxpZmVuZyBYdSIsImVtYWlsIjoibGlmZW5neHUyNkBnbWFpbC5jb20iLCJyb2xlIjoiUUxBZG1pbiIsInByZWZlcnJlZF91c2VybmFtZSI6IjM1ZGJkMmY2LTlmNDUtNDJhYy04M2EzLTgzZmUyMTFjNTNiNSIsIklzRW5hYmxlZCI6IlRydWUiLCJRaWNMaW5rVUlEIjoiIiwianRpIjoiQzE3Qjc2QzQ0NjA4MzkxMDBENEExMEM4Q0YwQzA1NDEiLCJzaWQiOiIzMEY5NTA5NzQ3OUUxMzAyMUVBQTdDOTAzNzg4MDcxNiIsImlhdCI6MTY0MDAzMTEwNywic2NvcGUiOlsib3BlbmlkIiwicHJvZmlsZSIsImVtYWlsIiwiUWljTGlua0NJRCIsIlFpY0xpbmtVSUQiLCJyb2xlcyIsIklzRW5hYmxlZCIsIkxpZmVuZ0FQSSIsIlFpY0xpbmtBUEkiLCJvZmZsaW5lX2FjY2VzcyJdLCJhbXIiOlsiZXh0ZXJuYWwiXX0.boZCqYImWfkE48X5UgFOAAz9bR6CH2cwAYHGd4Ykg0vDH9qnYdje5Zmqov4HpINsu_rt16zxAX_JCEn0hvdznXK2NQyZSBGsjF0tcMgtOY0__kAfhpOT-fORakiIjeMWIKG7tPEHCxSib0wNuMNw6i3o1giAnPt0ch2DH0fBtaEYkq4MRKMCteFuqbX0cogXIuMewNywMvrHv4_MixhMy3L8_xIwFvTZ67jhUn4Fd5X58-jc-RPNudcP95XIjzHm9OzWfgegV1IAKjsv98XEYX1pUxm-nrOMgYWxEJSyxEpp0L_9RzKTr_LZ-ep-x5QRvVewgiozJV3mse0pHgTjbw"
By default many of the more internal claims in a token are removed from the User ClaimsPrinicpal claims.
If you want to get a specific claim into your user, you can use in the client:
}).AddOpenIDConnect(options =>
{
//Will result in that the aud claim is not removed.
options.ClaimActions.Remove("idp");
...
secondly, some of the claims are renamed and if you want to disable that renaming, you can add:
// By default, Microsoft has some legacy claim mapping that converts
// standard JWT claims into proprietary ones. This removes those mappings.
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
JwtSecurityTokenHandler.DefaultOutboundClaimTypeMap.Clear();
For the API you should not need to do anything special to get the idp claim. I just ran a test with this setup in .NET 5:
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMyJwtBearer(opt =>
{
opt.IncludeErrorDetails = true;
opt.MapInboundClaims = false;
opt.TokenValidationParameters.RoleClaimType = "role";
opt.TokenValidationParameters.NameClaimType = "name";
opt.Audience = "paymentapi";
opt.Authority = "https://localhost:6001";
});
services.AddControllers();
}
I did give it a test on .NET 5 and if I have this access token:
{
"nbf": 1640033816,
"exp": 1640037416,
"iss": "https://localhost:6001",
"aud": "paymentapi",
"client_id": "clientcredentialclient",
"managment": "yes",
"email": "tore#tn-data.se",
"name": "tore nestenius",
"idp": "Google",
"role": [
"admin",
"developer",
"support"
],
"website": "https://www.tn-data.se",
"jti": "5DC46A29372031F0AA6F7B62B5FDCCD6",
"iat": 1640033816,
"scope": [
"payment"
]
}
Then my user in my API controller contains the idp claim:

How to create multiple schema in #RequestBody of swagger openapi specification 3.0 using springdoc?

I have the below api for which I need to have two parameters of content type application/x-www-form-urlencoded and therefore am using #RequestBody instead of #Parameter
#Operation(summary = "Revoke given permissions", description = "Allows admin to revoke permissions to users")
#RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public void revokePermission(
#RequestBody(description = "the permission id", content = #Content(mediaType = "application/x-www-form-urlencoded",
schema = { #Schema(type = "String", name = "permission_id",
description = "id of the permission to be revoked", required = true)},
{ #Schema(type = "String", name = "permission_type",
description = "the permission type")}))
String permission_id, String permissionType) {
do_something();
}
I need the swagger.json to be like below example, but I do not know how to generate it using springdoc .I tried #ArraySchema also , but I am not getting the output I need. I am making some mistakes in the syntax and not able to find examples online.
"requestBody": {
"content": {
"application/x-www-form-urlencoded": {
"schema": {
"properties": {
"permission_id": {
"description": "id of the permission to be revoked",
"type": "string"
},
"permission_type": {
"description": "the permission type",
"type": "string"
}
},
"required": ["permission_id"]
}
}
}
}
Any help is highly appreciated. TIA
The The simplest way to achieve what you want is to define the permission data in simple object as follow:
#Schema(name = "permissionData")
public class PermissionData {
#Schema(type = "String", name = "permiddionId", description = "id of the permission to be revoked", required = true)
#JsonProperty("permiddionId")
String permiddionId;
#Schema(type = "String", name = "permissionType",description = "the permission type")
#JsonProperty("permissionType")
String permissionType;
}
And then you controller method:
#Operation(summary = "Revoke given permissions", description = "Allows admin to revoke permissions to users")
#RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public void revokePermission(#RequestBody(description = "the permission data") PermissionData permissionData) {
}

How to set role when register a new user using strapi?

I'm try register an user and i would like to know how can i set role as Administrator.
I tried
user: {
username: "elialber",
email: "falecom#elialber.com.br",
password: "123456789",
role: ???
}
I'm using Angular5 and just api strapi
step 1: Go to node_modules/strapi-plugin-users-permissions/controllers/Auth.js
step 2: Go to on line 457(in my code)
step 3: Comment params.role=role.id; like (//params.role = role.id;)
and now you can pass role id like 1 or 2 or 3 as follow
{
"blocked":false,
"confirmed":true,
"username":"aziz",
"email":"musa#binarymarvels.com",
"password":"musa01010",
"role": 2
}
and now I receive a response like:
{
"jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MzQsImlhdCI6MTYwMjUzMzk0MSwiZXhwIjoxNjA1MTI1OTQxfQ.xM2tmP8PUcwABiW9lmxkjkmcmdmckMB8wlQ1YsBOQI",
"user": {
"id": 34,
"username": "aziz",
"email": "musa#binarymarvels.com",
"provider": "local",
"confirmed": true,
"blocked": false,
"role": {
"id": 2,
"name": "Public",
"description": "Default role given to the unauthenticated user.",
"type": "public"
},
"created_at": "2020-10-12T20:19:01.285Z",
"updated_at": "2020-10-12T20:19:01.299Z"
}
}
In Strapi Admin:
Roles & Permissions -> Advances settings
Set the field Default role for authenticated users to "Administrator"
One can write a custom endpoint to update user role, and add a corresponding controller action like below:
module.exports = {
updateUserRole: async ctx => {
return await strapi.services.auth.updateUserRole (ctx.request.body.user,
ctx.request.body.roleType);
}
};
auth service to update user role can be defined in api/auth/services/Auth.js as below:
module.exports = {
updateUserRole: async (userid, roleType) => {
const role = await strapi.query('role', 'users-permissions').findOne({ type:roleType }, []);
if ( role ) {
strapi.query('user', 'users-permissions').update({ id: userid }, { role: role.id });
return true;
}
return false;
}
};
You can do it like this
user: {
username: "elialber",
email: "falecom#elialber.com.br",
password: "123456789",
role: {
_id: "5afac95f0eabf75b64c6ef92" // role id that your app generate
}}
https://github.com/strapi/strapi/blob/master/packages/strapi-plugin-users-permissions/controllers/Auth.js#L261
Add following code to root/plugins/users-permissions/controllers/Auth.js in front of line 261
const roleType = params.role || settings.default_role
and change line 261
const role = hasAdmin === false ? root : await strapi.query('role', 'users-permissions').findOne({ type: roleType }, []);
post will like
{
"username": "test user",
"email": "qqqqqq#gmail.com",
"password": "qqqqqq",
"role": "boss"
}
boss <--- role created from admin panel
worked on strapi v-3.0.0-alpha.25.2

RavenDb 4: Check if a string of an array of strings exists in different array of strings

I am trying to filter my activities based on user roles. The manager is only allowed to see the activities that contain the role Manager. See below for more information.
Data:
var user = new User
{
Roles = [
"Manager"
]
}
var activities = new List<Activity>
{
new Activity
{
Description = "My First Activity",
Roles = [
"Admin",
"Manager"
]
},
new Activity
{
Description = "My Second Activity",
Roles = [
"Admin"
]
},
new Activity
{
Description = "My Third Activity",
Roles = [
"Manager",
"Client"
]
},
}
Query that filters the activity
var filtered = await context.Query<Activity>()
.Where(x => x.Roles.Any(y => user.Roles.Contains(y)))
.ToListAsync();
Expected result of filtered:
[
{
new Activity
{
Description = "My First Activity",
Roles = [
"Admin",
"Manager"
]
}
new Activity
{
Description = "My Third Activity",
Roles = [
"Manager",
"Client"
]
},
}
]
Error from query
System.InvalidOperationException : Can't extract value from expression of type: Parameter
I am getting an error, so obviously I am doing something wrong. Is this the correct way or is there something better?
This requires RavenDB to do computation during query, try this, instead:
.Where(x => x.Roles.In(user.Roles)) (might need to add a using statement for the In extension method, Raven.Client.Linq, IIRC).

Using acr_values="idp:Google" with Xmarin's IdentityModel.OidcClient does not Skip login page on identityserver4

I am using the sample code for xamarin oidcclient to authenticate users,
I want to skip login page if user chooses an external provider, I was able to do that in sample code by adding extras parameter acr_values:
var result = await _oidcClient.LoginAsync(extraParameters: new { acr_values="idp:Google" });
and it worked, but not with me implementation of identityserer4, my client config is:
new Client
{
ClientId = "mvc",
ClientName = "MVC Client",
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
RequireConsent=false,
AccessTokenType=AccessTokenType.Jwt,
AllowAccessTokensViaBrowser=true,
ClientSecrets = {
new Secret("secret".Sha256())
},
// where to redirect to after login
RedirectUris = { "http://192.168.1.6.nip.io:13013/Home/", "io.identitymodel.native://TrainAppCallback" },
// where to redirect to after logout
PostLogoutRedirectUris = { "http://192.168.1.6.nip.io:13013/signout-callback-oidc" },
AllowedScopes = new List<string>
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"Advancio.TrainApp.WebApi"
},
AllowOfflineAccess = true,
RequirePkce = true
}
am I missing some configuration on idsrv? because the only difference in my client code is the authorization endpoint (instead of https://demo.identityserver.io I use my local implementation of idsrv).
the authorization request log is :
info: IdentityServer4.Endpoints.AuthorizeEndpoint[0]
ValidatedAuthorizeRequest
{
"ClientId": "mvc",
"ClientName": "MVC Client",
"RedirectUri": "io.identitymodel.native://TrainAppCallback",
"AllowedRedirectUris": [
"http://192.168.5.165.nip.io:13013/Home/",
"io.identitymodel.native://TrainAppCallback",
"io.identitymodel.native://callback"
],
"SubjectId": "anonymous",
"ResponseType": "code id_token",
"ResponseMode": "fragment",
"GrantType": "hybrid",
"RequestedScopes": "openid profile Advancio.TrainApp.WebApi",
"Nonce": "9723a6643f38b51711400677cb30db73c4c0dda2de20c7ccebf71a06937be32f943d29e04763571337cda90391bb994afaf1d3188992955f045b16825133771b",
"AuthenticationContextReferenceClasses": [
"idp:Google"
],
"Raw": {
"client_id": "mvc",
"response_type": "code id_token",
"scope": "openid profile Advancio.TrainApp.WebApi",
"redirect_uri": "io.identitymodel.native://TrainAppCallback",
"nonce": "9723a6643f38b51711400677cb30db73c4c0dda2de20c7ccebf71a06937be32f943d29e04763571337cda90391bb994afaf1d3188992955f045b16825133771b",
"code_challenge": "Fpdz7OYUZPyE47Qvc3efwg8-UuAmLhiOQmCAYMqoSZw",
"code_challenge_method": "S256",
"acr_values": "idp:Google"
}
}

Resources