How set swagger client claims with identityServer4 - oauth-2.0

I'm protecting a .NET Core Web API with an access token issued by an IDS4.
It works and I added some claims in the client configuration, as follows:
// ... other code
new Client
{
ClientId = "apiclient",
AllowedGrantTypes = GrantTypes.ClientCredentials,
ClientSecrets = {
new Secret("mysupersecret".Sha256())
},
AllowedScopes = {
"joinot",
JwtClaimTypes.Role,
},
Claims = new List<Claim>() { new Claim("role", "WonderWoman"), new Claim("VatId", "123abc") },
},
// ... other code
These are the claims that I see in the webapi.
I found what I added with the prefix "client_".
For testing purposes I'm using swagger and I have this config for it:
new Client {
ClientId = "swagger_api_client",
ClientName = "Swagger API Client",
AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true,
RedirectUris = {"http://localhost:57303/swagger/oauth2-redirect.html"},
AllowedScopes = {
"joinot",
JwtClaimTypes.Role,
},
Claims = new List<Claim>() { new Claim("role", "ManOfSteel"), new Claim("VatId", "abc123") },
RequireConsent = false
},
This means that I have to authenticate with an interactive user and than I can call the webapi.
When I do it, the claims I found in the webapi are not as I expected.
"client_role" and "client_VatId" are not in the list.
How can I insert the claims for both the console and the swagger client?

You can set AlwaysSendClientClaims to true :
Claims = new List<Claim>() { new Claim("role", "WonderWoman"), new Claim("VatId", "123abc") },
AlwaysSendClientClaims =true,
If set, the client claims will be sent for every flow. If not, only for client credentials flow (default is false) . So your console application which use client credential flow would work even not set that property.
Reference document : http://docs.identityserver.io/en/latest/reference/client.html

Related

How to configure Identity Server with NSwag API using IIdentityServerBuilder's .AddApiAuthorization()?

I'd like to create an authentication/authorization flow of sorts using Identity Server to have a user authorize themselves in my Swagger API so that they may access endpoints marked with the [Authorize] attribute. This is the current flow I have:
I have Swagger set up with the NSwag middleware with the OAuth2 security scheme:
services.AddMvcCore().AddApiExplorer();
services.AddOpenApiDocument(settings =>
{
settings.Title = "MyProject Services";
settings.Version = "1.0";
settings.AddSecurity("oauth2", new NSwag.OpenApiSecurityScheme
{
Type = NSwag.OpenApiSecuritySchemeType.OAuth2,
Flow = NSwag.OpenApiOAuth2Flow.AccessCode,
AuthorizationUrl = "/connect/authorize",
TokenUrl = "/connect/token",
Scopes = new Dictionary<string, string>
{
{ "MyProjectServicesAPI", "API Access" }
}
});
settings.OperationProcessors.Add(new AspNetCoreOperationSecurityScopeProcessor("oauth2"));
});
And the OAuth2 client settings in Configure():
app.UseOpenApi();
app.UseSwaggerUi3(options =>
{
options.OAuth2Client = new NSwag.AspNetCore.OAuth2ClientSettings
{
ClientId = "MyProjectAPI",
ClientSecret = "mysecret",
UsePkceWithAuthorizationCodeGrant = true
};
});
After a user selects the scope and authorizes, they get redirected to my Identity Server Login Page I scaffolded and from there they can login. Once they put in their credentials and press, 'Login', they then get redirected back to the Swagger API. So far so good. Now this is where I start to have trouble cause I would like to later add policies so a user must have a specific claim to access an endpoint, but right now, I'm not able to see any of my user's claims in the JWT Bearer token that's in the request header when I access and endpoint. The only information I get about my user is in the 'sub' which is their GUID. I'd like to be able to get their username, email, and role(s) as well.
This is what I have setup for Identity Server so far (and where I'm currently stuck):
Under ConfigureServices():
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options =>
{
options.IdentityResources = new IdentityResourceCollection
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
new IdentityResources.Email(),
new IdentityResource
{
Name = "roles",
DisplayName = "roles",
UserClaims = new List<string> { JwtClaimTypes.Role }
},
new IdentityResource
{
Name = "basicInfo",
DisplayName = "basic info",
UserClaims = new List<string> {
JwtClaimTypes.PreferredUserName
}
}
};
options.Clients = new ClientCollection
{
new Client
{
ClientId = "MyProjectAPI",
ClientName = "My Project Services API",
ClientSecrets = { new Secret("mysecret".Sha256()) },
AllowedGrantTypes = GrantTypes.Code,
AllowAccessTokensViaBrowser = true,
RedirectUris = { "https://localhost:44319/swagger/oauth2-redirect.html" },
PostLogoutRedirectUris = { "https://localhost:44319/Identity/Account/Logout" },
AllowedScopes = {
"basicInfo",
"roles",
"MyProjectServicesAPI",
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
RequirePkce = true,
RequireConsent = false
}
};
});
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddIdentityServerJwt()
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
{
ValidateIssuer = true
};
});
And then in the pipeline:
app.UseIdentityServer();
app.UseAuthentication();
app.UseAuthorization();
I recently got this error that's being thrown from Identity Server's OidcConfigurationController:
'Can't determine the type for the client 'MyProject''
I'm putting the Authorization Code type for the AllowedGrantTypes in my client so I'm not quite sure why it's throwing that error.
Do I need to be adding the claims to the Bearer token myself? If I'm including the scopes, why aren't those claims showing up? Thank you in advance for any assistance.
EDIT #1: I did resolve the error I was receiving from the OidcConfigurationController. I will add the JWT Bearer token only shows the 'MyProjectServicesAPI" scope and nothing else. However, my oidc discovery doc shows all of them?
I think I was able to partially solve my problem. So I didn't have Identity Server's Profile Service set up to grab my user's ID so it could grab the identity claims.
ProfileService.cs:
private readonly UserManager<ApplicationUser> _userManager;
public ProfileService(UserManager<ApplicationUser> userManager)
{
_userManager = userManager;
}
// Add custom claims to access token.
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
context.IssuedClaims.AddRange(context.Subject.Claims);
var user = await _userManager.GetUserAsync(context.Subject);
var roles = await _userManager.GetRolesAsync(user);
var claims = new List<Claim>
{
new Claim(JwtClaimTypes.Email, user.Email),
new Claim(JwtClaimTypes.PreferredUserName, user.UserName),
};
foreach (var claim in claims)
{
context.IssuedClaims.Add(claim);
}
foreach (var role in roles)
{
context.IssuedClaims.Add(new Claim(JwtClaimTypes.Role, role));
}
}
public async Task IsActiveAsync(IsActiveContext context)
{
var user = await _userManager.GetUserAsync(context.Subject);
context.IsActive = (user != null) && user.LockoutEnabled;
}
And then back in Startup.cs:
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options =>
{
....
})
.AddProfileService<ProfileService>();
And that's it! Order does matter as I did have AddProfileService() before my AddApiAuthorization() and that didn't work. All of my scopes still aren't showing in my JWT token, so I will need to revisit that, even though the right claims are being pulled from those Identity resources.

How to manually authorize IdentityServer4 implicit granttype with dotnetify?

I'm trying to implement .net core WebAPi + SignalR chatting server with vue.js client.
While I can find many references using SignalR and IdentityServer4, I found this useful library called dotnetify.
The doetnetify author describes how to implement securepage using ASOS http://dotnetify.net/core/api/security. But I am stuck at validating withJwtsecurityTokenHandler.
I've tried replacing authentication server part which is implemented using ASOS with IdentityServer4 and copied all the custom implementation that author provided. But when Bearer token is passed to my chatting api, exception occurs on validating token with tokenValidationParameters that I manually changed.
My project setting is as below.
IdentityServer4 with following apis and client configurations.
API configure:
return new List<ApiResource>
{
new ApiResource("api1", "API1" ),
new ApiResource("chatapi", "Chat API")
};
Client configure:
return new List<Client>
{
new Client
{
ClientId = "client",
ClientName = "JavaScript Client",
AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true,
RequireConsent = false,
RedirectUris = { "http://localhost:8080/callback" },
PostLogoutRedirectUris = { "http://localhost:8080/login" },
AllowedCorsOrigins = { "http://localhost:8080" },
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
"api1",
"chatapi"
}
},
}
Sample implementation provided by dotnetify author:
statup.cs
https://github.com/dsuryd/dotNetify/blob/master/DevApp/server/Startup.cs
AddAuthenticationServer.cs https://github.com/dsuryd/dotNetify/blob/master/DevApp/server/AuthServer.cs
I removed services.AddAuthenticationServer() and replaced with IdentityServer4's AddAuthentication("Bearer") as below.
services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(options =>
{
options.Authority = "https:/localhost:5000";
options.RequireHttpsMetadata = false;
options.ApiName = "chatapi";
});
services.AddCors(options =>
{
// this defines a CORS policy called "default"
options.AddPolicy("default", policy =>
{
policy.WithOrigins("http://localhost:8080",
Configuration["ClientAddress"])
.AllowAnyHeader()
.AllowAnyMethod();
});
});
and on Configure, I copied example and modified a tokenValidationParameters as below.
app.UseWebSockets();
app.UseSignalR(routes => routes.MapDotNetifyHub());
app.UseDotNetify(config =>
{
var tokenValidationParameters = new TokenValidationParameters {
ValidateIssuer = true,
ValidIssuer = "https://localhost:5000",
ValidAudience = "chatapi",
};
config.UseFilter<AuthorizeFilter>();
config.UseJwtBearerAuthentication(tokenValidationParameters);
config.UseMiddleware<ExtractAccessTokenMiddleware>(tokenValidationParameters);
// Demonstration filter that passes access token from the middleware to the ViewModels.SecurePageVM class instance.
config.UseFilter<SetAccessTokenFilter>();
});
The exception is as below.
Microsoft.IdentityModel.Tokens.SecurityTokenSignatureKeyNotFoundException: IDX10501: Signature validation failed. Unable to match keys:
kid: '[PII is hidden]',
token: '[PII is hidden]'.
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateSignature(String token, TokenValidationParameters validationParameters)
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateToken(String token, TokenValidationParameters validationParameters, SecurityToken& validatedToken)
at Foresting.Chat.ExamplePipelines.ExtractAccessTokenMiddleware.ValidateToken(HeaderData headers, SecurityToken& validatedToken) in C:\GitHub\ChatApi\ChatApi\ExamplePipelines\ExtractAccessTokenMiddleware.cs:line 46
at ChatApi.ExamplePipelines.ExtractAccessTokenMiddleware.Invoke(DotNetifyHubContext hubContext, NextDelegate next) in C:\GitHub\ChatApi\ChatApi\ExamplePipelines\ExtractAccessTokenMiddleware.cs:line 27
I suspect my tokenValidationParameter is not set right but no idea how to set it right and get it validated.
Tried to understand oid / oauth flow but it seems too complicated to understand and solve my problem in limited time.
Can anyone help me to where to look at to solve this kind of issue?
Thanks in advance.

IdentityServer3 returns invalid_scope for authorize endpoint

I have and IdentityServer3 installed on my ASP.NET MVC website and a client (a NopCommerce plugin) defined as below (urls are just for test):
new Client
{
ClientName = "Deep Stores",
ClientId = "deepstores",
ClientSecrets = new List<Secret>
{
new Secret("0B0A3FD3-F30C-428C-B1A3-6E570103614D".Sha256())
},
AccessTokenType = AccessTokenType.Reference,
RedirectUris = new List<string>
{
"http://localhost:2030/plugins/ExternalAuthDeepStudies/logincallback"
},
Flow = Flows.AuthorizationCode,
AllowAccessToAllScopes = true,
AllowedScopes = StandardScopes.All.Select(x => x.Name).ToList()
}
When trying to get authorization token with a url like below, It works for the first time (login appears and grant page blah blah) but for the rest of requests it returns error=invalid_scope for my callback!
request to authorize endpoint :
https://127.0.0.1/core/connect/authorize?client_id=deepstores&redirect_uri=http%3A%2F%2Flocalhost%3A2030%2Fplugins%2FExternalAuthDeepStudies%2Flogincallback&scope=profile&response_type=code
what happens? and why do i get this error?
scopes are defined as :
public static IEnumerable<Scope> Get()
{
return new[]
{
StandardScopes.OpenId,
StandardScopes.Profile,
StandardScopes.Email,
StandardScopes.Address,
StandardScopes.Phone,
StandardScopes.OfflineAccess,
StandardScopes.RolesAlwaysInclude,
StandardScopes.AllClaims,
};
}
Note that I use EntityFramework and all my staff are stored in database. plus that I use ASP.NET Identity for managing users and roles.

IdentityServer 3 not validating secret, "invalid_client" response: Implicit flow, revocation endpoint

I'm using oidc-client in my SPA application against IdentityServer3, using implicit flow.
Revoking my reference token on sign out is not working.
I have set revokeAccessTokenOnSignout: true and client_secret to a dummy secret, and I see the request being sent out. However I'm getting a "400 bad request invalid client" response.
Here's the request being sent:
And here's the response:
{"error":"invalid_client"}
And here's the IdentityServer config:
new Client
{
ClientName = "Client Name",
ClientId = "myclientid",
ClientSecrets = new List<Secret>
{
new Secret("secret".Sha256())
},
AccessTokenType = AccessTokenType.Reference,
Flow = Flows.Implicit,
RequireConsent = false,
RedirectUris = new List<string>
{
...
},
AllowedCorsOrigins = new List<string>
{
...
},
PostLogoutRedirectUris = new List<string>
{
...
},
AllowedScopes = new List<string>
{
...
} //.. allowed
}, //..new client
Any ideas what I might be doing wrong?

error:invalid_scope - IdentityServer Flow.ClientCredential

I'm having a Client in my IdentityServer3
new Client
{
ClientName = "Client Credentials Flow Client",
Enabled = true,
ClientId = "clientcredentials.reference",
Flow = Flows.ClientCredentials,
ClientSecrets = new List<Secret>
{
new Secret("secret".Sha256()),
},
AllowedScopes = new List<string>()
{
"read",
"write"
}
}
I hosted the Token Service in my local IIS and I tried to ping the Token using Postman, but it given an error {"error":"invalid_scope"}
Host URL:
https://localhost:5775/core/connect/token
Header:
Content-Type:application/x-www-form-urlencoded
Body:
grant_type=client_credentials
&cliend_id=clientcredentials.reference
&client_secret=secret
Note: I'm using pure IdentityServer3 package not Thinktecture
Check the Scopes "read" and "write" in Scopes declaration
new Scope
{
Name = "read",
DisplayName = "Read data",
Type = ScopeType.Resource,
Emphasize = false,
ScopeSecrets = new List<Secret>
{
new Secret("secret".Sha256())
}
},
new Scope
{
Name = "write",
DisplayName = "Write data",
Type = ScopeType.Resource,
Emphasize = true,
ScopeSecrets = new List<Secret>
{
new Secret("secret".Sha256())
}
}
I think its missed... Check it once...

Resources