Problems with token issuer when running OpenIddict as a container - docker

I'm trying to run OpenIddict in Docker together with an Api, the Api communicates with the OppenIddict application through a defined link in docker-compose (api -> login). I have a frontend application running outside of Docker accessing the Api and Login over localhost-addresses. Everything works fine until the point where the Api communicates with the Login application using the linked address (login), the problem then is that the issuer of the token doesn't match.
The issuer is in this case http://localhost:4000 and it's expecting http://login when OpenIddict is validating the token.
I've tried to change the issuer in Startup and the OpenIddictServerBuilder, the problem then is that it changes the base address used in the discovery document resulting in the frontend trying to connect to http://login.
One potential solution to this is to change the hosts file and set a network redirect (redirecting login to 127.0.1.1 and setting the network redirect to use localhost:4000), but this feels like a too complex solution.
I've had similar issues with IdentityServer4, but there you can change the issuer without affecting the base address.
Hope that I explain the issue well enough, any ideas is really appreciated!

Finally figured it out, you can specify a list of valid issuers during the configuration of the OpenIddict server in Startup. Here I can say that http://localhost:4000 is a valid issuer and therefore the token will be valid when the Api validates it even though it is communicating using a different address. I did it like this if it helps anyone in the future:
services.AddOpenIddict()
.AddCore()
.AddServer(builder =>
{
builder.Configure(options =>
{
options.TokenValidationParameters.ValidIssuers = new List<string>
{
"http://localhost:4000/",
};
});
});

Related

Keycloak token verification fails when the backend is running in a Docker container

I am in the early stages of building my web application. I intend to use Keycloak as the identity provider to secure the backend. On my local machine, I am running both Keycloak and my backend as docker containers but on different networks, since eventually in production, I would like to have the authentication server running Keycloak running separately from the backend e.g account.example.com and api.example.com respectively
Locally, my Keycloak container can be accessed via the base URL http://localhost:8080/auth and the backend via http://test.localhost:8000/
I have created a client in the Keycloak realm whose access type is confidential. I am generating the token using the authorization code grant type.
Each REST API endpoint on the backend would therefore verify the token passed to the authorization header and then call the Keycloak server to verify the token before processing the request.
The issue that I am currently experiencing is that the token verification fails with the response
{"error":"invalid_token","error_description":"Token verification failed"}'
After investigation, apparently, it's because I am calling the Keycloak server from the backend API container. If I generate the token using curl within the backend docker container, the token I receive is being verified fine, but a token generated outside the container is not.
I am using python-keycloak as a wrapper for Keycloak REST API
from keycloak import KeycloakOpenID
self._keycloak = KeycloakOpenID(
server_url='http://host.docker.internal:8080/auth/',
realm_name='myrealm',
client_id='myclient',
client_secret_key='mysecret,
)
if "HTTP_AUTHORIZATION" not in request.META:
return JsonResponse(
{"detail": NotAuthenticated.default_detail},
status=NotAuthenticated.status_code,
)
auth_header = request.META.get("HTTP_AUTHORIZATION").split()
token = auth_header[1] if len(auth_header) == 2 else auth_header[0]
try:
self.keycloak.userinfo(token)
except KeycloakInvalidTokenError as e:
# print(e)
return JsonResponse(
{"detail": AuthenticationFailed.default_detail},
status=AuthenticationFailed.status_code,
)
How do I resolve this and have token verification working on my local machine
Your token is invalid, because the issuer (iss) in the token does not match the issuer that is expected by your backend service.
Your backend (or an adapter/framework within your backend) will use OIDC discovery protocol to determine the expected issuer. For this, it will call https://keycloak-container-name/auth/realms/<your-realm>/.well-known/openid-configuration. This will return metadata like this:
{
"issuer":"https://keycloak-container-name/auth/realms/<your-realm>",
...
}
Keycloak will determine the host part of the issuer (keycloak-container-name in this case) based on the request. So, if your backend queries the discovery endpoint wiht keycloak-container-name from within the docker network, the host part will be your-container-name. Your backend will expect the issuer to be https://keycloak-container-name/auth/realms/<your-realm> in this case.
Now, if you want to query a token from your frontend, your frontend will send the request to http://localhost:8080/auth/.... Since the issuer will be determined based on the request, the issuer in that token will be https://localhost:8080/auth/realms/<your-realm> in this case.
This does not match the expected issuer https://keycloak-container-name/auth/realms/<your-realm> and therefore the token will be rejected as invalid.
You can also verify this by calling the OIDC discovery endpoint via http://localhost:8080/auth/realms/<your-realm>/.well-known/openid-configuration. You will get a response like this:
{
"issuer":"http://localhost:8080/auth/realms/<your-realm>",
...
}
To fix this you can set the Frontend URL in your realm to http://localhost:8080/auth. With this setting the issuer will no longer be determined in the request, but will be fixed http://localhost:8080/auth/realms/<your-realm>.
You can check this from within your backend container by issuing a request to https://keycloak-container-name/auth/realms/<your-realm>/.well-known/openid-configuration. This will return metadata like this now:
{
"issuer":"http://localhost:8080/auth/realms/<your-realm>",
...
}
If you do not want to configure this for every realm seperately, you may instead configure the default hostname provider server-wide.
Problem is with the issuer of the token. Issuer of your token from the postman is http://localhost:8080/..., but backend is configured to accept only issuer http://host.docker.internal:8080/.... It is a best practise to use the same protocol:domain[:port] for IdP (Keycloak in your case) everywhere e.g. https://keycloak.domain.com, otherwise you will have this kind of problems.

Authorize Attribute to handle valid user in Docker

I am running my .net core 5 application, namely Authentication Server and API Gateway, in Docker in Linux container. And controller class in the API GateWay contains an [Authorize] attributes to validate the user. Despite the user is valid (eg register and log in successfully), API Gate is unable allow the user to access the API. And here a part of my log that is complaining:
Failed to validate the token.Microsoft.IdentityModel.Tokens.SecurityTokenSignatureKeyNotFoundException: IDX10501: Signature validation failed. Unable to match key:
kid: '6B7ACC520305BFDB4F7252DAEB2177CC091FAAE1'.
Exceptions caught:''.
Am I right to say that, is it due to the cookies generated by Identity Service 4; idsrv.session and .netcore related cookies failed to be shared.
This is because based on my observation when my micro-services are running in IIS. The Authentication Server, shall generates two mentioned cookies when user logs in successfully. And the mentioned cookies are "shared" among the rest of the micro-services, partly because they are in the same localhost but different port number.
When my application are migrated to Docker; the Authentication Server is able to generate the related cookies, the rest of the contains failed to get the cookies.
And as the result, which I'm guessing, that is the result that API Gateway is unable to authorise a valid user.
Hence, I am wondering is my understanding of Authorization is correct and how should I fix the issue with a valid user.
https://stackoverflow.com/a/58877655/3312570
The error code IDX10501 points to incorrect bearer token middleware.
Try adding cookies. Use the below code. My guess, it won't help.
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie();

Google OAuth redirect URL mismatch

I've followed a sample created here:
https://github.com/googlesamples/oauth-apps-for-windows/blob/master/OAuthDesktopApp/README.md
to have Google OAuth work with my desktop application and it seems I got stuck with redirect URIs. I know I have to provide a RedirectURI on Google Credentials page (in Developers Console), but I cannot figure out how to provide a RedirectURI to accept any port number.
In this sample, a local HTTP server opens a connection on localhost (127.0.0.1) and it seems to work, no matter which port is used for listening (it finds some random port and then sends RedirectURI as localhost:randomport) and authentication works as expected. However, when I try to do that in my application, I get notification that RedirectURI is not defined. Is there any special syntax to be used when defining RedirectURI inside Developer Console?
As stated in the read me for the sample you are following
Create a new OAuth 2.0 client, select Other
You should have created a oauth2 client of type other, not an web application. the only difference really is that other is intended for desktop or native applications which use localhost by default as the redirect uri.

Newly deployed cloud run app inaccessible, despite having requisite permissions (403)

Following this quickstart guide (Python version), I was able to quickly create and deploy a Python Flask app to a project owned by my organization. However, toward the end the guide instructs "Visit your deployed container by opening the service URL in a web browser", and but this is the step I can't get to work.
Curiously, in the logs
(https://console.cloud.google.com/logs/) the 'textPayload' data element for the request is "The request was not authenticated. Either...", which seems unusual, as I'd expect an unauthenticated request to return 401, not 403. In any case, it does not list my org email address in the request, suggesting my identity for some reason isn't being supplied. Any way to fix this so I can access the URL using a user with appropriate permissions?
Note: I can access the URL by manually running "curl -H 'Authorization: Bearer <my token>'"
Cloud Run URLs aren't directly accessible if the service only allows authenticated invocations because passing the URL on your browser will not generate the credential for you. You will have to pass auth credentials on your request and the easiest way to invoke the service is to use a tool like curl which is exactly what you noted.
You have two options to authenticate your end users which are the following:
Integrate Google Sign-In.
Implement Identity Platform or Firebase Authentication.
In either of the above choices, you need a public web or mobile app that handles the sign-in flow and then makes authenticated API calls to a Cloud Run (fully managed) service.
For more information on authenticating end users, check out the official documentation.
To complement what Mr. Donnald Cucharo said, you can also use Identity Aware Proxy (IAP) to expose your application in a secure way.

Get Default Credentials From A Windows Service

I want to be able to get the default credentials from a within a windows service. However if I call
var credentials = CredentialCache.DefaultCredentials;
Or
var networkCredentials = CredentialCache.DefaultNetWorkCredentials;
The NetworkCredential returned (as an ICredential) the UserName and Domain etc are empty. I am running the windows service under a domain account.
If I create a simple console application then a valid credential is returned.
The reason I need this is to be able to set the credentials on a signalR HubConnection. The SignalR service is running in IIS with Windows Authentication enabled. The only way I can see to do this is to set the HubConnection.Credentials property. But as explained above I cannot get the credentials when I am running in a windows service.
Can anyone help?
I found this was not the issue after all and not being able to retrieve the credentials was a red herring (I found I could not retrieve them in the console either), when calling into the SignalR hub the identity is passed and the request authenticated by IIS.
But to conclude. This was my stupid mistake, nothing to do with the default credentials.
As a side note, the credentials are not available to retrieve on purpose see
link

Resources