Register confidential OIDC client through registration endpoint - keycloak

I would like to programmatically register confidential OIDC client (the client is a backend service). I checked the keycloak document about it. And I use the “Initial access token“ approach as recommended.
After I created a "initial access token" on the UI console, I register a new client by:
POST https://my-keycloak-host/auth/realms/MyRealm/clients-registrations/default
Headers: Authorization: Bearer <initial access token>
{ "clientId": "my-client" }
The response contains a registrationAccessToken. But I expect to get a client secret. How can I get it? And what is the usage for that registrationAccessToken?

For some unknown reason Keycloak doesn't set secret properly. But you can define own secret with secret property in the payload (tested with Keycloak 16.1.1), e.g.:
{
"clientId": "my-client",
"secret": "my-secret"
}
Doc:
https://openid.net/specs/openid-connect-registration-1_0.html
https://www.keycloak.org/docs/latest/securing_apps/index.html#_client_registration

Related

Configure remote-user name in Wildfly OIDC

I successfully configured Wildfly 26 to authenticate via Keycloak following the usual tutorial. Example: https://www.youtube.com/watch?v=2gQO4_7Z5CI
It works as announced - when required the user's browser gets redirected to keycloak, after login it redirects back. That much for authentication. I also checked that the authorization can be performed by running request.isUserInRole(...) which is true if and only if in keycloak some role was associated to that user. That much for authorization.
But the methods request.getRemoteUser() and request.getUserPrincipal().getName() only return the keycloak's userid, which looks like 896128e8-3260-42c8-9457-3c8ee236eb4d.
How can I make Wildfly OIDC to report the username by looking at the preferred_username token claim?
So I learned that Wildfly's OIDC client can be configured to read the principal name from another token claim. For this it was sufficient to add the line
"principal-attribute": "preferred_username",
into my web application's WEB-INF/oidc.json. This is the only line required on top of the configuration generated by Keycloak:
{
"principal-attribute": "preferred_username",
"realm" : "myrealm",
"auth-server-url" : "http://localhost:8180/",
"ssl-required" : "external",
"resource" : "webapp",
"credentials" : {
"secret" : "7BqGXjt7cw6p88DNoiXCi1663bIzKPwY"
},
"confidential-port" : 0
}
When authenticating, the tokens emitted by Keycloak are exactly the same however the client uses the specified field's value instead of the sub value.
From the documentation:
principal-attribute
OpenID Connect ID Token attribute to populate the UserPrincipal name with. If token attribute is null, defaults to sub. Possible values are sub, preferred_username, email, name, nickname, given_name, family_name.

Using Keycloak for defining subjects in policies in Eclispe Ditto

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.

Keycloak frontend and backend clients

This is related to keycloak clients. My frontend is connected to public client and backend is connected to confidential client.
I am able to login, get the code, as I am using response_type=code by turning on "Standard Flow Enabled".
This code redirects and returns me Idtoken, refreshtoken and token.
Now I need to communicate with backend which is confidential, I would like to authenticate user using some of the values which I have received from the frontend client.
How can I do that?
Here is my frontend and backend conf
FRONTEND
{
"realm": "xyz",
"auth-server-url": "http://localhost:8333/auth/",
"ssl-required": "external",
"resource": "frontend-app",
"public-client": true,
"confidential-port": 0,
"enable-cors": true
}
BACKEND
keycloak.auth-server-url=http://localhost:8333/auth
keycloak.realm=xyz
keycloak.resource=backend-app
keycloak.principal-attribute=preferred_username
keycloak.bearer-only=true
keycloak.credentials.secret=xxx-xxx-xxx
this is from realm setting
This might help somebody.
My backend service which is springboot project with spring security keycloakAuthenticationProvider does authenticate the token received from the frontend public client.
Call from frontend
axios({
method: 'GET',
url: '/api/authenticate',
headers:{'Authorization': 'Bearer '+keycloak.token+''}
}).then((r) => {
console.log("response",r)
})
Call to backend
#GetMapping("/api/authenticate")
public ResponseEntity<SecureUserDto> authenticate() {
String username = SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString();
User user = userRepository.findWithPrivilegesByUsername(username);
return ResponseEntity.ok();
}
But i still was not able to get it right on postman at ../token end point provided by keycloak server.
Anyways my work is done.

Keycloak RBAC for nginx reverse proxy

I started to work with keycloak, and here is a setup I want to test.
I want to test this scenario:
It works, but I want to implement role-based access to apps behind Nginx proxy and I can't understand how exactly payload of jwt token generates.
Here is my JWT tokens payload:
"jti": "f5f07b6f-ccae-4f57-a8ea-ae02ebb3cb12",
"exp": 1569263630,
"nbf": 0,
"iat": 1569227630,
"iss": "https://keycloak.domain.local/auth/realms/LDAP_test",
"sub": "fedc6baf-4ba4-4fa6-924c-9501edf070f7",
"typ": "Serialized-ID",
"auth_time": 0,
"session_state": "aa0052ee-b5e1-45cc-bee4-e7bccdfa4a59",
"state_checker": "sC_nvlDXfjUDHhC15ZDpPauX5JkxhvVtYUOn62PhtV8"
I want my token to contain roles, username and email and i run out of ideas how to put it there. Is client somehow related to the content of the token or keycloak always gives everything he have into it?
Here are my Nginx client settings:
server {
server_name demo-a.domain.local;
location / {
proxy_pass http://10.10.10.168/index.html;
access_by_lua '
local opts = {
redirect_uri_path = "/redirect_uri",
accept_none_alg = true,
discovery = "https:/keycloak.domain.local/auth/realms/LDAP_test/.well-known/openid-configuration",
client_id = "nginx-gateway",
client_secret = "19fe43bc-4167-4433-816a-eb96da33f9a3",
redirect_uri_scheme = "https",
logout_path = "/logout",
redirect_after_logout_uri = "https://keycloak.domain.local/auth/realms/LDAP_test/protocol/openid-connect/logout?redirect_uri=https://www.nginx-gateway.domain.local/",
redirect_after_logout_with_id_token_hint = false,
session_contents = {id_token=true}
}
-- call introspect for OAuth 2.0 Bearer Access Token validation
local res, err = require("resty.openidc").authenticate(opts)
if err then
ngx.status = 403
ngx.say(err)
ngx.exit(ngx.HTTP_FORBIDDEN)
end
';
}
Thanks in advance!
Did you configure the client to be openid client? Is it UI or machine client?
To cause your JWT to have roles, one follows usual below flow:
Create client in Keycloak admin-console and configure it to support
open-id-connect.
Add users to Keycloak.
Assign roles to users.
Configure your client to have the client id of Keycloak client.
Exercise browser or machine flow which involves passing of JWT to the
client. JWT will contain the roles as claims.
Still debugging it, but here are a few pointers you will definitely need....
You need to be Authenticating Reverse Proxy with KeyCloak while using the keycloak behind an nginx proxy
Ok, completely forget about this question, but still here is a solution.
Just use a proper oidc proxy like gatekeeper (louketo now) or oauth2-proxy.

Access headers in AWS API Gateway using HTTP Proxy?

I'm using AWS API Gateway and it's HTTP Proxy,
I need to pass Authorization header to my endpoint through AWS API Gateway
Things I've tried:
Setting Method Request like so,
Integration Request setup
This doesn't work, my app doesn't receive the Authorization header,
Also I've tried using mapping template
{
"method": "$context.httpMethod",
"body" : $input.json('$'),
"headers": {
#foreach($param in $input.params().header.keySet())
"$param": "$util.escapeJavaScript($input.params().header.get($param))" #if($foreach.hasNext),#end
#end
},
"queryParams": {
#foreach($param in $input.params().querystring.keySet())
"$param": "$util.escapeJavaScript($input.params().querystring.get($param))" #if($foreach.hasNext),#end
#end
},
"pathParams": {
#foreach($param in $input.params().path.keySet())
"$param": "$util.escapeJavaScript($input.params().path.get($param))" #if($foreach.hasNext),#end
#end
}
}
This also doesn't work.
How can this be accomplished?
API Gateway strips the AWS SigV4 Authorization header due to security reasons. If you are using other Authorization mechanism like OAuth, the header wouldn't be stripped.
Recently I had to try using an API Gateway HTTP proxy to pass an AWS SigV4 HTTP request to an endpoint.
After testing and debugging found that the Authorization is being consumed and not passed!
So while sending the request to the API Gateway - I sent Authorization and a copy of the Authorization as another header "myauth". (I was able to do this since the request is coming from my own client.)
In the method request I added Authorization and myauth as HTTP Headers
Method Request - HTTP Headers
In the Integration Request - HTTP Headers I mapped myauth to Authorization before it was forwarded to the endpoint
Integration Request - HTTP Headers
Dont know if this is the best way to do this or if there could be any potential issues but this worked! Hope this helps someone or gives some ideas.