THREESCALE-11015 - Support client_secret_jwt and private_key_jwt as authentication type for token introspection policy
Known issue
Some time failed with the following error
/opt/app-root/src/gateway/src/apicast/policy_loader.lua:98: Invalid config for policy: failed to validate dependent schema for "auth_type": value should match only one schema, but matches none
~~This is due to the use of oneOf and seems like a bug. I will need to investigate this further.~~
The original intention was to add support for a single algorithm and then upgrade lua-retsy-jwt afterwards and have a minimal schema structure change. However, the jsonschema validation fails when the emum field only contains a single value so I decided to remove it for now.
~~@eguzki if you know a better way to build apicast-config.json or how to solve this problem, please let me know~~
What
This PR mainly adding 2 new authentication method for token introspection policy, client_secret_jwt and private_key_jwt.
JIRA: https://issues.redhat.com/browse/THREESCALE-11015 Reference: https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication
Why only HS256 and RS256 are supported?
This is due to the version of lua-resty-jwt (0.20) that we use
Support sign algorithm
| RHKB 24 | lua-resty-jwt 0.20 | lua-resty-0.2.2 | lua-resty-0.2.3 | |
|---|---|---|---|---|
| client_secret_jwt | HS256HS384HS512 | HS256HS512 | HS256HS512 | HS256HS512 |
| private_key_jwt | RS256RS382RS512ES256ES384ES512PS256PS384PS512 | RS256 | RS256ES256 | RS256RS512ES256ES512 |
Highest we can go is 0.2.2 but this only add support for ES256 0.2.3 introduce a new dependency lua-resty-openssl. See https://github.com/3scale/APIcast/issues/1375#issuecomment-1281071040
Eventually we will need an update but given the amount of work involved, I'd like to keep version 0.20 for now
Verification steps:
Validate client_secret_jwt authentication method
- Checkout this PR
- Build a new runtime image
$ make rumtime-image IMAGE_NAME=apicast-test
- Navigate to keycloak dev-environment
$ cd dev-environments/keycloak-env
- Update apicast-config.json with the following
diff --git a/dev-environments/keycloak-env/apicast-config.json b/dev-environments/keycloak-env/apicast-config.json
index 071296cd..2ee79be1 100644
--- a/dev-environments/keycloak-env/apicast-config.json
+++ b/dev-environments/keycloak-env/apicast-config.json
@@ -90,6 +90,17 @@
"auth_type": "use_3scale_oidc_issuer_endpoint"
}
},
+ {
+ "name": "token_introspection",
+ "version": "builtin",
+ "configuration": {
+ "auth_type": "client_secret_jwt",
+ "client_id": "oidc-issuer-for-3scale",
+ "client_secret": "oidc-issuer-for-3scale-secret",
+ "client_jwt_assertion_audience": "http://keycloak:8080/realms/basic/protocol/openid-connect/token",
+ "introspection_url": "http://keycloak:8080/realms/basic/protocol/openid-connect/token/introspect"
+ }
+ },
{
"name": "apicast",
"version": "builtin",
- Start dev-environment
$ make gateway IMAGE_NAME=apicast-test
$ make keycloak-data
- Configure keycloak client to use
client-secret-jwt- Open keycloak dashboard in the browser at `http://127.0.0.1:9090
- Login with
admin/adminpass - Navigate to
basicrealm - Navigate to Client ->
oidc-issuer-for-3scale-> Credentials - Change
Client AuthenticatortoSigned JWT with Client Secretand useHS256algorithm - Click Save
- Get the access token
$ export ACCESS_TOKEN=$(make token)
- Run request
$ curl -v --resolve stg.example.com:8080:127.0.0.1 -H "Authorization: Bearer ${ACCESS_TOKEN}" "http://stg.example.com:8080"
- The response should be 200
Validate private_key_jwt authentication method
- Configure keycloak client to use
signed_jwt- Navigate to the keycloak dashboard
Client -> oidc-issuer-for-3scale -> Credentials - Change
Client AuthenticatortoSigned JWTwith RS256 signature algorithm - Navigate to
Keytab and click generate a new key - Select archive format
PCKS12and fill in required fill and save keystore file to local disk - From the downloaded
keystore.p12file, run the following command to extract private key
- Navigate to the keycloak dashboard
openssl pkcs12 -in keystore.p12 -nodes -nocerts | openssl rsa -out privatekey.rsa
- Encode certificate as base64
$ openssl base64 -A -in privatekey.rsa
- Stop dev-environment
CTRL-C
- Update apicast-config.json
diff --git a/dev-environments/keycloak-env/apicast-config.json b/dev-environments/keycloak-env/apicast-config.json
index 071296cd..a5ff3ffb 100644
--- a/dev-environments/keycloak-env/apicast-config.json
+++ b/dev-environments/keycloak-env/apicast-config.json
@@ -90,6 +90,19 @@
"auth_type": "use_3scale_oidc_issuer_endpoint"
}
},
+ {
+ "name": "token_introspection",
+ "version": "builtin",
+ "configuration": {
+ "auth_type": "private_key_jwt",
+ "client_id": "oidc-issuer-for-3scale",
+ "client_secret": "oidc-issuer-for-3scale-secret",
+ "client_jwt_assertion_audience": "http://keycloak:8080/realms/basic/protocol/openid-connect/token",
+ "introspection_url": "http://keycloak:8080/realms/basic/protocol/openid-connect/token/introspect",
+ "certificate_type": "embedded",
+ "certificate": "data:application/octet-stream;name=privatekey.rsa;base64,LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2d0lCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktrd2dnU2xBZ0VBQW9JQkFRRDQwNWl2OG5GNlNpTmIKUFVIOTdqVjVxNXliOVlDd3hXVGdQZVF1Y0l0c1RJWDNMcDF1aSs5TXZueDUzYStERGhmUjJ2SE5hcS90aXE1aQpsalNGL3V1TEZZdjhPOGc0eFVZUTVFTjFjR1VDSld1NkRTNmtDRFI2VjM0MVgxb0xpWUp4M05zbHdLdUcwcm5qCmpyZURpK2tXaHdrMWZYVE5LczJRSlI1U
npsbW9YMFN5Wnh5YzUyN2ZZYmdjRGlQUFI3RTZrT2wvdXdEMlI4bk0KY28xcE5EKzJvL2svMWtQV2dLNXlZeEswYmRRd2JpcENoKzBUaUlnSXBxS3h0WVlZVHZyalVoRjNUNHpCYXErNQpNZ1ljRjdFSlRJM3ZFaklodmFSN1ZjUjNsYW9QUU9KcCtQMmErUkZCN3IwMEVOTHduUmdyWXhHa1JtK3dUeDA5CjZnTnozTFh6QWdNQkFBRUNnZ0VBTmxjN29yMC9WQllwMTR5dXcwNkpCaXZZMVdTTnVNMDdKUS9QSytjdlE3VUkKa3IxdTYwd0xORWJyZDAvWE96ZFNNMlh0NWM4VllicW1MK2lleXQ2cndTR3hBeUpwTFNERUZ2OUt6alNBRXJKcQpidVRmR1RxamYwNXBS
UzJ3UkJIQlY2MkVmSit4dGcyQ1JEU1FWbDJ4UjFheFI2bkEzdWVvb2dEQk9OdG9VREVqCjJhMDNQd2M0ano1Qlo4RmZsQ3JPcmE5M2dlTlRpLzRRTUwrZDdlbWIrQjN3a2R4aFF4ZnUwdWtxeUl6c1VXTy8KV2plMzFQbWZRL0hKRUhtM3BUMklySTIva21lQjN4NzBkUmRLYnJEK2taMXFDK2JpOWhoZjUvZThVbzBWM0d5RgoxeDd5RXQ2VHY5aWk2UjZESVlUMDV1T1V1MUU2aXVOQmN3SzFBSWYwRlFLQmdRRDlrV3pOMzNUZVpKSFBFdjdMCmlQNmFEbjdDS1liWVRwcDVOT0dkL3Vab2Q4dTV1ZkZJRW1NV00yc0RMZEp3cGRIWVRpZENxSktlQlRkbC85ODUKcU9
WNHRLbmxNV2NZam96bysrT0JPc2U0cnd0NDNCMVRLNzRDdHRCYlZWVElPbnVqdlk4d2EvU0hiaDlESlNFLwpEcjdOeUxiWDloY3BLeHVIaHF0NWJqV1hiUUtCZ1FEN05vZWU5eVFSYklQbUxHRjI3QnFmenBXdjhERDNuUkdBCnFBWnY2VHdTblpUOTM5UVlzRi9ZLzIxdjVPamdGcGJERmt4S3lNMkJHV09sZ1RMaXZHUnIzZ2o3TGxMRUh6OTYKejRVSEUzVlpJbDVIY2hQemZHakpVVVJscXVxbk1MRHJ5ME1uZ1RIREhuME5KdXhJTm9xUXdPaHJpQ1FRSzVOMwoxelJoSVI1RzN3S0JnUURqd3VPYmpMTWFLL1c0cmRSR0dIaXhBb0lqZjArTExoZWM5YjRPdis1UU
9nSzVnZWJUCm1RaDk0Wk9tMkZybEtsenlVVWo4bkJTT2Noc1B1S1RXMHZuRDBXdWwzaGsvdXBPaGx0Z0V0VHEramlUYzI4SlAKZWNRRUJoZmpZaU4wY3V1cDZWUWI1MnhPMWNDby9Fbi9yUXdBSmVEdTNUSnluVER1TEM0TU5jMVhoUUtCZ1FDcwphVUZ0UGFzNGRpU1ViYk02dmxLTGlXbzhoUG5taDV0Q2xJOU9jV0cwV1FpdnNOWE5XQWVBVTlZVkxLTVRZUTE1CnVTMEZTb21ZYUFkMnlKUlcvdnRnK05OcktPRFBENjh1cDR4aVRkMkZIa3hjZHBQdzBWck5pSVFMenVFYmZCU0EKMEZFM3BMaTFkSkJZM1hUZkh1ZTg3MWpVckd3cjJPeHVISG9yaTJKUE93S0JnU
UNoTUxUUXFBNlE2Qk9CUFAxYQpsQlNSMG05RUJld0k2Y0s0QmhUNVl0U21JUjN3Kzk3OWN5eEJJQ0JVcHRMelEyVzMwM0syc1l2RXhkYzRTU2hVCkFuV01jZE5WWDhvQmc4bEFpZTFuaUFuTmFqb213SGxCV25OUUVUZjdTYmR6NHFFUFVFbFJRaXRId0VhUjdIbmQKMG1tQzRXcllqQUJVRDFUMlVUWXdQYWJmbWc9PQotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tCg=="
+ }
+ },
{
"name": "apicast",
"version": "builtin",
Replace the content after base64 with the based64 string of your certificate from previous step.
- Start gateway
$ make gateway IMAGE_NAME=apicast-test
- Run request
$ curl -v --resolve stg.example.com:8080:127.0.0.1 -H "Authorization: Bearer ${ACCESS_TOKEN}" "http://stg.example.com:8080"
- The response should be 200