OpenIddict in abp framework - deploy in containerized environment (k8s) - kubernetes

abp version 6.0, tiered (.Web, .HttpApi.Host, .AuthServer), MVC.
The following messages appear in .AuthServer.
Client validation failed because 'https://webpage_url/signin-oidc' was not a valid redirect_uri for AppName_Web.
The authorization request was rejected because the redirect_uri was invalid: 'https://webpage_url/signin-oidc'.
How to properly set appsettings.json in .Web, .HttpApi.Host, .Web and .DbMigrator projects for deployment into containerized environments?
Where should be set internal (k8s) url address for auth server and when outer url (which is accessible via internet).
Url https://webpage_url/signin-oidc in the log is outer address (which is accessible via internet).
changing appsettings.json

Client validation failed because 'https://webpage_url/signin-oidc' was
not a valid redirect_uri for AppName_Web. The authorization request
was rejected because the redirect_uri was invalid:
'https://webpage_url/signin-oidc'.
Probably your redirect uri is not seeded, You can check your database if the redirect uri is added correctly for that client (application).
How to properly set appsettings.json in .Web, .HttpApi.Host, .Web and
.DbMigrator projects for deployment into containerized environments?
Where should be set internal (k8s) url address for auth server and
when outer url (which is accessible via internet).
Url https://webpage_url/signin-oidc in the log is outer address (which
is accessible via internet).
You don't change the redirect uri based on your deployment environment. It should point to a valid reachable endpoint that the openid-provider redirects to after signin.
As far as I understand, apart from normal login flow, you are having problems when interacting to openid-provider in isolated network (k8s, docker).
Since you have auth-server on real domain endpoint (like https://my-authserver.com), you are getting error from containers that tries to reach to domain (https://my-authserver.com/.well-known/openid-configuration) and you receive SSL error or not found error.
So you want internal requests done to the internal container (like http://my-auth-container/.well-known/openid-configuration) while user interacted login/logout should be done using the public domain name (https://my-authserver.com).
Instead of changing the public issuer, you can add OpenIdConnectOptions based on your deployment to configure MetadataAddress as:
context.Services.Configure<OpenIdConnectOptions>("oidc", options =>
{
options.MetadataAddress = configuration["AuthServer:MetaAddress"].EnsureEndsWith('/') +
".well-known/openid-configuration";
var previousOnRedirectToIdentityProvider = options.Events.OnRedirectToIdentityProvider;
options.Events.OnRedirectToIdentityProvider = async ctx =>
{
// Intercept the redirection so the browser navigates to the right URL in your host
ctx.ProtocolMessage.IssuerAddress = configuration["AuthServer:Authority"].EnsureEndsWith('/') +
"connect/authorize";
if (previousOnRedirectToIdentityProvider != null)
{
await previousOnRedirectToIdentityProvider(ctx);
}
};
var previousOnRedirectToIdentityProviderForSignOut =
options.Events.OnRedirectToIdentityProviderForSignOut;
options.Events.OnRedirectToIdentityProviderForSignOut = async ctx =>
{
// Intercept the redirection for signout so the browser navigates to the right URL in your host
ctx.ProtocolMessage.IssuerAddress = configuration["AuthServer:Authority"].EnsureEndsWith('/') +
"connect/endsession";
if (previousOnRedirectToIdentityProviderForSignOut != null)
{
await previousOnRedirectToIdentityProviderForSignOut(ctx);
}
};
This way, login/logout requests will be redirected to the configuration["AuthServer:Authority"] which should be a public domain (like https://my-authserver.com) and the internal requests will be redirected to the configuration["AuthServer:MetaAddress"] which should be an internal service (like http://my-auth-container)
For more details, check out:
eShopOnAbp Public-Web application configuration
eShopOnAbp Azure deployment configuration

Related

Blazor WASM IHttpClientFactory.CreateClient(..) returns client without JWT token in headers

I have a Blazor client app that needs to communicate to multiple API services. In order to simplify things, lets say I have two API servers:
one API server that handles login and register services (https://localhost:7031/api/...)
one server that serves Blazor ASP.NET Core hosted app (https://localhost:7030).
Below I show how I configured identity services on 7031 (I am not using Identity Server):
services.AddIdentity<AppUser, IdentityRole<Guid>>(...)
.AddEntityFrameworkStores<AuthDbContext>()
.AddDefaultTokenProviders();
services.AddAuthenticationWithJwt(configuration); // extension method that configures JWT (not shown here)
services.AddAuthorization(options =>
{
options.AddIdentityPolicies();
options.FallbackPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
.Build();
});
To cut things short, login / register works. Web app is served on port 7030, and authenticates using API's hosted on port 7031. I have also created a test API endpoint with [Authorize] on 7031, to see if I token will be attached to this HTTPS request - all is well. For this to work, I have the following configuration for HttpClient:
services.AddHttpClient(
"IdentityApi",
client => client.BaseAddress = new Uri("https://localhost:7031"));
services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("IdentityApi"));
So, when I inject HttpClient in some component, it successfully connects to authorized identity APIs. The problem starts when I need to connect to authorized base server APIs (port 7030). In one component I need to access both identity API and server API, so I tried by injecting IHttpClientFactory in it. For this purpose I also added another named HTTPClient:
services.AddHttpClient(
"BaseApi",
client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)); //"https://localhost:7030"
/* razor component */
var identityClient = HttpClientFactory.CreateClient("IdentityApi);
var baseClient = HttpClientFactory.CreateClient("BaseApi);
Both clients have NULL value for DefaultRequestHeaders property. Only if HttpClient is injected directly, then it has DefaultRequestHeaders set, along with Authorization token.
So, somehow this line makes sure that when HttpClient is injected, it has token assigned:
services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("IdentityApi"));
I tried to use CustomAuthorizationMessageHandler, as suggested here, but I receive exception:
System.InvalidOperationException: Unable to resolve service for type 'Microsoft.AspNetCore.Components.WebAssembly.Authentication.IAccessTokenProvider' while attempting to activate 'x.y.z.CustomAuthorizationMessageHandler'.
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateArgumentCallSites(Type implementationType, CallSiteChain callSiteChain, ParameterInfo[] parameters, Boolean throwIfCallSiteNotFound) ... etc
Anyone knows how can I generate named HttpClients with token assigned?

Nextjs redirect path has a redirection from HTTP to HTTPS

Im running my nextjs (UI only) application on localhost http://localhost:3000/, this localhost has to connect to BE API (another hosted service) say https://test-api/api/graphql. This API is called from FE on path http://localhost:3000/api/graphql which connect to https://test-api/api/graphql with the help of nextjs rewrite function
async rewrites() {
const rewrites = [];
rewrites.push({
source: "/api/graphql",
destination: "https://test-api/api/graphql",
});
return rewrites;
},
Now looks like my BE api https://test-api/api/graphql is doing a force direct for path http://localhost:3000/api/graphql to https://localhost/api/graphql
And now since https://localhost/api/graphql is not existing, I'm unable to connect to the BE API. Any idea what can i do in nextjs config so that even if redirect is happening I'm still able to connect to my BE API
Facing this issue only on local environment, hosted nextjs application is not having this issue as it is already on https

Keycloak client URL configuration of redirectURLs

I am having trouble trying to figure out what the values should be for 'Valid Redirect URIs', 'Base URL', 'Backchannel Logout URL'.
I am using Keycloak 15.02 along with 10 Spring Boot applications, and 2 Realms. The suite of applications and Keycloak are deployed to our customer sites, and may have more than 2 realms in some cases.
In our dev environment we have two hosts (api.dev, and web.dev) that are running Keycloak, and client apps. Everything is running Docker containers.
The client config for `Valid Redirect URIs', and 'Backchannel Logout URL' currently include the host name web.dev. I'd like to be able to remove that host name to make the Realm configs portable between environments. Having to configure each client in each realm makes for a lot of repetitive and mistake-prone work.
But when I remove the hostname, I get the error: Invalid parameter: redirect_uri.
The redirect URL shown by Keyloak in the request parameters looks the same for both configurations so I dont really understand why its telling me that its invalid.
This works:
That configuration produces the redirect_uri value seen in the following request:
http://api.dev.etisoftware.local:8080
/auth/realms/OSS/protocol/openid-connect/auth
?response_type=code
&client_id=launchpad
&scope=openid%20profile%20email%20roles
&state=E-8VBZUc1CbsIUi5HdPG68pNK1IVNB8bzDT3Aengx9Q%3D
&redirect_uri=http://web.dev.etisoftware.local/launchpad/login/oauth2/code/OSS
&nonce=3OUMxVmrglSC0KK-WGWDjG4yB9TOuvqBO5TMnDk4R-A
But this does not:
That configuration produces the redirect_uri value seen in the following request:
http://api.dev.etisoftware.local:8080
/auth/realms/OSS/protocol/openid-connect/auth
?response_type=code
&client_id=launchpad
&scope=openid%20profile%20email%20roles
&state=cGh1zZ3et0ssogIsNclL2sHcrfDxNePaHf5UXxw0aR8%3D
&redirect_uri=http://web.dev.etisoftware.local/launchpad/login/oauth2/code/OSS
&nonce=Qm846RYZZnU3fG4Cj75e8lBejupf24VbV1WjDVW1NJA
As you can see the values for redirect_uri in the request parameters are same for both requests and client configurations so its unclear (to me) what Keycloak is trying to tell me.
I also happen to have Keycloak and the client apps running in a K3s cluster. For some reason on that environment I dont have to have the hostname in the Valid Redirect URIs and it works perfectly fine. Is it just a fluke?
Redirect URIs tooltip:
"Valid URI pattern a browser can redirect to after a successful login or logout. Simple wildcards are allowed such as 'http://example.com/’. Relative path can be specified too such as /my/relative/path/. Relative paths are relative to the client root URL, or if none is specified the auth server root URL is used. For SAML, you must set valid URI patterns if you are relying on the consumer service URL embedded with the login request"
So if you want to use relative paths in the redirect URIs, then configure properly Root URL, not Base URL.
I got this answered on Keycloak's site but Jangaraj.
https://keycloak.discourse.group/t/trouble-with-configuring-client-valid-redirect-uris/13251

Facebook login fail elb of aws

There is the same topic at Facebook authentication callback to wrong server with AWS ELB elastic load balancer
It was working perfectly when there was a single instance but after add new instance the FB login not working.
I have enabled the sticky session after that it is not working. I am not using the distributed cache. The main problem is the cookie get changed after redirect from facebook. But do not know what need to do keep the cookie same.
Here is the sticky session setup image that I configure.
I add the bellow code at the UseNopAuthentication.
application.Use((context, next) =>
{
if (context.Request.Headers["x-forwarded-proto"] == "https")
{
context.Request.Scheme = "https";
}
return next();
});
I set the UseHttpXForwardedProto to true from appsettings.json but did not work as we remove http to https redirection from application.
A useful link How to Configure HTTP redirect URL for Facebook Login in ASP.NET MVC

Keycloak issuer validation and multi-tenancy approach

Let's say we have several micro-services. Each of them uses Keycloak authentication. We have also load balancer based on for ex. nginx which has external URLs and different routes to keycloak (for ex. in OpenShift it can be https://keycloak.rhel-cdk.10.1.2.2.xip.io). But internally this address can be inaccessible. Also having micro-service configuration dependent on the load balancer URL is a bit weird. What what be more appropriate is to use internal keycloak auth URL inside of the micro-services or even short URI. But in this case token will not be validated because of issuer validation problem. How to configure this in good and flexible manner? Can I simply override realmInfoUrl in order to change the validation? Can I define what issuer will be used for client based token.
Another problem is how to better handle multi-tenant scenario? First on the client side I guess we don't have any specific support for multi-tenancy. I should handle this manually by switching between different URLs/headers and use proper Config Resolver. On the server side I need to dynamically provide a proper KeycloakDeployment instance for each case. Any other recommendations?
Unfortunately Keycloak is too restrictive with its token validation according to the issuer ("iss") field in the token. It requires that the URL used to validate the token matches the URL in the "iss" field.
A while ago I have opened a JIRA ticket for that problem (vote for it!): https://issues.jboss.org/browse/KEYCLOAK-5045
In case this helps anyone out during the early stages of development, you can set the Host header to the keycloak url that your backend service will use during the validation of the token. This way, the generated token will contain your Host header url in the issuer field. In my sandbox, I had keycloak running on docker at keycloack:8080 and a functional test calling keycloack via localhost:8095 to request a token (direct grant). Before setting the Host header to keycloack:8080, the issuer field was being set to localhost:8095 and the token was failing the validation with the "Invalid token issuer" error, since the backend service connects to keycloak on keycloak:8080 and TokenVerifier.java does the following check.
public boolean test(JsonWebToken t) throws VerificationException {
if (this.realmUrl == null) {
throw new VerificationException("Realm URL not set");
} else if (!this.realmUrl.equals(t.getIssuer())) {
throw new VerificationException("Invalid token issuer. Expected '" + this.realmUrl + "', but was '" + t.getIssuer() + "'");
} else {
return true;
}
}
Reference: https://github.com/keycloak/keycloak-community/blob/master/design/hostname-default-provider.md