asp.net identity failing on login-callback - reactjs

I have my API, react spa app in one project, and am integrating asp.net identity into the same project. I have scaffolded it and everything works when I go to the identity pages. If I go to Identity/Account/Login I can register and login to a user just fine. However, when I attempt to log in from one of my pages, I get the following error after the login-callback when accessing https://localhost:44393/connect/token
System.NotSupportedException: IDX10634: Unable to create the SignatureProvider.
Algorithm: 'System.String', SecurityKey: 'Microsoft.IdentityModel.Tokens.X509SecurityKey'
is not supported. The list of supported algorithms is available here: https://aka.ms/IdentityModel/supported-algorithms
at Microsoft.IdentityModel.Tokens.CryptoProviderFactory.CreateSignatureProvider(SecurityKey key, String algorithm, Boolean willCreateSignatures, Boolean cacheProvider)
at Microsoft.IdentityModel.Tokens.CryptoProviderFactory.CreateForSigning(SecurityKey key, String algorithm, Boolean cacheProvider)
at Microsoft.IdentityModel.Tokens.CryptoProviderFactory.CreateForSigning(SecurityKey key, String algorithm)
at Microsoft.IdentityModel.JsonWebTokens.JwtTokenUtilities.CreateEncodedSignature(String input, SigningCredentials signingCredentials)
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.WriteToken(SecurityToken token)
at IdentityServer4.Services.DefaultTokenCreationService.CreateJwtAsync(JwtSecurityToken jwt)
at IdentityServer4.Services.DefaultTokenCreationService.CreateTokenAsync(Token token)
at IdentityServer4.Services.DefaultTokenService.CreateSecurityTokenAsync(Token token)
at IdentityServer4.ResponseHandling.TokenResponseGenerator.CreateAccessTokenAsync(ValidatedTokenRequest request)
at IdentityServer4.ResponseHandling.TokenResponseGenerator.ProcessAuthorizationCodeRequestAsync(TokenRequestValidationResult request)
at IdentityServer4.ResponseHandling.TokenResponseGenerator.ProcessAsync(TokenRequestValidationResult request)
at IdentityServer4.Endpoints.TokenEndpoint.ProcessTokenRequestAsync(HttpContext context)
at IdentityServer4.Endpoints.TokenEndpoint.ProcessAsync(HttpContext context)
at IdentityServer4.Hosting.IdentityServerMiddleware.Invoke(HttpContext context, IEndpointRouter router, IUserSession session, IEventService events, IBackChannelLogoutService backChannelLogoutService)
at IdentityServer4.Hosting.IdentityServerMiddleware.Invoke(HttpContext context, IEndpointRouter router, IUserSession session, IEventService events, IBackChannelLogoutService backChannelLogoutService)
at IdentityServer4.Hosting.MutualTlsEndpointMiddleware.Invoke(HttpContext context, IAuthenticationSchemeProvider schemes)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at IdentityServer4.Hosting.BaseUrlMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
I have wrapped my route in the default AuthorizeRoute componet like this :
<AuthorizeRoute path="/Settings" render={(props) => (<SettingsMenu {...props} cloud={this.state.cloud} evaluation={this.state.evaluation}/>)} />
and otherwise have not changed the code from the identity react pages.
I assume I am missing something in my configuration. But I built the sample identityreact project and that seems to work fine. Any pointers would be appreciated.

In the log it says
Algorithm: 'System.String',
That looks like a bug, what algorithm do you try to use?
The algorithm should be one of the one listed here
To create a signing key, you typically use the openssl command line tool to generate the signing key and also wrap it into a .pkcs12/.pfx file. If you use Azure Key Vault, it can generate it for you as well. Adding and importing the key into IdentityServer can be tricky.
I typically use this code to import the key into .NET:
private static SecurityKey LoadEcdsaKey()
{
var ecdsaCert = new X509Certificate2("es256.pfx", "mypassword");
SecurityKey ecdsaPrivateKey = new ECDsaSecurityKey(ecdsaCert.GetECDsaPrivateKey());
return ecdsaPrivateKey;
}
See this article on how to use openssl:

Related

Identity Server Confusion

I am having issues with IdentityServer and I am learning it so there is a good chance I have something wrong. Basically I can run the App (.net core 5 app with identity server 4)
Below is my ConfigureServices method:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlite(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultUI()
.AddDefaultTokenProviders();
services.AddDbContext<BMProAppContext>();
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>()
.AddInMemoryApiScopes(deviceclients.GetApiScopes())
.AddInMemoryClients(deviceclients.GetClients());
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddIdentityServerJwt();
}
Here is my Configure method:
app.UseRouting();
app.UseAuthentication();
app.UseIdentityServer();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
endpoints.MapRazorPages();
});
When the app runs it issues a token successfully (I am using client credential flow)- then it throws an error as per below:
Token request success.
fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1]
An unhandled exception has occurred while executing the request.
System.InvalidOperationException: No authentication handler is registered for the scheme 'Bearer'. The registered schemes are: Identity.Application, Identity.External, Identity.TwoFactorRememberMe, Identity.TwoFactorUserId, idsrv, idsrv.external, IdentityServerJwt, IdentityServerJwtBearer. Did you forget to call AddAuthentication().AddSomeAuthHandler?
at Microsoft.AspNetCore.Authentication.AuthenticationService.AuthenticateAsync(HttpContext context, String scheme)
at Microsoft.AspNetCore.Authorization.Policy.PolicyEvaluator.AuthenticateAsync(AuthorizationPolicy policy, HttpContext context)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at IdentityServer4.Hosting.IdentityServerMiddleware.Invoke(HttpContext context, IEndpointRouter router, IUserSession session, IEventService events, IBackChannelLogoutService backChannelLogoutService)
at IdentityServer4.Hosting.MutualTlsEndpointMiddleware.Invoke(HttpContext context, IAuthenticationSchemeProvider schemes)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at IdentityServer4.Hosting.BaseUrlMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at NSwag.AspNetCore.Middlewares.SwaggerUiIndexMiddleware.Invoke(HttpContext context)
at NSwag.AspNetCore.Middlewares.RedirectToIndexMiddleware.Invoke(HttpContext context)
at NSwag.AspNetCore.Middlewares.OpenApiDocumentMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Builder.Extensions.MapMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
Not sure what is wrong and have searched for answer but nothing seems to help :(
Any advice appreciated.
You are declaring a "Bearer" scheme as the default authentication scheme, but you are forgetting the handler for that scheme.
If you use the defaultScheme parameter when you call AddAuthentication, you are setting the default scheme name and the application will try to use it when neccessary.
You need a registered handler with the same name to handle the request. Handlers also accept a scheme name when you register them. If you omit this parameter, an internal default value of each handler will be used, for example, jwtBearer internally uses "Bearer".
If you wanted to use the jwt bearer handler you'll need to use something like this:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Audience = "https://localhost:5000/";
options.Authority = "https://localhost:5000/identity/";
})
But I guess you pretend to use the IdentityServerJwt handler instead.
Just call AddIdentityServerJwt without the scheme name parameter to add "IdentityServerJwt" as the scheme name for this handler. That is the default name assigned internally by the handler.
services.AddAuthentication()
.AddIdentityServerJwt();
You could call your scheme whatever you want by specifying the scheme name:
services.AddAuthentication()
.AddIdentityServerJwt("MySchemeName");
Be aware that your Identity service doesn't need this handler if you don't need to protect resources on it, that is, if you want that in addition to being the Identity service, it acts as an API resource protected by itself.
And another issue... according with IdentityServer4 documentation: UseIdentityServer includes a call to UseAuthentication, so it’s not necessary to have both.
When to add a default scheme in AddAuthentication(string defaultScheme)?
When you add a default scheme:
Any attempt to perform an authentication action (Authenticate, Forbid, Challenge, SignIn or SignOut) without specifying a scheme implicitly, will use the default scheme.
When a user is authenticated with more than one scheme, HttpContext.User will be set with the Identity resolved by the default scheme.
If it is not specified, we must use the Authorize attribute indicating the scheme or the schemes that should be used to construct the user's identity:
[Authorize(AuthenticationSchemes = "MyScheme")]

Overriding or Intercepting Camel Logging

An existing application uses Camel logging (bog the "log()" DSL, and also the Log component.
We would like to either intercept or override so that every log message also logs out a specific Header value (e.g. x-correlation-id=ABC-123)
What is a good, idiomatic way to achieve this?
Apache Camel supports pluggable LogListener since version 2.19.0. This is pretty powerful, because its method onLog, which is invoked right before logging, have instances of Exchange, CamelLogger and message. You can customize the message there with almost no limitations.
Implementation of LogListener:
public class MyLogListener implements LogListener {
#Override
public String onLog(Exchange exchange, CamelLogger camelLogger, String message) {
return String.format("%s: %s", exchange.getIn().getHeader(Exchange.CORRELATION_ID), message);
}
}
LogListener registration:
getContext().addLogListener(new MyLogListener());
If you are using Apache Camel version 2.21.0 and newer, you dont need register it to context, because it is looked up in Registry, so annotating MyLogListener as #Bean is enough.

Camel-Azure BlobServiceProducer IllegalArgumentException: Unsupported blob type:org.apache.camel.component.file.GenericFile

I have written a camel route which polls a folder and sends it to Azure Blob Container
I followed the example mentioned in the Azure document page
https://github.com/apache/camel/blob/master/components/camel-azure/src/main/docs/azure-blob-component.adoc
I am reversing the route. Instead of a consumer, I am using the Azure Blob Producer.
This is my route. I have used Java DSL.
from("file://C:/camel/source1").to("azure-blob://datastorage/container1/BLOB1?credentials=#credentials&operation=updateBlockBlob")
When I placed a file, I got the following error.
**java.lang.IllegalArgumentException: Unsupported blob type:org.apache.camel.component.file.GenericFile
at org.apache.camel.component.azure.blob.BlobServiceProducer.getInputStreamFromExchange(BlobServiceProducer.java:474) ~[camel-azure-2.19.2.jar:2.19.2]
at org.apache.camel.component.azure.blob.BlobServiceProducer.updateBlockBlob(BlobServiceProducer.java:143) ~[camel-azure-2.19.2.jar:2.19.2]
at org.apache.camel.component.azure.blob.BlobServiceProducer.process(BlobServiceProducer.java:79) ~[camel-azure-2.19.2.jar:2.19.2]**
I was able to fix this. I rewrote my route as.
from("file://C:/camel/source1")
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
Object file = exchange.getIn().getMandatoryBody();
exchange.getOut().setBody(
GenericFileConverter.genericFileToInputStream(
(GenericFile<?>) file, exchange));
}
})
.to("azure-blob://datastorage/container1/BLOB1?credentials=#credentials&operation=updateBlockBlob")
.to("mock:Result");
My Question is, do I need to really write the processor? Shouldn't the camel component be receiving a stream or a File Object?
Yeah this is a little bug. I have logged a ticket: https://issues.apache.org/jira/browse/CAMEL-11844
You can do the workaround you did, or you can add a .convertBodyTo and convert to a FileInputStream, String etc.
from("file://C:/camel/source1")
.convertBodyTo(String.class)
...

Camel doesn't retrieve SQS messages attributes

Here is the route:
from("aws-sqs://myQueue?accessKey=RAW(xxx)&secretKey=RAW(yyy)&deleteAfterRead=false")
.log("Attributes: ${header.CamelAwsSqsAttributes}")
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
Map<String, String> messageAttributes = (Map<String, String>) exchange.getIn().getHeader("CamelAwsSqsAttributes");
...
}
});
The .log() shows an empty map as well as if I print messageAttributes from the processor.
I also tried with the header "CamelAwsSqsMessageAttributes" instead of "CamelAwsSqsAttributes" but still nothing.
I see the attributes from the AWS console though.
By the way I get the message body, and I use Camel 2.15
I figured it out, here is an example to get queue attributes and message attributes:
main.bind("sqsAttributeNames", Collections.singletonList("All"));
main.bind("sqsMessageAttributeNames", Collections.singletonList("All"));
Or add those objects to the registry if you don't use org.apache.camel.main.Main
Then:
from("aws-sqs://myQueue?accessKey=RAW(xxx)&secretKey=RAW(yyy)&deleteAfterRead=false&attributeNames=#sqsAttributeNames&messageAttributeNames=#sqsMessageAttributeNames")
Of course you can replace Collections.singletonList("All") with the list of attributes you need if you don't want all of them.
I faced the same issue. When I am using camel-aws 2.16.x and I have my endpoint configured as follow
from("aws-sqs://myQueue?...&messageAttributeNames=#sqsMsgAttributeNames")
.to(...)
Then I have defined a Collection of String in my spring configuration file
#Bean
public Collection<String> sqsMsgAttributeNames() {
return Arrays.asList("Attr1", "Attr2");
}
Above settings work fine but ever since I upgraded to camel-aws 2.17.3. It no longer works. As mentioned in Camel SQS Component, collection of string no longer will be supported for messageAttributeNames and it should be a String with attributes separated by comma.
Note: The string containing attributes should not contain any white
spaces otherwise camel-aws component will only read the first
attribute. I went through the pain to debug on this. Besides, setting the
attribute value to be "All" does not work for me, none of the message
attributes will be read.
Below is the changes I made that allowed camel-aws's SqsConsumer to work again:
#Bean
public String sqsMsgAttributeNames() {
return String.format("%s,%s", "Attr1", "Attr2");
}
It is not an issue of Camel. It can be the default behavior of SQS or aws-java-sdk-core library.
As a quick solution this aws-sqs URL can be used
aws-sqs://myQueue?<other attributes here>&attributeNames=All
Keep in mind that localstack can work well without attributeNames parameter, unlike SQS.

WS-security (usernametoken) for CXF - encrypted passwords possible?

I'm trying to get together with CXF's WS-security implementation(usernametoken). I've done everything as said at http://cxf.apache.org/docs/ws-security.html. My PasswordCallbackHandler seems to be working, but what bothers me is a part:
if (pc.getIdentifier().equals("joe")) {
// set the password on the callback. This will be compared to the
// password which was sent from the client.
pc.setPassword("password");
}
as said
Note that for up to and including CXF 2.3.x, the password validation of the special case of a plain-text password (or any other yet unknown password type) is delegated to the callback class, see org.apache.ws.security.processor.UsernameTokenProcessor#handleUsernameToken() method javadoc of the WSS4J project. In that case, the ServerPasswordCallback should be something like the following one:
so up to cxf 2.3.x it was done like that
if (pc.getIdentifer().equals("joe") {
if (!pc.getPassword().equals("password")) {
throw new IOException("wrong password");
}
}
My issue is: I don't want to pc.setPassword("plainTextPassword") as I want to store it in any resource. This up-to-2.3.x design would allow me to do this since I could encrypt it manually. Are there any ways of setting encrypted password in callback or doing usernametoken authentication for stored, encrypted passwords ?
I'm using cxf 2.5.x
The answer (which I've tried) is found in this blog page:
http://coheigea.blogspot.com/2011/06/custom-token-validation-in-apache-cxf.html
The essence is to create a subclass of org.apache.ws.security.validate.UsernameTokenValidator, and override the verifyPlaintextPassword method. In that method, the UsernameToken (which provides getName and getPassword) is passed. Throw an exception if they're not valid.
To install the custom validator in a spring configuration, add e.g.
<jaxws:properties>
<entry key="ws-security.ut.validator">
<bean class="com.example.webservice.MyCustomUsernameTokenValidator" />
</entry>
</jaxws:properties>
into the <jaxws:endpoint/>.
Callback Handlers are there to provide the plaintext password or verify a digest password where the plaintext password is known.
But if you don't know the plaintext i.e. its one way hashed, then the callback interface is not appropriate and you should create a class that implements the Validator interface.
Here is my example implementation of that interface that uses a JPA repository in which the password is already stored as a BCrypt hash.
Use with the ws-security.ut.validator property documented here
i.e. as a CXF property
<entry key="ws-security.ut.validator" value-ref="com.package.CustomUsernameTokenValidator" />
public class CustomUsernameTokenValidator implements Validator {
#Autowired
ProfileRepository profileRepository;
#Override
public Credential validate(Credential credential, RequestData requestData) throws WSSecurityException {
Profile profile = profileRepository.findByName(credential.getUsernametoken().getName());
if (profile != null) {
if (BCrypt.checkpw(credential.getUsernametoken().getPassword(), profile.getPassword())) {
return credential;
}
}
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_AUTHENTICATION);
}
}

Resources