I'm trying to upgrade my APEX application (APEX 20.2.0.00.20) with a Keycloak authorization procedure.
What I am doing:
A - On Keycloak:
Realm: we already have a realm configured, which is used by other apps, in other development technologies.
1 - Client configuration - first attempt:
Root URL: ${authBaseUrl}
Valid Redirect URIs: /realms/[realm]/[client]/*
Base URL: /realms/[realm]/[client]/
Web Origins: *
2 - Client configuration - second attempt:
Root URL: empty
Valid Redirect URIs: http://[APEX app IP]:7020/*
Base URL: http://[APEX app IP]:7020/ords/[workspace]/r/[app_name]/
Web Origins: *
APEX configuration:
Authentication Scheme:
Credential Store: [APEX webcredential configured to keycloak client]
Authentication Provider: generic oauth2 provider.
Authorization Endpoint URL:
https://[keycloak address]/auth/realms/[realm]/protocol/openid-connect/auth
Token Endpoint URL:
https://[keycloak address]/auth/realms/[realm]/protocol/openid-connect/token
User Info Endpoint URL:
https://[keycloak address]/auth/realms/[realm]/protocol/openid-connect/userinfo
Token Authentication Method: basic authentication and client id in body
Scope: email
Authentication URI Parameters: empty
Username: #sub# (#APEX_AUTH_NAME#)
Convert Username To Upper Case: no
Verify Attributes: yes
** Tests **
I run my APEX app URL in workspace.
Test result of Client configuration nr. 1:
The following URL is shown as result:
https://[keycloak host]/auth/realms/[realm]/protocol/openid-connect/auth?response_type=code&client_id=[client_name]&scope=email&redirect_uri=http://[APEX address]:7020/ords/apex_authentication.callback&state=[A TOKEN/HASH]
And on the screen, the keycloak background image with the message:
Invalid parameter: redirect_uri. and a return to application link.
Test result of Client configuration nr. 2:
The keycloak login URL is activated and the login form is shown.
I type my user and password (keycloak user, configured on the client) and submit.
The following URL is shown as result:
https://[keycloak host]:7020/ords/apex_authentication.callback?state=[A TOKEN/HASH]
And on the screen, a APEX grey background (I know is a apex screen because a error with the icon is shown here) with the message:
Error processing request.
Contact your application administrator.
Question:
I think the second configuration is better, because I can do the Login on keycloak, but the redirect by keycloak to APEX fails, I think I APEX side.
Maybe any information sent by keycloak is wrong or it is missed.
Anyone knows what is the right configuration in APEX and keycloak?
I'm trying to integrate Grafana with Keycloak but I'm receiving the following errors when I'm setup the "Scopes = openid profile email" on grafana.ini :
t=2021-11-03T05:51:22-0400 lvl=eror msg="**Error getting email address**" logger=oauth.generic_oauth url=http://192.168.101.221:8080/auth/realms/grafana/protocol/openid-connect/userinfo/emails error="{\"error\":\"RESTEASY003210: C**ould not find resource for full path: http://101.221:8080/auth/realms/grafana/protocol/openid-connect/userinfo/emails**\"}"
t=2021-11-03T05:51:22-0400 lvl=eror msg="login.OAuthLogin(get info from generic_oauth)" logger=context userId=0 orgId=0 uname= error="Error getting email address: {\"error\":\"RESTEASY003210: Could not find resource for full path: http://192.168.101.221:8080/auth/realms/grafana/protocol/openid-connect/userinfo/emails\"}"
Definitely, is not getting any email addreess when I'm trying to call the "http://192.168.101.221:8080/auth/realms/grafana/protocol/openid-connect/userinfo/emails" - Page not found.
My grafana.ini config looks like below:
[auth.generic_oauth]
enabled = true
allow_sign_up = true
team_ids =
allowed_organizations =
name = Keycloak
scopes = openid profile email
client_id = grafana
client_secret = ba342011-3705-483c-8e04-7f95be561cd5
auth_url = http://192.168.101.221:8080/auth/realms/grafana/protocol/openid-connect/auth
token_url = http://192.168.101.221:8080/auth/realms/grafana/protocol/openid-connect/token
api_url = http://192.168.101.221:8080/auth/realms/grafana/protocol/openid-connect/userinfo
I found the solution:
Step 1. Create an user on keycloak , for example : grafana and set an password and also you can add you email address or whatever email address you want :
keycloak user
My current use case is: I have a frontend application where a user is logged in via Keycloak. I would like to implement some parts of the Ditto HTTP API in this frontend (https://www.eclipse.org/ditto/http-api-doc.html).
For example I want to create policies (https://www.eclipse.org/ditto/basic-policy.html) for authorization. I've read in the documentation that one can use an OpenID Connect compliant provider and the form is : (https://www.eclipse.org/ditto/basic-policy.html#who-can-be-addressed).
There's basic auth example at the bottom of the page, it seems to use the username in this case.
{
"policyId": "my.namespace:policy-a",
"entries": {
"owner": {
"subjects": {
"nginx:ditto": {
"type": "nginx basic auth user"
}
},
...
}
My question is: What exactly would be the sub-claim if I want to use Keycloak? Is it also the username of the user I want to grant rights to? And how would I get this in my frontend where I want to specify the policy for sending it to Ditto afterwards?
UPDATE 1:
I tried to enable keycloak authentication in Ditto like suggested below and as stated here: https://www.eclipse.org/ditto/installation-operating.html#openid-connect
Because I'm running Ditto with Docker Compose, I added the following line as an environment variable in ditto/deployment/docker/docker-compose.yml in line 136: - Dditto.gateway.authentication.oauth.openid-connect-issuers.keycloak=http://localhost:8090/auth/realms/twin
This URL is the same as in the issuer claim of my token which I'm receiving from keycloak.
Now if I try to make for example a post request with Postman to {{basePath}}/things I get the following error:
<html>
<head>
<title>401 Authorization Required</title>
</head>
<body bgcolor="white">
<center>
<h1>401 Authorization Required</h1>
</center>
<hr>
<center>nginx/1.13.12</center>
</body>
</html>
I chose Bearer Token as Auth in Postman and pasted a fresh token. Basic Auth with the default ditto user is still working.
Do I have to specify the new subject/my user in Ditto before?
UPDATE 2:
I managed to turn basic auth in nginx off by commenting out "auth_basic" and "auth_basic_user_file" in nginx.conf!
It seems to be forwarded to Ditto now, because now I get the following error with Postman:
{
"status": 401,
"error": "gateway:jwt.issuer.notsupported",
"message": "The JWT issuer 'localhost:8090/auth/realms/twin' is not supported.",
"description": "Check if your JWT is correct."
}
UPDATE 3:
My configuration in gateway.conf looks now like this:
oauth {
protocol = "http"
openid-connect-issuers = {
keycloak = "localhost:8090/auth/realms/twin"
}
}
I also tried to add these two lines in the docker-compose.yml:
- Dditto.gateway.authentication.oauth.protocol=http
- Dditto.gateway.authentication.oauth.openid-connect-issuers.keycloak=localhost:8090/auth/realms/twin
Unfortunately I still had no luck, same error as above :/ It seems like an user had a similar problem with keycloak before (https://gitter.im/eclipse/ditto?at=5de3ff186a85195b9edcb1a6), but sadly he mentioned no solution.
EDIT: It turns out that I specified these variables in the wrong way, the correct solution is to add them as part of command: java ... more info here
UPDATE 4:
I tried to build Ditto locally instead of using the latest docker images and I think I might be one step further now, it seems like my oauth config is working. I get now:
{
"status": 503,
"error": "gateway:publickey.provider.unavailable",
"message": "The public key provider is not available.",
"description": "If after retry it is still unavailable, please contact the service team."
}
The error message from the log is:
gateway_1 | 2020-11-05 15:33:18,669 WARN [] o.e.d.s.g.s.a.j.DittoPublicKeyProvider - Got Exception from discovery endpoint <http://localhost:8090/auth/realms/twin/.well-known/openid-configuration>.
gateway_1 | akka.stream.StreamTcpException: Tcp command [Connect(localhost:8090,None,List(),Some(10 seconds),true)] failed because of java.net.ConnectException: Connection refused
gateway_1 | Caused by: java.net.ConnectException: Connection refused
...
gateway_1 | java.util.concurrent.CompletionException: org.eclipse.ditto.services.gateway.security.authentication.jwt.PublicKeyProviderUnavailableException [message='The public key provider is not available.', errorCode=gateway:publickey.provider.unavailable, statusCode=SERVICE_UNAVAILABLE, description='If after retry it is still unavailable, please contact the service team.', href=null, dittoHeaders=ImmutableDittoHeaders [{}]]
...
gateway_1 | Caused by: org.eclipse.ditto.services.gateway.security.authentication.jwt.PublicKeyProviderUnavailableException [message='The public key provider is not available.', errorCode=gateway:publickey.provider.unavailable, statusCode=SERVICE_UNAVAILABLE, description='If after retry it is still unavailable, please contact the service team.', href=null, dittoHeaders=ImmutableDittoHeaders [{}]]
...
gateway_1 | Caused by: akka.stream.StreamTcpException: Tcp command [Connect(localhost:8090,None,List(),Some(10 seconds),true)] failed because of java.net.ConnectException: Connection refused
gateway_1 | Caused by: java.net.ConnectException: Connection refused
My keyloak is definitely running, I'm able to get tokens. If I'm opening http://localhost:8090/auth/realms/twin/.well-known/openid-configuration which is in the first error message, I'm able to see my openid-configuration from keycloak config.
Edit: It seems that my gateway container cannot reach my keycloak container, will try to figure this out.
FINAL UPDATE:
Unreachable keycloak docker container from the gateway docker container was the issue. I'm now using traefik:
Keycloak container has the following alias: keycloak.localhost
Oauth configuration in the gateway looks like this:
oauth {
protocol = "http"
openid-connect-issuers = {
keycloak = "keycloak.localhost/auth/realms/twin"
}
}
Now the gateway can find the keycloak container via the alias and I can still use the keycloak admin ui from my localhoast: http://keycloak.localhost:8090/auth/admin/
Additional info: Traefic Blog
What exactly would be the sub-claim if I want to use Keycloak?
Keycloak provides you a JWT.
A JWT is an encrypted JSON which contains multiple fields called "claims". You can check how your token looks like by visiting https://jwt.io and pasting your token there. One of those fields is called sub. This is the sub claim.
To enable your keycloak authentication in eclipse ditto you need to add the issuer to the ditto configuration.
An example can be founde here.
The address must match the URL in the issuer claim of your JWT token.
ditto.gateway.authentication {
oauth {
protocol = "http"
openid-connect-issuers = {
some-name = "localhost:8090/auth/realms/twin"
}
}
}
Is it also the username of the user I want to grant rights to?
In eclipse ditto there is not really a concept of "user names". Eclipse ditto authentication is based on authorization subjects. For the basic authentication example you provided, the authorization subject which is generated within ditto is nginx:ditto.
For JWT authentication the authorization subject is generated as a combination of the name for the open id connect issuer which you configured (in my case some-name) and the value of the sub claim. An authorization subject could look like this: some-name:8d078113-3ee5-4dbf-8db1-eb1a6cf0fe81.
And how would I get this in my frontend where I want to specify the policy for sending it to Ditto afterwards?
I'm not sure if I understand the question correctly. If you mean how to authenticate your frontend HTTP requests to eclipse ditto, you need to provide the JWT to eclipse ditto by adding it to the authorization header of your HTTP requests in the following form:
authorization: Bearer yourJWT
If you mean how you would know the sub claim of a JWT, you need to parse the JWT to a JSON object and then read the sub claim out of the payload section.
I'm trying to hit keycloak installed in docker container behind an nginx server. That's passing it with proxy_pass.
The query is http://example.compute.amazonaws.com/auth/realms/master/protocol/openid-connect/auth?client_id=security-admin-console&redirect_uri=http%3A%2F%2Fec2-<ip>.eu-west-2.compute.amazonaws.com%2Fauth%2Fadmin%2Fmaster%2Fconsole%2F&state=a4b04d6b-b490-41ef-8974-f99cf210129d&response_mode=fragment&response_type=code&scope=openid&nonce=9f67c39b-2312-48cd-980f-ca08f265504d
However when I want to go through to the admin console I get a screen with "Missing parameters: client_id".
Anyone have any ideas? Logging locations etc, any assistance would be incredible.
Update:
What is logged on that request is only:
10:57:26,916 WARN [org.keycloak.events] (default task-1) type=LOGIN_ERROR, realmId=master, clientId=null, userId=null, ipAddress=<anotherip>, error=invalid_request
The fault was with the NGINX config that was stripping the URL params off.
Solution was to add $is_args$args to the proxy_pass url.
When user clicks login, redirected to Keycloak login page & then after successful login, user comes back to application with 400 error page.
Server log shows following:
[Server:node-00] 13:40:00,709 WARN
[org.keycloak.adapters.OAuthRequestAuthenticator] (default task-30)
state parameter invalid
My application conf is:
<subsystem xmlns="urn:jboss:domain:keycloak:1.1">
<secure-deployment name="appWEB.war">
<realm>demo</realm>
<resource>app</resource>
<public-client>true</public-client>
<auth-server-url>http://localhost:8180/auth</auth-server-url>
<ssl-required>EXTERNAL</ssl-required>
</secure-deployment>
</subsystem>
Application URL is https://localhost:8443/app & redirect_url is https://localhost:8443/app/private.jsf.
When I use http, it works. But the error comes when I use same with https.
Any thoughts?
Here it can be many scenario which may failing with https
Keycloak running in https
Create self sign certification for keycloak.
Import this certificate to your local Java environment.SO handshake can be possible.
I hope you generate the certificates in keycloak you can find the the certificate inside keycloak/security/ssl.