Add support for JWKS based OAuth Token validation.

Keycloak only enables the introspection API when using a 'confidential'
client.  This means that for user initiated OAuth2 tokens the user would
need to know the secrets for a client (ie the confidential piece).

With Keycloak to use a 'public' client, where the user doesn't need to
know a client secret, you need to use a JWKS to validate the JWT tokens
instead of the introspection endpoint.  OIDCOAuthVerifyJwksUri tells
mod_auth_openidc where to find that certificate.  You also need to
*remove* OIDCOAuthIntrospectionEndpoint as this conflicts with
OIDCOAuthVerifyJwksUri

Change-Id: I95220db87f61ca19e50c64b4f799d3973b256858
This commit is contained in:
Mark Chappell 2020-03-06 16:06:06 +01:00
parent 7586bc88c1
commit 6d6860ac49
3 changed files with 55 additions and 5 deletions

View File

@ -61,9 +61,23 @@
#
# [*openidc_enable_oauth*]
# (Optional) Set to true to enable oauthsupport.
# Defaults to false.
#
# [*openidc_introspection_endpoint*]
# (Required if oauth is enabled) Oauth introspection endpoint url.
# (Required if oauth is enabled and configured for introspection)
# OAuth introspection endpoint url.
# Defaults to undef.
#
# [*openidc_verify_jwks_uri*]
# (Required if oauth is enabled and configured for JWKS based validation)
# The JWKS URL on which the Identity Provider
# publishes the keys used to sign its JWT access tokens.
# Defaults to undef.
#
# [*openidc_verify_method*]
# (Optional) The method used to verify OAuth tokens.
# Must be one of introspection or jwks
# Defaults to introspection
#
# [*memcached_servers*]
# (Optional) A list of memcache servers. Defaults to undef.
@ -80,6 +94,7 @@
# [*remote_id_attribute*]
# (Optional) Value to be used to obtain the entity ID of the Identity
# Provider from the environment.
# Defaults to undef.
#
# [*template_order*]
# This number indicates the order for the concat::fragment that will apply
@ -112,6 +127,8 @@ class keystone::federation::openidc (
$openidc_cache_clean_interval = undef,
$openidc_enable_oauth = false,
$openidc_introspection_endpoint = undef,
$openidc_verify_jwks_uri = undef,
$openidc_verify_method = 'introspection',
$memcached_servers = undef,
$redis_server = undef,
$redis_password = undef,
@ -124,8 +141,21 @@ class keystone::federation::openidc (
include keystone::deps
include keystone::params
if $openidc_enable_oauth and !$openidc_introspection_endpoint {
fail('You must set openidc_introspection_endpoint when enabling oauth support')
if !($openidc_verify_method in ['introspection', 'jwks']) {
fail('Unsupported token verification method.' +
' Must be one of "introspection" or "jwks"')
}
if ($openidc_verify_method == 'introspection') {
if $openidc_enable_oauth and !$openidc_introspection_endpoint {
fail('You must set openidc_introspection_endpoint when enabling oauth support' +
' and introspection.')
}
} elsif ($openidc_verify_method == 'jwks') {
if $openidc_enable_oauth and !$openidc_verify_jwks_uri {
fail('You must set openidc_verify_jwks_uri when enabling oauth support' +
' and local signature verification using a JWKS URL')
}
}
$memcached_servers_real = join(any2array($memcached_servers), ' ')

View File

@ -94,7 +94,7 @@ describe 'keystone::federation::openidc' do
end
end
context 'with oauth enabled' do
context 'with oauth and introspection enabled' do
before do
params.merge!({
:openidc_enable_oauth => true,
@ -102,7 +102,7 @@ describe 'keystone::federation::openidc' do
})
end
it 'should contain oauth config' do
it 'should contain oauth and introspection config' do
content = get_param('concat::fragment', 'configure_openidc_keystone', 'content')
expect(content).to match('OIDCOAuthClientID "openid_client_id"')
expect(content).to match('OIDCOAuthClientSecret "openid_client_secret"')
@ -111,6 +111,22 @@ describe 'keystone::federation::openidc' do
end
end
context 'with oauth and jwks enabled' do
before do
params.merge!({
:openidc_enable_oauth => true,
:openidc_verify_method => 'jwks',
:openidc_verify_jwks_uri => 'http://example.com',
})
end
it 'should contain oauth and jwks config' do
content = get_param('concat::fragment', 'configure_openidc_keystone', 'content')
expect(content).to match('OIDCOAuthVerifyJwksUri "http://example.com"')
expect(content).to match('/v3/OS-FEDERATION/identity_providers/myidp/protocols/openid/auth')
end
end
context 'with remote id attribute' do
before do
params.merge!({

View File

@ -48,9 +48,13 @@
</LocationMatch>
<%- if scope['::keystone::federation::openidc::openidc_enable_oauth'] -%>
<%- if scope['keystone::federation::openidc::openidc_verify_method'] == 'introspection' -%>
OIDCOAuthClientID "<%= scope['keystone::federation::openidc::openidc_client_id']-%>"
OIDCOAuthClientSecret "<%= scope['keystone::federation::openidc::openidc_client_secret']-%>"
OIDCOAuthIntrospectionEndpoint "<%= scope['keystone::federation::openidc::openidc_introspection_endpoint']-%>"
<%- elsif scope['keystone::federation::openidc::openidc_verify_method'] == 'jwks' -%>
OIDCOAuthVerifyJwksUri "<%= scope['keystone::federation::openidc::openidc_verify_jwks_uri']-%>"
<%- end -%>
<Location ~ "/v3/OS-FEDERATION/identity_providers/<%= scope['keystone::federation::openidc::idp_name']-%>/protocols/openid/auth">
AuthType oauth20