Authenticate postgrest api through keycloak - postgresql

Postgrest APIs can be secured through inbuilt JWT tokens or through a third party service like Auth0, Okta or Keycloak (http://postgrest.org/en/v5.0/install.html)
We want to consume JWT provided by a Keycloak only , but there is very limited document available.
Can anybody guide me how can I secure postgrest apis through keycloak ?
thanks

UPDATE: Simple method with curl examples
Install keycloak, add realm and client
get the keys
curl https://$KEYCLOAK_URL/auth/realms/$REALM/protocol/openid-connect/certs
copy the first element of the keys array and use it in the postgrest configuration for the jwt-secret variable
use .preferred_username as the value of role-claim-key in postgrest configuration
Assuming you 've done the above you can test your installation:
Get token
curl -X POST https://$KEYCLOAK_URL/auth/realms/$REALM/protocol/openid-connect/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=$USERNAME" \
-d "password=$PASSWORD" \
-d 'grant_type=password' \
-d "client_id=$CLIENT"
Use the access_token element of the reply for your requests to postgrest
curl -H "Authorization: Bearer $ACCESS_TOKEN" $POSTGREST_URL/your_table
There are several ways to do it, I'll just describe one:
Set up keycloak to pass the claims you want in the token. ( Probably you would want a claim of type "role": "username" )
Get the key keycloak is using and pass it in the jwt-secret section of the postgrest configuration
Set up a web server to communicate with keycloak and get the tokens.
Pass the token to the browser
Use the token to access postgrest
Details
In keycloak admin console go to: /#/realms/<realm name>/clients/<client id>/mappers and set the claims you want
In keycloak admin console at #/realms/<realm name>/keys you can get your rsa public key, translate it to jwk format and save it to the postgrest configuration. In order to translate it to jwk
Translate it to pem format by enclosing it in -----BEGIN PUBLIC KEY-----
-----END PUBLIC KEY-----
Translate it to jwk format. A nice tool pem-jwk
There are infinite options. A node js example: https://github.com/keycloak/keycloak-nodejs-connect/tree/master/example that uses the keycloak-connect package
You can read the token at req.kauth.grant and place it at a hidden field of the html you send to the browser
From the browser read the token and place it at the authentication header with the bearer prefix. If you use axios:
axios({
method: 'get',
url: 'your url',
headers: { 'authorization': `Bearer ${token}` }
})

Related

Can I login to the Vault UI using a JWT bearer token in the Authorization header?

Vault supports logging in using a JWT. I have a proxy in front of my Vault instance which manages an OIDC flow and injects a JWT as a bearer token in the Authorization header.
Instead of being presented with the Vault login screen, it would be convenient if Vault could parse the Authorization header and automatically log me in -- is this possible?
I know Vault supports Vault tokens in the Authorization header, but since I access my other dashboards using this JWT, it would be powerful for Vault to also interpret it.
As far as i know: no, not in the usual way with http-header authorization.
Vault would accept the jwt-token from oidc for login if the jwt-token comes as body/payload in json like this:
POST /v1/auth/jwt/login
{"jwt":"YOUR.JWT.TOKEN"}
Like written in vault docs:
curl --request POST \
--data '{"jwt": "YOUR.JWT.TOKEN", "role": "demo"}' \
http://127.0.0.1:8200/v1/auth/jwt/login
As header vault accepts the http-header X-Vault-Token: s.XYZYXr3kuxR4, which is a vault-token and not the oidc-jwt-token

Access Keycloak REST Admin API using a service account (client credential grant)

I like to manage keycloak from my own application:create user & clients, display users & client. As this is not a real user but a machine I would like to use a service account with a client credential grant as proposed in How to get Keycloak users via REST without admin account . To realize this I:
create a realm
inside the real created a client
configured the access type of the client to "confidential" saved and activated the "Service Accounts Enabled" option that will apear after the save.
enable under scopes the client-roles of the "real-management" (see screenshot)
requested an access token with the "username:password" base64 encoded in the header
curl -X POST 'http://accounts.d10l.de/auth/realms/d10l/protocol/openid-connect/token' \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "Authorization: Basic ZGV2ZWxvcGVyLXBvcnRhbDpmZGRmYzM4Yy05MzAyLTRlZmQtYTM3Yy1lMWFmZGEyMmRhMzc=" \
-d 'grant_type=client_credentials' \
| jq -r '.access_token'
Try to access the users using the access token:
curl -I GET 'http://accounts.d10l.de/auth/admin/realms/d10l/users/' \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICIxRVdoNENFUjIweDY5SlBCekU4dU9GdXF4R2NVNlVfWmpTNTQ5bmd2QjNjIn0.eyJqdGkiOiI0NDM0ZDFhNS0xZTA5LTQ4MzQtYWI2Yy0zOTk1YmEwMTgxMzAiLCJleHAiOjE1MzY0MzYwMDEsIm5iZiI6MCwiaWF0IjoxNTM2NDM1NzAxLCJpc3MiOiJodHRwOi8vYWNjb3VudHMuZDEwbC5kZS9hdXRoL3JlYWxtcy9kMTBsIiwiYXVkIjoiZGV2ZWxvcGVyLXBvcnRhbCIsInN1YiI6IjliYWI0YWM1LTRiNWMtNGIxOS05ZTc3LWFjOWFmNzlkNzFhZiIsInR5cCI6IkJlYXJlciIsImF6cCI6ImRldmVsb3Blci1wb3J0YWwiLCJhdXRoX3RpbWUiOjAsInNlc3Npb25fc3RhdGUiOiIyOWM2YWI3Mi05N2RiLTQ2NWUtYTE1Yy03ZWE5NzA0NmZlYzQiLCJhY3IiOiIxIiwiYWxsb3dlZC1vcmlnaW5zIjpbXSwicmVzb3VyY2VfYWNjZXNzIjp7fSwic2NvcGUiOiJlbWFpbCBwcm9maWxlIiwiY2xpZW50SWQiOiJkZXZlbG9wZXItcG9ydGFsIiwiY2xpZW50SG9zdCI6IjE3Mi4xNy4wLjEiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInByZWZlcnJlZF91c2VybmFtZSI6InNlcnZpY2UtYWNjb3VudC1kZXZlbG9wZXItcG9ydGFsIiwiY2xpZW50QWRkcmVzcyI6IjE3Mi4xNy4wLjEiLCJlbWFpbCI6InNlcnZpY2UtYWNjb3VudC1kZXZlbG9wZXItcG9ydGFsQHBsYWNlaG9sZGVyLm9yZyJ9.D_XnpF1rwCayup8h4UXM4AGWkY_xQo40X-yIlWhmqaxkVh1FQy24932VDRCAmxYHcrwazRMqO7snXmre3_8YF5R9Dt8GYjiBorECvQ9X_nBwunmHqnGxIeE64c2GXiz6zSjdgQJQE8fH10NsLyFWHQ-lBPsBwZBsrkKQ5QUEU2qjE7rDRPtYLJPB94BSE4QGfedmRIbvg39snVkClBDUmuBTq_Rc4p7kV69h0a2Mb1sgEr3MdB4RcsOe3gJPZVVtu7gZuGqcAQKMYgtybArF3OXz37w8hjUp6FABxDcvY7K-jsGxXn0hSU0OB7wxAWY9vP4ar4tQYlKxNjs46rPLWw"
But the response is a 403:
url: (6) Could not resolve host: GET
HTTP/1.1 403 Forbidden
content-length: 0
date: Sat, 08 Sep 2018 19:42:06 GMT
How/Is it possible accessing the Admin REST API from a new service account through a client credential grant?
Keycloak differentiates between the Scopes/Scope mapping & the roles management.
The Scopes tab: you see in the question above only manages the roles that a client is allowed to request.
For the client credential grant to work these roles must be assigned to the client in the "Service Account Roles" Tab.
So in the end the client receive a token that is the intersection of both of those configurations.
Source: https://www.keycloak.org/docs/latest/server_admin/index.html#_service_accounts

Keycloak - How to request a token with a custom lifespan?

Context: We are using Keycloak to secure our APIs by usually passing tokens through Authorization Headers. However, these APIs also allow users to download files (for instance: https://api.service.io/users.xlsx).
To use these "download endpoints", our web client applications pass users' token via query strings. (e.g. https://api.service.io/users.xlsx?accessToken=${bearerToken})).
Problem: Passing tokens via query string has several security flaws (browser history, ...). Therefore we would like to pass a very short-lived token (e.g. lifespan of 15sec) instead of the normal one (lifespan of 300sec by default).
Question: How could we request a different token from Keycloak API (for instance, /realms/#{realm_id}/protocol/openid-connect/token) by:
providing the normal access token (not credentials);
and specifying a different lifespan ?
After reading Keycloak's source code, it appears this is not possible (version 3.4.2.Final) to ask for a specific lifespan at runtime.
However, I developed a Keycloak Custom REST endpoint to do that. https://github.com/looorent/keycloak-configurable-token-api
When this JAR file is deployed in Keycloak, you can ask for a given lifespan at runtime. For example:
$ curl -X POST -d '{ "tokenLifespanInSeconds": 20}' -H "Content-Type: application/json" -H "Authorization: Bearer <user-access-token>" http://auth.service.io/auth/realms/a-realm/configurable-token

Basic HTTP and Bearer Token Authentication

I am currently developing a REST-API which is HTTP-Basic protected for the development environment. As the real authentication is done via a token, I'm still trying to figure out, how to send two authorization headers.
I have tried this one:
curl -i http://dev.myapp.com/api/users \
-H "Authorization: Basic Ym9zY236Ym9zY28=" \
-H "Authorization: Bearer mytoken123"
I could for example disable the HTTP-Authentication for my IP but as I usually work in different environments with dynamic IPs, this is not a good solution. So am I missing something?
Try this one to push basic authentication at url:
curl -i http://username:password#dev.myapp.com/api/users -H "Authorization: Bearer mytoken123"
^^^^^^^^^^^^^^^^^^
If above one doesn't work, then you have nothing to do with it. So try the following alternates.
You can pass the token under another name. Because you are handling the authorization from your Application. So you can easily use this flexibility for this special purpose.
curl -i http://dev.myapp.com/api/users \
-H "Authorization: Basic Ym9zY236Ym9zY28=" \
-H "Application-Authorization: mytoken123"
Notice I have changed the header into Application-Authorization. So from your application catch the token under that header and process what you need to do.
Another thing you can do is, to pass the token through the POST parameters and grab the parameter's value from the Server side. For example passing token with curl post parameter:
-d "auth-token=mytoken123"
Standard (https://www.rfc-editor.org/rfc/rfc6750) says you can use:
Form-Encoded Body Parameter: Authorization: Bearer mytoken123
URI Query Parameter: access_token=mytoken123
So it's possible to pass many Bearer Token with URI, but doing this is discouraged (see section 5 in the standard).
If you are using a reverse proxy such as nginx in between, you could define a custom token, such as X-API-Token.
In nginx you would rewrite it for the upstream proxy (your rest api) to be just auth:
proxy_set_header Authorization $http_x_api_token;
... while nginx can use the original Authorization header to check HTTP AUth.
With nginx you can send both tokens like this (even though it's against the standard):
Authorization: Basic basic-token,Bearer bearer-token
This works as long as the basic token is first - nginx successfully forwards it to the application server.
And then you need to make sure your application can properly extract the Bearer from the above string.
I had a similar problem - authenticate device and user at device. I used a Cookie header alongside an Authorization: Bearer... header. One header authenticated the device, the other authenticated the user. I used a Cookie header because these are commonly used for authentication.
curl --anyauth
Tells curl to figure out authentication method by itself, and use the
most secure one the remote site claims to support. This is done by
first doing a request and checking the response- headers, thus
possibly inducing an extra network round-trip. This is used
instead of setting a specific authentication method, which you can
do with --basic, --digest, --ntlm, and
--negotiate.
There is another solution for testing APIs on development server.
Set HTTP Basic Authentication only for web routes
Leave all API routes free from authentication
Web server configuration for nginx and Laravel would be like this:
location /api {
try_files $uri $uri/ /index.php?$query_string;
}
location / {
try_files $uri $uri/ /index.php?$query_string;
auth_basic "Enter password";
auth_basic_user_file /path/to/.htpasswd;
}
Authorization: Bearer will do the job of defending the development server against web crawlers and other unwanted visitors.
You can use Body with x-www-form-url-encoded to send with multiple headers.
curl --location --request POST 'http://dev.myapp.com/api/users' \
--header 'Authorization: Basic Ym9zY236Ym9zY28=' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'access_token=mytoken123'

OpenId via Curl

How can I do OpenId based authentication using Curl?
At first place can I do it?
Regards,
Allahbaksh
I suppose you are talking about the curl command line, not the library. I have not tried, but according to what I know of OpenID and curl, it should be possible. However, not fully automated. You'll have to "parse" the content of the identity provider and of the content provider login pages if you want to be realy restful and generic. If you know where you're going and don't mind to couple your service and client (no hateoas), you can first authenticate with the identity provider, e.g.:
curl -iSsL --user-agent 'Mozilla/5.0' --cookie cookies --cookie-jar cookies \
--data login=$mylogin \
--data passwd=$mypasswd \
https://identprovider.example.com/login
and then post your OpenID to the content provider:
curl -iSsL --user-agent 'Mozilla/5.0' --cookie cookies --cookie-jar cookies \
--data openid="$myopenidurl" \
http://contentprovider.example.com/login
This suppose that the content provider is already authorised to use the identity provider.
Then get your content:
curl -iSsL --user-agent 'Mozilla/5.0' --cookie cookies --cookie-jar cookies \
http://contentprovider.example.com/interesting/content
Note that this approach is not restful, since I hard encoded the POST uris and fields in the code. To decouple the client and the server, the uris and field names must be extracted from responses. In a bash script, you can use sed for example.
I think is should work, but if not, then you'll have to realy follow the redirects and extract URIs and forms, since some params can be passed in the redirect URIs or in hidden form fields.