New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Token Introspection Policy to get client information from "oidc_issuer_endpoint". #755
Token Introspection Policy to get client information from "oidc_issuer_endpoint". #755
Conversation
@@ -21,6 +21,11 @@ | |||
"description": "Client Secret for the Token Introspection Endpoint", | |||
"type": "string" | |||
}, | |||
"use_oidc_issuer_endpoint": { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When this is true
we don't need ClientID + Secret anymore, right?
I wonder if it makes sense to use dynamic schema like in #741.
Using either https://github.com/mozilla-services/react-jsonschema-form#property-dependencies or https://github.com/mozilla-services/react-jsonschema-form#schema-dependencies could allow us to have two login methods: clientid+secret or automatic using oidc_issuer_endpoint.
@davidor wdyt?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When this is true we don't need ClientID + Secret anymore, right?
Yes. We don't need to define client_id
and client_secret
in the schema.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What I mean is having either client_id+client_secret or use_oidc_issuer_endpoint.
Try following schema:
{
"type": "object",
"properties": {
"auth_type": {
"type": "string",
"enum": [
"oidc_issuer_endpoint",
"client_id+client_secret"
],
"default": "client_id+client_secret"
}
},
"required": [
"auth_type"
],
"dependencies": {
"auth_type": {
"oneOf": [
{
"properties": {
"auth_type": {
"enum": [
"oidc_issuer_endpoint"
]
}
}
},
{
"properties": {
"auth_type": {
"enum": [
"client_id+client_secret"
]
},
"client_id": {
"type": "string"
},
"client_secret": {
"type": "string"
}
},
"required": [
"client_id",
"client_secret"
]
}
]
}
}
}
In https://mozilla-services.github.io/react-jsonschema-form/
That would allow user to choose which auth method to use and then fill the credentials.
It should also be extensible to add more properties in the future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed.
function _M:rewrite(context) | ||
if self.use_oidc_issuer_endpoint then | ||
local components = resty_url.parse(context.service.oidc.issuer_endpoint) | ||
set_client(self, components.user, components.password) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be easier to pass the headers into the individual http call instead of initializing new client every time. Then we won't mutate self
and initialize new http client on every request.
You can pass headers when making the request: https://github.com/3scale/apicast/blob/154dc5a4087c97ef9c46209ada6fc8d7d944fea6/spec/resty/http_ng_spec.lua#L78
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed.
@@ -55,7 +66,8 @@ local function introspect_token(self, token) | |||
|
|||
--- Parameters for the token introspection endpoint. | |||
-- https://tools.ietf.org/html/rfc7662#section-2.1 | |||
local res, err = self.http_client.post(self.introspection_url , { token = token, token_type_hint = 'access_token'}) | |||
local res, err = self.http_client.post{self.introspection_url , { token = token, token_type_hint = 'access_token'}, | |||
headers = {['User-Agent'] = user_agent(), ['Authorization'] = self.credential}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
User-Agent does not change, so it can be passed when creating the HTTP Client.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
function _M.new(config) | ||
local self = new(config) | ||
self.config = config or {} | ||
self.auth_type = config.auth_type or {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the default value for auth_type
is "client_id+client_secret", right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
9be11ca
to
da3a079
Compare
@@ -13,13 +13,10 @@ | |||
"description": "Introspection Endpoint URL", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When using the OIDC endpoint, this setting is also unnecessary, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mikz Regarding keycloak, the endpoint is http://${host}:${port}/auth/realms/${realm_name}/protocol/openid-connect/token/introspect
.
But this is not common to all.
I wonder whether it's good that this is dedicated to keycloak.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But this endpoint is discoverable through OIDC.
For example: https://keycloak.3sca.net/auth/realms/oidc-demo/.well-known/openid-configuration.
It should be possible to get it from context.proxy.oauth.config
. You can use require('resty.repl').start()
to start a REPL and see around the objects available.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed.
if self.auth_type == "oidc_issuer_endpoint" then | ||
local components = resty_url.parse(context.service.oidc.issuer_endpoint) | ||
self.credential = create_credential(components.user, components.password) | ||
self.introspection_url = context.proxy.oauth.config.openid.token_introspection_endpoint |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We probably should test how this policy behaves on non OIDC service. I think this would crash.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great change! Thank you so much @y-tabata ! 👍
Left few final comments regarding the schema naming.
"properties": { | ||
"auth_type": { | ||
"describe": "Get the client and the introspection end point from the oidc issuer endpoint", | ||
"enum": ["oidc_issuer_endpoint"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One final nitpick. oidc_issuer_endpoint
would imply the value is an endpoint, but in fact it is just a boolean flag. IMO better name would be use_3scale_oidc_issuer_endpoint
, use_service_oidc_issuer_endpoint
or something along those lines.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed.
"oneOf": [{ | ||
"properties": { | ||
"auth_type": { | ||
"describe": "Get the client and the introspection end point from the oidc issuer endpoint", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this could be improved a bit.
Use the Client credentials and the OIDC Issuer Endpoint from the OpenID Connect Issuer setting.
Still not perfect, but we can improve that later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed.
}, { | ||
"properties": { | ||
"auth_type": { | ||
"describe": "Specify the client ID and the client secret", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This probably should say something like:
Specify the Token Introspection Endpoint, Client ID, and Client Secret.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed.
bc91d90
to
20823bc
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
Since it is troublesome to define the
client_id
andclient_secret
twice in theAUTHENTICATION SETTINGS
and in thePOLICIES
of the Admin Portal, this PR adds "use_oidc_issuer_endpoint" property to get client information fromoidc_issuer_endpoint
.