From 20fff960f1fbce73ff691fa50c06b5836a127490 Mon Sep 17 00:00:00 2001 From: Hans Zandbelt Date: Fri, 14 Apr 2023 00:30:16 +0200 Subject: [PATCH] add support for resolving provider metadata from a Discovery endpoint - see https://github.com/OpenIDC/ngx_openidc_module/issues/18 - bump to 1.5.1dev Signed-off-by: Hans Zandbelt --- ChangeLog | 4 ++ configure.ac | 2 +- src/openidc/resolver.c | 131 +++++++++++++++++++++++++++++++++++++++++ test/check_openidc.c | 39 ++++++++++++ 4 files changed, 175 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index cb3f7df..d139bc5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +04/14/2023 +- add support for resolving provider metadata from a Discovery endpoint URL; see https://github.com/OpenIDC/ngx_openidc_module/issues/18 +- bump to 1.5.1dev + 03/22/2023 - add error logs about missing or invalid "active" boolean claim in introspection response diff --git a/configure.ac b/configure.ac index fe3c62b..b02158b 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -AC_INIT([liboauth2],[1.5.0],[hans.zandbelt@openidc.com]) +AC_INIT([liboauth2],[1.5.1dev],[hans.zandbelt@openidc.com]) AM_INIT_AUTOMAKE([foreign no-define subdir-objects]) AC_CONFIG_MACRO_DIR([m4]) diff --git a/src/openidc/resolver.c b/src/openidc/resolver.c index 1f2de33..39a50bd 100644 --- a/src/openidc/resolver.c +++ b/src/openidc/resolver.c @@ -215,6 +215,135 @@ static char *_oauth2_cfg_openidc_provider_resolver_file_set_options( return NULL; } +_OAUTH2_CFG_CTX_TYPE_START(oauth2_openidc_provider_resolver_url_ctx) +oauth2_cfg_endpoint_t *endpoint; +_OAUTH2_CFG_CTX_TYPE_END(oauth2_openidc_provider_resolver_url_ctx) + +_OAUTH2_CFG_CTX_INIT_START(oauth2_openidc_provider_resolver_url_ctx) +ctx->endpoint = NULL; +_OAUTH2_CFG_CTX_INIT_END + +_OAUTH2_CFG_CTX_CLONE_START(oauth2_openidc_provider_resolver_url_ctx) +dst->endpoint = oauth2_cfg_endpoint_clone(log, src->endpoint); +_OAUTH2_CFG_CTX_CLONE_END + +_OAUTH2_CFG_CTX_FREE_START(oauth2_openidc_provider_resolver_url_ctx) +if (ctx->endpoint) + oauth2_cfg_endpoint_free(log, ctx->endpoint); +_OAUTH2_CFG_CTX_FREE_END + +_OAUTH2_CFG_CTX_FUNCS(oauth2_openidc_provider_resolver_url_ctx) + +static bool _oauth2_openidc_provider_resolve_url_exec( + oauth2_log_t *log, oauth2_openidc_provider_resolver_url_ctx_t *ctx, + char **s_json) +{ + bool rc = false; + oauth2_http_call_ctx_t *http_ctx = NULL; + char *s_response = NULL; + oauth2_uint_t status_code = 0; + + oauth2_debug(log, "enter"); + + http_ctx = oauth2_http_call_ctx_init(log); + if (http_ctx == NULL) + goto end; + + if (oauth2_http_call_ctx_ssl_verify_set( + log, http_ctx, + oauth2_cfg_endpoint_get_ssl_verify(ctx->endpoint)) == false) + goto end; + + if (oauth2_http_call_ctx_timeout_set( + log, http_ctx, + oauth2_cfg_endpoint_get_http_timeout(ctx->endpoint)) == false) + goto end; + oauth2_http_call_ctx_outgoing_proxy_set( + log, http_ctx, + oauth2_cfg_endpoint_get_outgoing_proxy(ctx->endpoint)); + + if (oauth2_http_get(log, oauth2_cfg_endpoint_get_url(ctx->endpoint), + NULL, http_ctx, s_json, &status_code) == false) + goto end; + + if ((status_code < 200) || (status_code >= 300)) { + goto end; + } + + rc = true; + +end: + + if (s_response) + oauth2_mem_free(s_response); + if (http_ctx) + oauth2_http_call_ctx_free(log, http_ctx); + + oauth2_debug(log, "leave: %d", rc); + + return rc; +} + +static bool _oauth2_openidc_provider_resolve_url( + oauth2_log_t *log, const oauth2_cfg_openidc_t *cfg, + const oauth2_http_request_t *request, char **s_json) +{ + bool rc = false; + oauth2_openidc_provider_resolver_url_ctx_t *ctx = NULL; + + oauth2_debug(log, "enter"); + + ctx = (oauth2_openidc_provider_resolver_url_ctx_t *) + cfg->provider_resolver->ctx->ptr; + if (ctx->endpoint == NULL) + goto end; + + if (_oauth2_openidc_provider_resolve_url_exec(log, ctx, s_json) == + false) + goto end; + + rc = true; + +end: + + oauth2_debug(log, "leave: %d", rc); + + return rc; +} + +static char *_oauth2_cfg_openidc_provider_resolver_url_set_options( + oauth2_log_t *log, const char *value, const oauth2_nv_list_t *params, + void *c) +{ + oauth2_cfg_openidc_t *cfg = (oauth2_cfg_openidc_t *)c; + char *rv = NULL; + + // TODO: macroize? + cfg->provider_resolver = oauth2_cfg_openidc_provider_resolver_init(log); + cfg->provider_resolver->callback = _oauth2_openidc_provider_resolve_url; + cfg->provider_resolver->ctx->callbacks = + &oauth2_openidc_provider_resolver_url_ctx_funcs; + cfg->provider_resolver->ctx->ptr = + cfg->provider_resolver->ctx->callbacks->init(log); + + // TODO: factor out? + oauth2_openidc_provider_resolver_url_ctx_t *ctx = + (oauth2_openidc_provider_resolver_url_ctx_t *) + cfg->provider_resolver->ctx->ptr; + + ctx->endpoint = oauth2_cfg_endpoint_init(log); + rv = oauth2_cfg_set_endpoint(log, ctx->endpoint, value, params, NULL); + if (rv) + goto end; + + cfg->provider_resolver->cache = + oauth2_cache_obtain(log, oauth2_nv_list_get(log, params, "cache")); + +end: + + return rv; +} + // DIR static char *_oauth2_cfg_openidc_provider_resolver_dir_set_options( @@ -286,12 +415,14 @@ static char *_oauth2_cfg_openidc_provider_resolver_string_set_options( #define OAUTH2_OPENIDC_PROVIDER_RESOLVER_STR_STR "string" #define OAUTH2_OPENIDC_PROVIDER_RESOLVER_FILE_STR "file" #define OAUTH2_OPENIDC_PROVIDER_RESOLVER_DIR_STR "dir" +#define OAUTH2_OPENIDC_PROVIDER_RESOLVER_URL_STR "url" // clang-format off static oauth2_cfg_set_options_ctx_t _oauth2_cfg_resolver_options_set[] = { { OAUTH2_OPENIDC_PROVIDER_RESOLVER_STR_STR, _oauth2_cfg_openidc_provider_resolver_string_set_options }, { OAUTH2_OPENIDC_PROVIDER_RESOLVER_FILE_STR, _oauth2_cfg_openidc_provider_resolver_file_set_options }, { OAUTH2_OPENIDC_PROVIDER_RESOLVER_DIR_STR, _oauth2_cfg_openidc_provider_resolver_dir_set_options }, + { OAUTH2_OPENIDC_PROVIDER_RESOLVER_URL_STR, _oauth2_cfg_openidc_provider_resolver_url_set_options }, { NULL, NULL } }; // clang-format on diff --git a/test/check_openidc.c b/test/check_openidc.c index f4edf2e..6586f66 100644 --- a/test/check_openidc.c +++ b/test/check_openidc.c @@ -106,6 +106,7 @@ static cjose_jwk_t *jwk_rsa = NULL; static char *jwks_uri_path = "/jwks_uri"; static char *token_endpoint_path = "/token"; static char *userinfo_endpoint_path = "/userinfo"; +static char *discovery_endpoint_path = "/.well-known/openid-configuration"; static cjose_jwk_t *oauth2_jwk_rsa_get() { @@ -137,6 +138,10 @@ static char *oauth2_check_openidc_serve_get(const char *request) strlen(userinfo_endpoint_path)) == 0) { rv = oauth2_strdup("{ \"sub\": \"myclient\", " "\"myuserinfoclaim\": \"somevalue\" }"); + } else if (strncmp(request, discovery_endpoint_path, + strlen(discovery_endpoint_path)) == 0) { + rv = + oauth2_strdup("{ \"issuer\": \"https://op.example.org\" }"); } else { rv = oauth2_strdup("problem"); } @@ -527,6 +532,39 @@ static void _test_openidc_resolve_to_false(oauth2_cfg_openidc_t *c, ck_assert_ptr_eq(NULL, provider); } +START_TEST(test_openidc_resolver_url) +{ + bool rc = false; + char *rv = NULL; + oauth2_cfg_openidc_t *c = NULL; + oauth2_http_request_t *r = NULL; + oauth2_openidc_provider_t *provider = NULL; + char *discovery_uri = oauth2_stradd(NULL, oauth2_check_http_base_url(), + discovery_endpoint_path, NULL); + + c = oauth2_cfg_openidc_init(_log); + r = oauth2_http_request_init(_log); + + rv = oauth2_cfg_openidc_provider_resolver_set_options( + _log, c, "url", discovery_uri, NULL); + ck_assert_ptr_eq(rv, NULL); + + rc = _oauth2_openidc_provider_resolve(_log, c, r, NULL, &provider); + ck_assert_int_eq(rc, true); + ck_assert_ptr_ne(NULL, provider); + ck_assert_str_eq("https://op.example.org", + oauth2_openidc_provider_issuer_get(_log, provider)); + oauth2_openidc_provider_free(_log, provider); + provider = NULL; + + oauth2_openidc_provider_free(_log, provider); + provider = NULL; + + oauth2_mem_free(discovery_uri); + oauth2_http_request_free(_log, r); + oauth2_cfg_openidc_free(_log, c); +} + START_TEST(test_openidc_resolver) { bool rc = false; @@ -1103,6 +1141,7 @@ Suite *oauth2_check_openidc_suite() tcase_add_test(c, test_openidc_handle_cookie); tcase_add_test(c, test_openidc_handle_cache); tcase_add_test(c, test_openidc_state_cookie); + tcase_add_test(c, test_openidc_resolver_url); tcase_set_timeout(c, 8);