Enable Access Control in MongoDB doesn't work - database

Everything worked fine when I used as a connection string: mongodb://0.0.0.0:27017/data. After add Enable Access Control with connection string: mongodb://user:pwd#0.0.0.0:27017/?authSource=data stops working.
I'm connected to mongodb but in application I see on request This request has no response data available. But for connection string to mongodb://0.0.0.0:27017/data there was data.
To add authentication I used this instruction Enable Access Control:
I created admin:
> use admin
switched to db admin
> db.createUser(
... {
... user: 'admin',
... pwd: 'password',
... roles: [ { role: 'root', db: 'admin' } ]
... }
... );
Successfully added user: {
"user" : "admin",
"roles" : [
{
"role" : "root",
"db" : "admin"
}
]
}
> exit;
I changed mongo config file - mongod.cfg:
security:
authorization: enabled
I logged in as admin:
> use admin
> db.auth('admin','password');
1
I created new user:
use data
db.createUser(
{
user: "user",
pwd: "pwd",
roles: [
{ role: "dbAdmin", db: "data" },
]
}
)
db.grantRolesToUser(
"user",
[ "readWrite" , { role: "read", db: "data" } ],
{ w: "majority" , wtimeout: 4000 }
)
And I used: mongodb://user:pwd#0.0.0.0:27017/?authSource=data as a connection string, but there is not working. What am I doing wrong?

I solved my problem. The reason was authSource in connection string. Before:
"connectionString": "mongodb://user:pwd#0.0.0.0:27017/?authSource=data"
After:
"connectionString": "mongodb://user:pwd#0.0.0.0:27017/data"
Now it's working fine.

Related

Added Custom Claim, showing in ID token missing in Access token

I have a .NET Core Identity Provider (which also uses IdentityServer4) which authenticates SPA applications with Azure AD. I am adding an "oid" claim with the object identifier value received from Azure. The problem is that from the SPA application I can see the "oid" claim in the ID token but cannot see it in the access token. I need the oid in the access token as well. Here is the relevant code:
Startup.cs
services.AddAuthentication()
.AddCookie("Cookies", options =>
{
options.ExpireTimeSpan = TimeSpan.FromMinutes(10);
})
.AddOpenIdConnect(ActiveDirectoryTenants.TenantA, ActiveDirectoryTenants.TenantA, options => Configuration.Bind("TenantAAzureAd", options))
.AddOpenIdConnect(ActiveDirectoryTenants.TenantB, ActiveDirectoryTenants.TenantB, options => Configuration.Bind("TenantBAzureAd", options));
AddActiveDirectoryOpenIdConnectOptions(services, ActiveDirectoryTenants.TenantA);
AddActiveDirectoryOpenIdConnectOptions(services, ActiveDirectoryTenants.TenantB);
I have a common function to add other options to these configurations. I tried to add the oid claim in OnTokenValidated but didn't receive the oid claim in the access token.
protected virtual void AddActiveDirectoryOpenIdConnectOptions(IServiceCollection services, string tenant)
{
services.Configure<OpenIdConnectOptions>(tenant, options =>
{
options.Authority = options.Authority + "/v2.0/";
options.TokenValidationParameters.ValidateIssuer = false;
options.Events = new OpenIdConnectEvents
{
OnRedirectToIdentityProvider = ctx =>
{
ctx.ProtocolMessage.LoginHint = ctx.Properties.GetString("username");
return Task.CompletedTask;
},
OnTokenValidated = ctx =>
{
//Maybe need to add oid here???
}
};
});
}
The oid claim is being added after successfully logging in to Azure AD.
AccountController.cs
public async Task<IActionResult> ExternalLoginCallback(string returnUrl, string remoteError = null, string openIdScheme = null)
{
var authResult = await HttpContext.AuthenticateAsync(openIdScheme ?? ActiveDirectoryTenants.TenantA);
var externalUser = authResult.Principal;
var claims = externalUser.Claims.ToList();
var applicationUser = //gets the user based on the email found in claims, omitted for brevity
await userManager.AddClaimAsync(applicationUser, new Claim("oid", claims.First(x => x.Type == http://schemas.microsoft.com/identity/claims/objectidentifier).Value));
await signInManager.SignInAsync(applicationUser, false, "AzureAD");
return Redirect("~/");
}
The ID token received in the SPA application (note the oid claim):
{
"nbf": xxx,
"exp": xxx,
"iss": "https://localhost:3000",
"aud": "xxx-spa-test",
"iat": xxx,
"at_hash": "",
"s_hash": "",
"sid": "",
"sub": "guid",
"auth_time": 1620026953,
"idp": "AzureAD",
"display_name": "Test User",
"oid": "guid",
"role": [
"Staff",
],
"name": "test#azureaddomain",
"amr": [
"external"
]
}
The access token received in the SPA application (note the missing oid claim):
{
"nbf": xxx,
"exp": xxx,
"iss": "https://localhost:3000",
"aud": [
"https://localhost:3000/resources",
"xxx-api-test-scope"
],
"client_id": "xxx-spa-test",
"sub": "guid",
"auth_time": 1620026953,
"idp": "AzureAD",
"role": [
"Staff",
],
"name": "test#azureaddomain",
"scope": [
"openid",
"profile",
"xxx-api-test-scope"
],
"amr": [
"external"
]
}
For the claim to end up in the access token, you need to add a ApiScope and add the Userclaim name to it. Alternatively, add an ApiScope and an ApiResource that contains the UserClaim.
Like
var apiresource1 = new ApiResource()
{
Name = "apiresource1", //This is the name of the API
ApiSecrets = new List<Secret>
{
new Secret("myapisecret".Sha256())
},
Description = "This is the order Api-resource description",
Enabled = true,
DisplayName = "Orders API Service",
Scopes = new List<string> { "apiscope1"},
UserClaims = new List<string>
{
//Custom user claims that should be provided when requesting access to this API.
//These claims will be added to the access token, not the ID-token!
"apiresource1-userclaim3",
}
};
See my answer here for more details
To complement this answer, I write a blog post that goes into more detail about this topic:
IdentityServer – IdentityResource vs. ApiResource vs. ApiScope

How do I retrieve the 'id' of an entry/row from an 'item' in the entry/row?

Building an app using React Native (for iOS) using AWS Amplify
I want to do something seemingly so simple, but i am a bit lost as to how to do it: I have a table with user information already in it. Here's the Schema:
type Users #model {
id: ID!
userName: String
firstname: String
weblink: String
email: String
mobileNum: String
.
.
.
}
//**Here's my current Query.js**
export const getUsers = `query GetUsers($id: ID!) {
getUsers(id: $id) {
id
userName
firstname
weblink
email
.
.
.
}
}
`;
This table is populated in DynamoDB when i check my AWS console. What i need is to be able to get the id from the table using the userName (not vice versa). The id is generated when i createUser() and it's used throughout my app to get all my user's information. However when a user signs in on a new phone, this id isn't available anymore. So when they sign in via Cognito, i do know the userName and all i need to do is retrieve this id. Because there's only one unique userName, it should only return one id
Here's what i'm thinking so far: use a GSI (global secondary index). So change my schema to:
type Users #model
#key(
name: "ByUsername"
fields: ["userName"]
queryField: "getIdFromUserName"
)
{
id: ID!
userName: String
firstname: String
weblink: String
email: String
mobileNum: String
.
.
.
}
Then call in my app:
const data = await API.graphql(graphqlOperation(getIdFromUserName, { userName }));
5 questions:
1) Is there a simpler way than GSI?
2) Is that how you add the GSI? Or is it more robust to do it in the AWS console?
3) What should my Query.js then look like?
4) Do i need to make a custom resolver, or is this sufficient?
5) Am i missing anything else, or can i just
amplify push ?
//11/04/2020
//Resolver
## [Start] Prepare DynamoDB PutItem Request. **
$util.qr($context.args.input.put("createdAt", $util.defaultIfNull($ctx.args.input.createdAt, $util.time.nowISO8601())))
$util.qr($context.args.input.put("updatedAt", $util.defaultIfNull($ctx.args.input.updatedAt, $util.time.nowISO8601())))
$util.qr($context.args.input.put("__typename", "Users"))
#set( $condition = {
"expression": "attribute_not_exists(#id)",
"expressionNames": {
"#id": "id"
}
} )
#if( $context.args.condition )
#set( $condition.expressionValues = {} )
#set( $conditionFilterExpressions = $util.parseJson($util.transform.toDynamoDBConditionExpression($context.args.condition)) )
$util.qr($condition.put("expression", "($condition.expression) AND $conditionFilterExpressions.expression"))
$util.qr($condition.expressionNames.putAll($conditionFilterExpressions.expressionNames))
$util.qr($condition.expressionValues.putAll($conditionFilterExpressions.expressionValues))
#end
#if( $condition.expressionValues && $condition.expressionValues.size() == 0 )
#set( $condition = {
"expression": $condition.expression,
"expressionNames": $condition.expressionNames
} )
#end
{
"version": "2017-02-28",
"operation": "PutItem",
"key": #if( $modelObjectKey ) $util.toJson($modelObjectKey) #else {
"id": $util.dynamodb.toDynamoDBJson($util.defaultIfNullOrBlank($ctx.args.input.id, $util.autoId()))
} #end,
"attributeValues": $util.dynamodb.toMapValuesJson($context.args.input),
"condition": $util.toJson($condition)
}
## [End] Prepare DynamoDB PutItem Request. **
1) You dont need to create GSI (global secondary index).
2) You can update your createUser resolver, instead of using $util.autoId() you can pass $ctx.args.input.userName as id

How to correctly protect API in STS Asp.Net 2.2 project + Api Asp.Net 2.2 project + angular7 client

Have been struggling with this for a while, no problem having angular7 client access id4 Asp.Net core 2.2 project for logging in and getting jwt back, struggling with angular7 client accessing Asp.Net core 2.2 api project protected api which is under id4 protection.
jwt.io decode (values x'd out):
HEADER:ALGORITHM & TOKEN TYPE
{
"alg": "RS256",
"kid": "c672fc19f3ff652c5c8816cfac31bfcc",
"typ": "JWT"
}
PAYLOAD:DATA
{
"nbf": 1550161736,
"exp": 1550164736,
"iss": "https://localhost:44340",
"aud": "angularclient",
"nonce": "N0.88924643059608991550161727071",
"iat": 1550161736,
"at_hash": "A3fYyAynZIUQN5Z3ugvpvw",
"sid": "90c459301964e9f136a38b9b19d9b1e0",
"sub": "71765055-647D-432E-AFB6-0F84218D0247",
"auth_time": 1550161731,
"idp": "local",
"preferred_username": "xxxxxxxx",
"name": "xxxxxxxxxx",
"regid": "xxxxxxxxx",
"jseg": "xxxxx",
"jobid": "xxxxx",
"role": "xxxx",
"given_name": "xxxx",
"family_name": "Grexxxxenwald",
"email": "xxxxx",
"amr": [
"pwd"
]
}
ID4 config:
private static readonly string[] customClaimTypes = { "role", "jseg", "jobid", "regid", "api1" };
public static IEnumerable<IdentityResource> GetIdentityResources()
{
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
new IdentityResources.Email(),
new IdentityResource("api1scope", customClaimTypes),
};
}
public static IEnumerable<ApiResource> GetApis()
{
return new ApiResource[]
{
new ApiResource()
{
Name = "api1",
Description = "tsicApis",
ApiSecrets =
{
new Secret(Startup.Configuration.GetSection("StsConfig:STSTSICApisSecuredSecret").Value.Sha256())
},
Scopes =
{
new Scope()
{
Name = "api1",
DisplayName = "Scope for the api1 ApiResource",
},
},
UserClaims = customClaimTypes
}
};
}
// clients want to access resources (aka scopes)
public static IEnumerable<Client> GetClients()
{
var trustedClientSecrets = Startup.Configuration.GetSection("StsConfig:TrustedClientSecrets").Value;
var angularClientUrl = Startup.Configuration.GetSection("StsConfig:AngularClientUrl").Value;
var angularRedirectUris = Startup.Configuration.GetSection("StsConfig:AngularRedirectUris").Value;
var angularPostLogoutRedirectUris = Startup.Configuration.GetSection("StsConfig:AngularPostLogoutRedirectUris").Value;
var angularAllowedCorsOrigins = Startup.Configuration.GetSection("StsConfig:AngularAllowedCorsOrigins").Value;
var angularClientSecret = Startup.Configuration.GetSection("StsConfig:STSTSICApisSecuredSecret").Value;
var mvcClientSecrets = Startup.Configuration.GetSection("StsConfig:MVCClientSecrets").Value;
var mvcRedirectUris = Startup.Configuration.GetSection("StsConfig:MVCRedirectUris").Value;
var mvcFrontChannelLogoutUri = Startup.Configuration.GetSection("StsConfig:MVCFrontChannelLogoutUri").Value;
var mvcPostLogoutRedirectUris = Startup.Configuration.GetSection("StsConfig:MVCPostLogoutRedirectUris").Value;
// client credentials client
return new List<Client>
{
new Client
{
ClientName = "angularclient",
ClientId = "angularclient",
RequireClientSecret = true,
ClientSecrets = { new Secret(angularClientSecret) },
RequireConsent = true,
AllowRememberConsent = false,
AccessTokenType = AccessTokenType.Jwt,
AlwaysIncludeUserClaimsInIdToken = true,
AccessTokenLifetime = 33000,// 330 seconds, default 60 minutes
IdentityTokenLifetime = 3000,
AllowAccessTokensViaBrowser = true,
AllowedGrantTypes = GrantTypes.Implicit,
AllowedCorsOrigins = angularAllowedCorsOrigins.Split(','),
AllowedScopes =
{
"openid",
"profile",
"email",
"role",
"jseg",
"jobid",
"regid",
"api1",
"api1scope",
},
RedirectUris = angularRedirectUris.Split(','),
PostLogoutRedirectUris = angularPostLogoutRedirectUris.Split(',')
},
new Client
{
ClientId = "mvcclient",
ClientName = "mvcclient",
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
ClientSecrets = { new Secret(mvcClientSecrets.Sha256()) },
RequireConsent = true,
AllowRememberConsent = false,
RedirectUris = mvcRedirectUris.Split(','),
FrontChannelLogoutUri = mvcFrontChannelLogoutUri,
PostLogoutRedirectUris = mvcPostLogoutRedirectUris.Split(','),
AllowOfflineAccess = true,
AllowedScopes = new List<string>
{
"openid",
"profile",
"api1"
}
},
};
}
Angular app.module.ts:
export class AppModule {
constructor(
private oidcSecurityService: OidcSecurityService
) {
const openIDImplicitFlowConfiguration = new OpenIDImplicitFlowConfiguration();
openIDImplicitFlowConfiguration.storage = sessionStorage;
openIDImplicitFlowConfiguration.stsServer = environment.oidc.stsServer;
openIDImplicitFlowConfiguration.redirect_url = environment.oidc.redirect_url;
// The Client MUST validate that the aud (audience) Claim contains its client_id value registered at the Issuer
// identified by the iss (issuer) Claim as an audience.
// The ID Token MUST be rejected if the ID Token does not list the Client as a valid audience,
// or if it contains additional audiences not trusted by the Client.
openIDImplicitFlowConfiguration.client_id = 'angularclient';
openIDImplicitFlowConfiguration.response_type = 'id_token token';
openIDImplicitFlowConfiguration.scope = 'openid profile email api1scope';
openIDImplicitFlowConfiguration.post_logout_redirect_uri = environment.oidc.post_logout_redirect_uri;
// openIDImplicitFlowConfiguration.start_checksession = this.oidcConfigService.clientConfiguration.start_checksession;
openIDImplicitFlowConfiguration.silent_renew = true;
openIDImplicitFlowConfiguration.silent_renew_url = environment.oidc.silent_renew_url;
openIDImplicitFlowConfiguration.post_login_route = environment.oidc.post_login_route;
// HTTP 403
openIDImplicitFlowConfiguration.forbidden_route = '/forbidden';
// HTTP 401
openIDImplicitFlowConfiguration.unauthorized_route = '/unauthorized';
openIDImplicitFlowConfiguration.log_console_warning_active = environment.oidc.log_console_warning_active;
openIDImplicitFlowConfiguration.log_console_debug_active = environment.oidc.log_console_debug_active;
// id_token C8: The iat Claim can be used to reject tokens that were issued too far away from the current time,
// limiting the amount of time that nonces need to be stored to prevent attacks.The acceptable range is Client specific.
openIDImplicitFlowConfiguration.max_id_token_iat_offset_allowed_in_seconds = environment.oidc.max_id_token_iat_offset_allowed_in_seconds;
// openIDImplicitFlowConfiguration.iss_validation_off = false;
// configuration.FileServer = this.oidcConfigService.clientConfiguration.apiFileServer;
// configuration.Server = this.oidcConfigService.clientConfiguration.apiServer;
const authWellKnownEndpoints = new AuthWellKnownEndpoints();
authWellKnownEndpoints.issuer = environment.oidc.stsServer;
authWellKnownEndpoints.jwks_uri = `${environment.oidc.stsServer}/.well-known/openid-configuration/jwks`;
authWellKnownEndpoints.authorization_endpoint = `${environment.oidc.stsServer}/connect/authorize`;
authWellKnownEndpoints.token_endpoint = `${environment.oidc.stsServer}/connect/token`;
authWellKnownEndpoints.userinfo_endpoint = `${environment.oidc.stsServer}/connect/userinfo`;
authWellKnownEndpoints.end_session_endpoint = `${environment.oidc.stsServer}/connect/endsession`;
authWellKnownEndpoints.check_session_iframe = `${environment.oidc.stsServer}/connect/checksession`;
authWellKnownEndpoints.revocation_endpoint = `${environment.oidc.stsServer}/connect/revocation`;
authWellKnownEndpoints.introspection_endpoint = `${environment.oidc.stsServer}/connect/introspect`;
this.oidcSecurityService.setupModule(
openIDImplicitFlowConfiguration,
authWellKnownEndpoints
);
}
}
Asp.Net core 2.2 api project start.cs:
services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(options =>
{
options.Authority = Configuration.GetValue<string>("IdentityServer4Strings:Authority");
options.RequireHttpsMetadata = Configuration.GetValue<bool>("IdentityServer4Strings:RequireHttpsMetadata");
options.ApiName = Configuration.GetValue<string>("IdentityServer4Strings:ApiName");
options.SupportedTokens = IdentityServer4.AccessTokenValidation.SupportedTokens.Jwt;
options.ApiSecret = Configuration.GetValue<string>("IdentityServer4Strings:STSTSICApisSecuredSecret");
options.EnableCaching = true;
options.CacheDuration = TimeSpan.FromMinutes(10); // that's the default
});
Asp.Net core 2.2 sts project start.cs:
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
var identityServer = services.AddIdentityServer(options =>
{
options.Events.RaiseErrorEvents = true;
options.Events.RaiseInformationEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseSuccessEvents = true;
})
.AddProfileService<IdentityWithAdditionalClaimsProfileService>()
//.AddTestUsers(TestUsers.Users)
// this adds the config data from DB (clients, resources, CORS)
.AddConfigurationStore(options =>
{
options.ConfigureDbContext = builder => builder.UseSqlServer(connectionString);
})
// this adds the operational data from DB (codes, tokens, consents)
.AddOperationalStore(options =>
{
options.ConfigureDbContext = builder => builder.UseSqlServer(connectionString);
// this enables automatic token cleanup. this is optional.
options.EnableTokenCleanup = true;
})
.AddProfileService<IdentityWithAdditionalClaimsProfileService>()
.AddAspNetIdentity<ApplicationUser>();
services.AddTransient<IProfileService, IdentityWithAdditionalClaimsProfileService>();
Api authorize protection decorator (have tried both):
`
//[Authorize]
[Authorize(AuthenticationSchemes = "Bearer")]
`
Asp.Net core 2.2 sts start.cs:
`
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
var identityServer = services.AddIdentityServer(options =>
{
options.Events.RaiseErrorEvents = true;
options.Events.RaiseInformationEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseSuccessEvents = true;
})
.AddProfileService<IdentityWithAdditionalClaimsProfileService>()
//.AddTestUsers(TestUsers.Users)
// this adds the config data from DB (clients, resources, CORS)
.AddConfigurationStore(options =>
{
options.ConfigureDbContext = builder => builder.UseSqlServer(connectionString);
})
// this adds the operational data from DB (codes, tokens, consents)
.AddOperationalStore(options =>
{
options.ConfigureDbContext = builder => builder.UseSqlServer(connectionString);
// this enables automatic token cleanup. this is optional.
options.EnableTokenCleanup = true;
})
.AddProfileService<IdentityWithAdditionalClaimsProfileService>()
.AddAspNetIdentity<ApplicationUser>();
services.AddTransient<IProfileService, IdentityWithAdditionalClaimsProfileService>();
Asp.Net core 2.2 sts project IdentityWithAdditionalClaims handler:
public IdentityWithAdditionalClaimsProfileService(UserManager<ApplicationUser> userManager, IUserClaimsPrincipalFactory<ApplicationUser> claimsFactory, SqlDbContext Sql)
{
_userManager = userManager;
_claimsFactory = claimsFactory;
_context = Sql;
}
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
var sub = context.Subject.GetSubjectId();
var user = await _userManager.FindByIdAsync(sub);
var principal = await _claimsFactory.CreateAsync(user);
var claims = principal.Claims.ToList();
var tsicCustomClaims = await GetTSICCustomClaims(claims);
claims = claims.Where(claim => context.RequestedClaimTypes.Contains(claim.Type)).ToList();
claims.Add(new Claim(JwtClaimTypes.Scope, "api1"));
claims.Add(new Claim("regid", tsicCustomClaims.RegId.ToString()));
claims.Add(new Claim("jseg", tsicCustomClaims.JobPath));
claims.Add(new Claim("jobid", tsicCustomClaims.JobId.ToString()));
claims.Add(new Claim(JwtClaimTypes.Role, tsicCustomClaims.RoleName));
claims.Add(new Claim(JwtClaimTypes.GivenName, tsicCustomClaims.FirstName));
claims.Add(new Claim(JwtClaimTypes.FamilyName, tsicCustomClaims.LastName));
claims.Add(new Claim(IdentityServerConstants.StandardScopes.Email, tsicCustomClaims.EMail));
context.IssuedClaims = claims;
}
Errors:
When accessing the protected api I get from Asp.Net core 2.2 api project:
[09:29:03 Information] Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler
Failed to validate the token.
Microsoft.IdentityModel.Tokens.SecurityTokenInvalidAudienceException: IDX10214: Audience validation failed. Audiences: 'https://localhost:44340/resources'. Did not match: validationParameters.ValidAudience: 'api1' or validationParameters.ValidAudiences: 'null'.
at Microsoft.IdentityModel.Tokens.Validators.ValidateAudience(IEnumerable`1 audiences, SecurityToken securityToken, TokenValidationParameters validationParameters)
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateAudience(IEnumerable`1 audiences, JwtSecurityToken jwtToken, TokenValidationParameters validationParameters)
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateTokenPayload(JwtSecurityToken jwtToken, TokenValidationParameters validationParameters)
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateToken(String token, TokenValidationParameters validationParameters, SecurityToken& validatedToken)
at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.HandleAuthenticateAsync()
[09:29:03 Information] Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler
BearerIdentityServerAuthenticationJwt was not authenticated. Failure message: IDX10214: Audience validation failed. Audiences: 'https://localhost:44340/resources'. Did not match: validationParameters.ValidAudience: 'api1' or validationParameters.ValidAudiences: 'null'.
[09:29:03 Information] IdentityServer4.AccessTokenValidation.IdentityServerAuthenticationHandler
Bearer was not authenticated. Failure message: IDX10214: Audience validation failed. Audiences: 'https://localhost:44340/resources'. Did not match: validationParameters.ValidAudience: 'api1' or validationParameters.ValidAudiences: 'null'.
On startup of the Asp.Net Core 2.2 sts project I get:
Seeding database...
Clients already populated
IdentityResources already populated
ApiResources already populated
Done seeding database.
[09:28:09 Information] IdentityServer4.Startup
Starting IdentityServer4 version 2.3.2.0
[09:28:09 Information] IdentityServer4.Startup
Using the default authentication scheme Identity.Application for IdentityServer
[09:28:09 Debug] IdentityServer4.Startup
Using Identity.Application as default ASP.NET Core scheme for authentication
[09:28:09 Debug] IdentityServer4.Startup
Using Identity.External as default ASP.NET Core scheme for sign-in
[09:28:09 Debug] IdentityServer4.Startup
Using Identity.External as default ASP.NET Core scheme for sign-out
[09:28:09 Debug] IdentityServer4.Startup
Using Identity.Application as default ASP.NET Core scheme for challenge
[09:28:09 Debug] IdentityServer4.Startup
Using Identity.Application as default ASP.NET Core scheme for forbid
[09:28:10 Debug] IdentityServer4.EntityFramework.TokenCleanup
Starting grant removal
Hosting environment: Development
Content root path: E:\Projects-STS\TSIC\TSIC.STS
Now listening on: https://localhost:44340
Application started. Press Ctrl+C to shut down.
[09:28:13 Debug] IdentityServer4.Startup
Login Url: /Account/Login
[09:28:13 Debug] IdentityServer4.Startup
Login Return Url Parameter: ReturnUrl
[09:28:13 Debug] IdentityServer4.Startup
Logout Url: /Account/Logout
[09:28:13 Debug] IdentityServer4.Startup
ConsentUrl Url: /consent
[09:28:13 Debug] IdentityServer4.Startup
Consent Return Url Parameter: returnUrl
[09:28:13 Debug] IdentityServer4.Startup
Error Url: /home/error
[09:28:13 Debug] IdentityServer4.Startup
Error Id Parameter: errorId
[09:28:25 Debug] IdentityServer4.Hosting.EndpointRouter
Request path /connect/authorize matched to endpoint type Authorize
[09:28:25 Debug] IdentityServer4.Hosting.EndpointRouter
Endpoint enabled: Authorize, successfully created handler: IdentityServer4.Endpoints.AuthorizeEndpoint
[09:28:25 Information] IdentityServer4.Hosting.IdentityServerMiddleware
Invoking IdentityServer endpoint: IdentityServer4.Endpoints.AuthorizeEndpoint for /connect/authorize
[09:28:25 Debug] IdentityServer4.Endpoints.AuthorizeEndpoint
Start authorize request
[09:28:25 Debug] IdentityServer4.Endpoints.AuthorizeEndpoint
No user present in authorize request
[09:28:25 Debug] IdentityServer4.Validation.AuthorizeRequestValidator
Start authorize request protocol validation
[09:28:26 Debug] IdentityServer4.EntityFramework.Stores.ClientStore
angularclient found in database: True
[09:28:26 Debug] IdentityServer4.Stores.ValidatingClientStore
client configuration validation for client angularclient succeeded.
[09:28:27 Debug] IdentityServer4.EntityFramework.Stores.ResourceStore
Found ["openid", "profile", "email", "api1scope"] identity scopes in database
[09:28:27 Debug] IdentityServer4.EntityFramework.Stores.ResourceStore
Found [] API scopes in database
[09:28:27 Debug] IdentityServer4.EntityFramework.Stores.ResourceStore
Found ["openid", "profile", "email", "api1scope"] identity scopes in database
[09:28:27 Debug] IdentityServer4.EntityFramework.Stores.ResourceStore
Found [] API scopes in database
I think I'm close here and just need a push in the right direction.
I'm currently focused on the line:
[09:28:27 Debug] IdentityServer4.EntityFramework.Stores.ResourceStore
Found [] API scopes in database
Thinking this is related to the api project error:
IDX10214: Audience validation failed. Audiences: 'https://localhost:44340/resources'. Did not match: validationParameters.ValidAudience: 'api1' or validationParameters.ValidAudiences: 'null'.
This bugs me because the database DOES have an entry in dbo.ApiScopes, from SQL Server:
Id Name DisplayName Description Required Emphasize ShowInDiscoveryDocument ApiResourceId
9 api1 Scope for the api1 ApiResource NULL 0 0 1 12
I'm grateful for any assistance
In your angular config you have:
openIDImplicitFlowConfiguration.scope = 'openid profile email api1scope';
However, it should match one of your valid scopes for the api1:
openIDImplicitFlowConfiguration.scope = 'openid profile email api1';
Identity Server 4 only adds Api resource as a valid token audience if you request at least one of the scopes that belong to a given api and if your client is allowed that scope.
Vidmantas, thanks for your response, I tried that and same error occurred. I then got lucky and solved (was a problem with the different audiences indicated in the id token vs the access token, and how the id token aud: could be set by the IdentityServer4 configuration in the Api project startup.cs):
Got it, hope this helps others:
The angular client after logging in is returned:
id token:
{
"nbf": 1550240640,
"exp": 1550273640,
"iss": "https://localhost:44340",
"aud": "https://localhost:44340/resources",
"client_id": "angularclient",
"sub": "71765055-647D-432E-AFB6-0F84218D0247",
"auth_time": 1550240638,
"idp": "local",
"regid": "xxxx",
"jseg": "xxxxx",
"jobid": "b0984a87-172a-436e-a382-e95de3e1059f",
"role": "xxxx",
"given_name": "xxxxx",
"family_name": "xxxx",
"email": "xxxx",
"scope": [
"openid",
"profile",
"email"
],
"amr": [
"pwd"
]
}
and access token:
{
"nbf": 1550240640,
"exp": 1550243640,
"iss": "https://localhost:44340",
"aud": "angularclient",
"nonce": "N0.55036966062308791550240634889",
"iat": 1550240640,
"at_hash": "yNVxDVHkmEmUvurl7XlzuA",
"sid": "f54dee03793e7cc202b57f1d6de7622e",
"sub": "71765055-647D-432E-AFB6-0F84218D0247",
"auth_time": 1550240638,
"idp": "local",
"preferred_username": "TSICSuperUser",
"name": "xxxx",
"email": "xxxxx",
"email_verified": true,
"regid": "xxxxx",
"jseg": "xxxxx",
"jobid": "xxxxxxf",
"role": "xxxxx",
"given_name": "xxxx",
"family_name": "xxxxx",
"amr": [
"pwd"
]
}
NOTE THE DIFFERENT AUDIENCES (aud:)
The Asp.Net Core 2.2 Api Project startup.cs configured IdentityServer4:
.AddIdentityServerAuthentication(options =>
{
options.Authority = Configuration.GetValue<string>("IdentityServer4Strings:Authority");
options.RequireHttpsMetadata = Configuration.GetValue<bool>("IdentityServer4Strings:RequireHttpsMetadata");
options.ApiName = "api1";
options.SupportedTokens = IdentityServer4.AccessTokenValidation.SupportedTokens.Jwt;
options.ApiSecret = Configuration.GetValue<string>("IdentityServer4Strings:STSTSICApisSecuredSecret");
options.EnableCaching = true;
options.CacheDuration = TimeSpan.FromMinutes(10); // that's the default
});
and the Bearer authentication error was:
2019-02-14 18:03:15.188 -07:00 [DBG] AuthenticationScheme: Bearer was not authenticated.
2019-02-14 18:04:04.360 -07:00 [INF] Failed to validate the token.
Microsoft.IdentityModel.Tokens.SecurityTokenInvalidAudienceException: IDX10214: Audience validation failed. Audiences: 'https://localhost:44340/resources'. Did not match: validationParameters.ValidAudience: 'api1' or validationParameters.ValidAudiences: 'null'.
at Microsoft.IdentityModel.Tokens.Validators.ValidateAudience(IEnumerable`1 audiences, SecurityToken securityToken, TokenValidationParameters validationParameters)
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateAudience(IEnumerable`1 audiences, JwtSecurityToken jwtToken, TokenValidationParameters validationParameters)
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateTokenPayload(JwtSecurityToken jwtToken, TokenValidationParameters validationParameters)
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateToken(String token, TokenValidationParameters validationParameters, SecurityToken& validatedToken)
at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.HandleAuthenticateAsync()
2019-02-14 18:04:04.430 -07:00 [INF] BearerIdentityServerAuthenticationJwt was not authenticated. Failure message: IDX10214: Audience validation failed. Audiences: 'https://localhost:44340/resources'. Did not match: validationParameters.ValidAudience: 'api1' or validationParameters.ValidAudiences: 'null'.
2019-02-14 18:04:04.433 -07:00 [INF] Bearer was not authenticated. Failure message: IDX10214: Audience validation failed. Audiences: 'https://localhost:44340/resources'. Did not match: validationParameters.ValidAudience: 'api1' or validationParameters.ValidAudiences: 'null'.
The "api1" in error:
Did not match: validationParameters.ValidAudience: 'api1'
references startup.cs
.AddIdentityServerAuthentication
options.ApiName = "api1";
Changing the Asp.Net Core 2.2 Api project startup.cs to:
// critical for bearer authentication, the audience of the id token (set by Options.ApiName) is equal to this value
var idTokenAudience = $"{Configuration.GetValue<string>("IdentityServer4Strings:Authority")}/resources";
services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(options =>
{
options.Authority = Configuration.GetValue<string>("IdentityServer4Strings:Authority");
options.RequireHttpsMetadata = Configuration.GetValue<bool>("IdentityServer4Strings:RequireHttpsMetadata");
options.ApiName = idTokenAudience;
options.SupportedTokens = IdentityServer4.AccessTokenValidation.SupportedTokens.Jwt;
options.ApiSecret = Configuration.GetValue<string>("IdentityServer4Strings:STSTSICApisSecuredSecret");
options.EnableCaching = true;
options.CacheDuration = TimeSpan.FromMinutes(10); // that's the default
});
resolved the issue.
2019-02-15 07:45:12.414 -07:00 [INF] Successfully validated the token.
2019-02-15 07:45:12.414 -07:00 [DBG] AuthenticationScheme: Bearer was successfully authenticated.

How to get user group information from Azure AD B2C

I am using Microsoft Graph for fetching user information, namely "List users" API.
Following is the code for accessing the user information :
ClientCredential clientCred = new ClientCredential(clientId, clientSecret);
AuthenticationResult authenticationResult = await authenticationContext.AcquireTokenAsync(resourceId, clientCred);
string token = authenticationResult.AccessToken;
Debug.WriteLine("token=" + token);
var responseString = String.Empty;
string[] scopes = { "User.Read" };
using (var client = new HttpClient())
{
string requestUrl = "https://graph.microsoft.com/v1.0/users?$select=id,givenName,surname";
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUrl);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
Debug.WriteLine(request.ToString());
HttpResponseMessage response = client.SendAsync(request).Result;
responseString = response.Content.ReadAsStringAsync().Result;
Debug.WriteLine(responseString);
}
Output :
{
"#odata.context": "https://graph.microsoft.com/v1.0/$metadata#users(id,givenName,surname)",
"value": [
{
"id": "000000000-0000-0000-000-000000000",
"givenName": "XXX",
"surname": "XXX"
}, {
"id": "000000000-0000-0000-000-000000000",
"givenName": "XXX",
"surname": "XXX"
}
]
}
How to the get user group ?
You can call the user: getMemberGroups API action for a user to get their groups:
You need to make a request like so:
POST https://graph.microsoft.com/v1.0/users/user-id-here/getMemberGroups
Content-type: application/json
Content-length: 33
{
"securityEnabledOnly": true
}
The securityEnabledOnly parameter defines if it should only return security groups. Setting it to false will also return the user's Office 365 group memberships for example.
An alternative is to use the memberOf navigation property:
GET https://graph.microsoft.com/v1.0/users/user-id-here/memberOf
This returns the groups and directory roles the user is a direct member of.

Jmxtrans monitors solr issue

I want to monitor solr, and have the jmxtrans config:
{
"servers":[
{
"port":"8099",
"host":"localhost",
"queries":[
{
"obj":"solr/*:type=/select,id=org.apache.solr.handler.component.SearchHandler",
"resultAlias":"solr",
"attr":[
"requests","errors","avgRequestsPerSecond","avgTimePerRequest","95thPcRequestTime"
],
"outputWriters":[
{
"#class":"com.googlecode.jmxtrans.model.output.KeyOutWriter",
"settings":{
"outputFile" : "/tmp/jmx.log",
"maxLogFileSize" : "10MB",
"maxLogBackupFiles" : 2,
"debug" : true
}
}
]
}
]
}
]
}
I have configure the wildcard domain name
"obj":"solr/*:type=/select,id=org.apache.solr.handler.component.SearchHandler"
but I get the following result without domain name:
localhost_8099.solr.errors 0 1446715240625
localhost_8099.solr.avgRequestsPerSecond 0.00883917964270778 1446715240625
localhost_8099.solr.avgTimePerRequest 1.99831994970047 1446715240625
localhost_8099.solr.95thPcRequestTime 3.8249146499999997 1446715240625
localhost_8099.solr.requests 717419 1446715241205
localhost_8099.solr.errors 0 1446715241205
I tried typeNames: https://code.google.com/p/jmxtrans/wiki/Queries
But it seems doesn't support domain.
I found the answer, add the following configuration:
...
"obj":"solr/*:type=/select,id=org.apache.solr.handler.component.SearchHandler",
"useObjDomainAsKey":true,
...

Resources