Skip to content

Commit 369633e

Browse files
committed
add OIDCProfile to configure OpenID Connect profile behaviours
i.e. FAPI 2.0 Signed-off-by: Hans Zandbelt <[email protected]>
1 parent b134a74 commit 369633e

File tree

16 files changed

+214
-41
lines changed

16 files changed

+214
-41
lines changed

ChangeLog

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
01/29/2025
2+
- add OIDCProfile to configure OpenID Connect profile behaviours for, so far "FAPI20" only, which configures:
3+
Authentication Request method, DPoP, PKCE, ID token aud values requirements
4+
token endpoint JWT authentication "aud" values, "iss" parameter requirement in authentication reponses
5+
16
01/24/2025
27
- fix OIDCProviderRevocationEndpoint when not setting it to ""; see #1301; thanks @tarteens
38

Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ libauth_openidc_la_SOURCES = \
3636
src/proto/jwks.c \
3737
src/proto/jwt.c \
3838
src/proto/pkce.c \
39+
src/proto/profile.c \
3940
src/proto/proto.c \
4041
src/proto/request.c \
4142
src/proto/response.c \

auth_openidc.conf

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,19 @@
372372
# NB: this can be overridden on a per-OP basis in the .conf file using the key: userinfo_encrypted_response_enc
373373
#OIDCUserInfoEncryptedResponseEnc [A128CBC-HS256|A256CBC-HS512|A256GCM]
374374

375+
# The OpenID Connect (client) profile to adhere to, which configures settings for:
376+
# - Authentication Request method
377+
# - DPoP
378+
# - PKCE
379+
# - ID token aud values
380+
# - token endpoint JWT authentication "aud" values,
381+
# - "iss" parameter requirement in authentication reponses
382+
# FAPI20: configures settings for the FAPI 2.0 Security Profile i.e :
383+
Auth Request Method: PAR, DPoP: Required, PCKE: S256, aud: client_id, aud: iss, iss: true
384+
# OIDC10: adheres to the core OpenID Connect spec v1.0
385+
# When not default the default is OIDC10
386+
#OIDCProfile [ OIDC10 | FAPI20 ]
387+
375388
########################################################################################
376389
#
377390
# WARNING:

src/cfg/cmds.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,11 @@ const command_rec oidc_cfg_cmds[] = {
594594
OIDCProviderAuthRequestMethod,
595595
auth_request_method,
596596
"HTTP method used to send the authentication request to the provider (GET or POST)."),
597+
OIDC_CFG_CMD_PROVIDER(
598+
AP_INIT_TAKE1,
599+
OIDCProfile,
600+
profile,
601+
"OpenID Connect Profile."),
597602

598603
// oauth
599604

src/cfg/provider.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ struct oidc_provider_t {
9696
oidc_userinfo_token_method_t userinfo_token_method;
9797
char *request_object;
9898
oidc_auth_request_method_t auth_request_method;
99+
oidc_profile_t profile;
99100
int response_require_iss;
100101
};
101102

@@ -669,6 +670,17 @@ const char *oidc_cfg_provider_revocation_endpoint_url_get(oidc_provider_t *provi
669670
return provider->revocation_endpoint_url;
670671
}
671672

673+
#define OIDC_PROFILE_OIDC10_STR "OIDC10"
674+
#define OIDC_PROFILE_FAPI20_STR "FAPI20"
675+
676+
const char *oidc_cfg_provider_parse_profile(apr_pool_t *pool, const char *arg, oidc_profile_t *profile) {
677+
static const oidc_cfg_option_t options[] = {{OIDC_PROFILE_OIDC10, OIDC_PROFILE_OIDC10_STR},
678+
{OIDC_PROFILE_FAPI20, OIDC_PROFILE_FAPI20_STR}};
679+
return oidc_cfg_parse_option(pool, options, OIDC_CFG_OPTIONS_SIZE(options), arg, (int *)profile);
680+
}
681+
#define OIDC_DEFAULT_PROFILE OIDC_PROFILE_OIDC10
682+
OIDC_PROVIDER_MEMBER_FUNCS_STR_INT(profile, oidc_cfg_provider_parse_profile, oidc_profile_t, OIDC_DEFAULT_PROFILE)
683+
672684
/*
673685
* base
674686
*/
@@ -732,6 +744,7 @@ static void oidc_cfg_provider_init(oidc_provider_t *provider) {
732744
provider->response_require_iss = OIDC_CONFIG_POS_INT_UNSET;
733745

734746
provider->id_token_aud_values = NULL;
747+
provider->profile = OIDC_CONFIG_POS_INT_UNSET;
735748
}
736749

737750
void oidc_cfg_provider_merge(apr_pool_t *pool, oidc_provider_t *dst, const oidc_provider_t *base,
@@ -846,6 +859,7 @@ void oidc_cfg_provider_merge(apr_pool_t *pool, oidc_provider_t *dst, const oidc_
846859

847860
dst->id_token_aud_values =
848861
add->id_token_aud_values != NULL ? add->id_token_aud_values : base->id_token_aud_values;
862+
dst->profile = add->profile != OIDC_CONFIG_POS_INT_UNSET ? add->profile : base->profile;
849863
}
850864

851865
oidc_provider_t *oidc_cfg_provider_create(apr_pool_t *pool) {

src/cfg/provider.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,11 @@ typedef struct oidc_jwks_uri_t {
9393
apr_array_header_t *jwk_list;
9494
} oidc_jwks_uri_t;
9595

96+
typedef enum {
97+
OIDC_PROFILE_OIDC10 = 1,
98+
OIDC_PROFILE_FAPI20 = 2,
99+
} oidc_profile_t;
100+
96101
// NB: need the primitive strings and the declarations of the custom
97102
// set routines here because the commands are included in config.c.
98103
// via include "cmds.inc"
@@ -115,6 +120,7 @@ typedef struct oidc_jwks_uri_t {
115120
#define OIDCProviderVerifyCertFiles "OIDCProviderVerifyCertFiles"
116121
#define OIDCResponseType "OIDCResponseType"
117122
#define OIDCProviderAuthRequestMethod "OIDCProviderAuthRequestMethod"
123+
#define OIDCProfile "OIDCProfile"
118124
#define OIDCPKCEMethod "OIDCPKCEMethod"
119125
#define OIDCDPoPMode "OIDCDPoPMode"
120126
#define OIDCResponseMode "OIDCResponseMode"
@@ -242,6 +248,7 @@ void OIDC_CFG_MEMBER_FUNC_NAME(dpop_mode, cfg_provider, int_set)(oidc_provider_t
242248
// for metadata.c
243249
OIDC_CFG_PROVIDER_MEMBER_FUNCS_INT_INT_DECL(userinfo_token_method, oidc_userinfo_token_method_t)
244250
OIDC_CFG_PROVIDER_MEMBER_FUNCS_INT_INT_DECL(auth_request_method, oidc_auth_request_method_t)
251+
OIDC_CFG_PROVIDER_MEMBER_FUNCS_INT_INT_DECL(profile, oidc_profile_t)
245252

246253
// types
247254
OIDC_CFG_PROVIDER_MEMBER_FUNCS_TYPE_DECL(pkce, const oidc_proto_pkce_t *)

src/handle/logout.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ static void oidc_logout_revoke_tokens(request_rec *r, oidc_cfg_t *c, oidc_sessio
8484
if (oidc_proto_token_endpoint_auth(
8585
r, c, oidc_cfg_provider_token_endpoint_auth_get(provider), oidc_cfg_provider_client_id_get(provider),
8686
oidc_cfg_provider_client_secret_get(provider), oidc_cfg_provider_client_keys_get(provider),
87-
oidc_cfg_provider_token_endpoint_url_get(provider), params, NULL, &basic_auth, &bearer_auth) == FALSE)
87+
oidc_proto_profile_token_endpoint_auth_aud(provider), params, NULL, &basic_auth, &bearer_auth) == FALSE)
8888
goto out;
8989

9090
token = oidc_session_get_refresh_token(r, session);

src/handle/request.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -201,14 +201,14 @@ int oidc_request_authenticate_user(request_rec *r, oidc_cfg_t *c, oidc_provider_
201201

202202
if ((oidc_util_spaced_string_contains(r->pool, oidc_cfg_provider_response_type_get(provider),
203203
OIDC_PROTO_CODE) == TRUE) &&
204-
(oidc_cfg_provider_pkce_get(provider) != &oidc_pkce_none)) {
204+
(oidc_proto_profile_pkce_get(provider) != &oidc_pkce_none)) {
205205

206206
/* generate the code verifier value that correlates authorization requests and code exchange requests */
207-
if (oidc_cfg_provider_pkce_get(provider)->state(r, &pkce_state) == FALSE)
207+
if (oidc_proto_profile_pkce_get(provider)->state(r, &pkce_state) == FALSE)
208208
return HTTP_INTERNAL_SERVER_ERROR;
209209

210210
/* generate the PKCE code challenge */
211-
if (oidc_cfg_provider_pkce_get(provider)->challenge(r, pkce_state, &code_challenge) == FALSE)
211+
if (oidc_proto_profile_pkce_get(provider)->challenge(r, pkce_state, &code_challenge) == FALSE)
212212
return HTTP_INTERNAL_SERVER_ERROR;
213213
}
214214

src/metadata.c

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
#define OIDC_METADATA_ID_TOKEN_ENCRYPTED_RESPONSE_ALG "id_token_encrypted_response_alg"
8787
#define OIDC_METADATA_ID_TOKEN_ENCRYPTED_RESPONSE_ENC "id_token_encrypted_response_enc"
8888
#define OIDC_METADATA_ID_TOKEN_AUD_VALUES "id_token_aud_values"
89+
#define OIDC_METADATA_PROFILE "profile"
8990
#define OIDC_METADATA_USERINFO_SIGNED_RESPONSE_ALG "userinfo_signed_response_alg"
9091
#define OIDC_METADATA_USERINFO_ENCRYPTED_RESPONSE_ALG "userinfo_encrypted_response_alg"
9192
#define OIDC_METADATA_USERINFO_ENCRYPTED_RESPONSE_ENC "userinfo_encrypted_response_enc"
@@ -1003,7 +1004,7 @@ static void oidc_metadata_parse_url(request_rec *r, const char *type, const char
10031004
oidc_error(r, "oidc_cfg_provider_%s_set: %s", TOSTRING(member), rv); \
10041005
}
10051006

1006-
#define OIDC_METADATA_PROVIDER_SET_INT(member, ivalue, rv) \
1007+
#define OIDC_METADATA_PROVIDER_SET_INT(provider, member, ivalue, rv) \
10071008
if (ivalue != OIDC_CONFIG_POS_INT_UNSET) { \
10081009
rv = oidc_cfg_provider_##member##_set(r->pool, provider, ivalue); \
10091010
if (rv != NULL) \
@@ -1094,7 +1095,7 @@ apr_byte_t oidc_metadata_provider_parse(request_rec *r, oidc_cfg_t *cfg, json_t
10941095
// provided
10951096
oidc_metadata_parse_boolean(r, j_provider, OIDC_METADATA_BACKCHANNEL_LOGOUT_SUPPORTED, &ivalue,
10961097
oidc_cfg_provider_backchannel_logout_supported_get(provider));
1097-
OIDC_METADATA_PROVIDER_SET_INT(backchannel_logout_supported, ivalue, rv)
1098+
OIDC_METADATA_PROVIDER_SET_INT(provider, backchannel_logout_supported, ivalue, rv)
10981099

10991100
if (oidc_cfg_provider_token_endpoint_auth_get(provider) == NULL) {
11001101
if (oidc_metadata_valid_string_in_array(r->pool, j_provider,
@@ -1230,6 +1231,17 @@ apr_byte_t oidc_metadata_conf_parse(request_rec *r, oidc_cfg_t *cfg, json_t *j_c
12301231
int ivalue = OIDC_CONFIG_POS_INT_UNSET;
12311232
apr_array_header_t *keys = NULL, *auds = NULL;
12321233

1234+
// NB: need this first so the profile - if explicitly configured - will override
1235+
// potentially non-conformant / insecure settings
1236+
oidc_util_json_object_get_string(r->pool, j_conf, OIDC_METADATA_PROFILE, &value, NULL);
1237+
if (value) {
1238+
rv = oidc_cfg_provider_profile_set(r->pool, provider, value);
1239+
if (rv != NULL)
1240+
oidc_error(r, "oidc_cfg_provider_profile_set: %s", rv);
1241+
} else {
1242+
oidc_cfg_provider_profile_int_set(provider, oidc_cfg_provider_profile_get(oidc_cfg_provider_get(cfg)));
1243+
}
1244+
12331245
oidc_util_json_object_get_string(r->pool, j_conf, OIDC_METADATA_CLIENT_JWKS_URI, &value,
12341246
oidc_cfg_provider_client_jwks_uri_get(oidc_cfg_provider_get(cfg)));
12351247
OIDC_METADATA_PROVIDER_SET(client_jwks_uri, value, rv)
@@ -1263,8 +1275,9 @@ apr_byte_t oidc_metadata_conf_parse(request_rec *r, oidc_cfg_t *cfg, json_t *j_c
12631275
oidc_cfg_provider_id_token_encrypted_response_enc_get(oidc_cfg_provider_get(cfg)));
12641276
OIDC_METADATA_PROVIDER_SET(id_token_encrypted_response_enc, value, rv)
12651277

1266-
oidc_util_json_object_get_string_array(r->pool, j_conf, OIDC_METADATA_ID_TOKEN_AUD_VALUES, &auds,
1267-
oidc_cfg_provider_id_token_aud_values_get(oidc_cfg_provider_get(cfg)));
1278+
oidc_util_json_object_get_string_array(
1279+
r->pool, j_conf, OIDC_METADATA_ID_TOKEN_AUD_VALUES, &auds,
1280+
oidc_proto_profile_id_token_aud_values_get(r->pool, oidc_cfg_provider_get(cfg)));
12681281
if (auds != NULL) {
12691282
rv = oidc_cfg_provider_id_token_aud_values_set_str_list(r->pool, provider, auds);
12701283
if (rv != NULL)
@@ -1291,11 +1304,11 @@ apr_byte_t oidc_metadata_conf_parse(request_rec *r, oidc_cfg_t *cfg, json_t *j_c
12911304
* for this provider */
12921305
oidc_metadata_parse_boolean(r, j_conf, OIDC_METADATA_SSL_VALIDATE_SERVER, &ivalue,
12931306
oidc_cfg_provider_ssl_validate_server_get(oidc_cfg_provider_get(cfg)));
1294-
OIDC_METADATA_PROVIDER_SET_INT(ssl_validate_server, ivalue, rv)
1307+
OIDC_METADATA_PROVIDER_SET_INT(provider, ssl_validate_server, ivalue, rv)
12951308

12961309
oidc_metadata_parse_boolean(r, j_conf, OIDC_METADATA_VALIDATE_ISSUER, &ivalue,
12971310
oidc_cfg_provider_validate_issuer_get(oidc_cfg_provider_get(cfg)));
1298-
OIDC_METADATA_PROVIDER_SET_INT(validate_issuer, ivalue, rv)
1311+
OIDC_METADATA_PROVIDER_SET_INT(provider, validate_issuer, ivalue, rv)
12991312

13001313
/* find out what scopes we should be requesting from this provider */
13011314
// TODO: use the provider "scopes_supported" to mix-and-match with what we've configured for the client
@@ -1307,17 +1320,17 @@ apr_byte_t oidc_metadata_conf_parse(request_rec *r, oidc_cfg_t *cfg, json_t *j_c
13071320
/* see if we've got a custom JWKs refresh interval */
13081321
oidc_util_json_object_get_int(j_conf, OIDC_METADATA_JWKS_REFRESH_INTERVAL, &ivalue,
13091322
oidc_cfg_provider_jwks_uri_refresh_interval_get(oidc_cfg_provider_get(cfg)));
1310-
OIDC_METADATA_PROVIDER_SET_INT(jwks_uri_refresh_interval, ivalue, rv)
1323+
OIDC_METADATA_PROVIDER_SET_INT(provider, jwks_uri_refresh_interval, ivalue, rv)
13111324

13121325
/* see if we've got a custom IAT slack interval */
13131326
oidc_util_json_object_get_int(j_conf, OIDC_METADATA_IDTOKEN_IAT_SLACK, &ivalue,
13141327
oidc_cfg_provider_idtoken_iat_slack_get(oidc_cfg_provider_get(cfg)));
1315-
OIDC_METADATA_PROVIDER_SET_INT(idtoken_iat_slack, ivalue, rv)
1328+
OIDC_METADATA_PROVIDER_SET_INT(provider, idtoken_iat_slack, ivalue, rv)
13161329

13171330
/* see if we've got a custom max session duration */
13181331
oidc_util_json_object_get_int(j_conf, OIDC_METADATA_SESSION_MAX_DURATION, &ivalue,
13191332
oidc_cfg_provider_session_max_duration_get(oidc_cfg_provider_get(cfg)));
1320-
OIDC_METADATA_PROVIDER_SET_INT(session_max_duration, ivalue, rv)
1333+
OIDC_METADATA_PROVIDER_SET_INT(provider, session_max_duration, ivalue, rv)
13211334

13221335
/* see if we've got custom authentication request parameter values */
13231336
oidc_util_json_object_get_string(r->pool, j_conf, OIDC_METADATA_AUTH_REQUEST_PARAMS, &value,
@@ -1341,7 +1354,7 @@ apr_byte_t oidc_metadata_conf_parse(request_rec *r, oidc_cfg_t *cfg, json_t *j_c
13411354

13421355
/* get the PKCE method to use */
13431356
oidc_util_json_object_get_string(r->pool, j_conf, OIDC_METADATA_PKCE_METHOD, &value,
1344-
oidc_cfg_provider_pkce_get(oidc_cfg_provider_get(cfg))->method);
1357+
oidc_proto_profile_pkce_get(provider)->method);
13451358
OIDC_METADATA_PROVIDER_SET(pkce, value, rv)
13461359

13471360
/* see if we've got a custom DPoP mode */
@@ -1351,8 +1364,7 @@ apr_byte_t oidc_metadata_conf_parse(request_rec *r, oidc_cfg_t *cfg, json_t *j_c
13511364
if (rv != NULL)
13521365
oidc_error(r, "oidc_cfg_provider_dpop_mode_set: %s", rv);
13531366
} else {
1354-
oidc_cfg_provider_dpop_mode_int_set(provider,
1355-
oidc_cfg_provider_dpop_mode_get(oidc_cfg_provider_get(cfg)));
1367+
oidc_cfg_provider_dpop_mode_int_set(provider, oidc_proto_profile_dpop_mode_get(provider));
13561368
}
13571369

13581370
/* get the client name */
@@ -1392,7 +1404,7 @@ apr_byte_t oidc_metadata_conf_parse(request_rec *r, oidc_cfg_t *cfg, json_t *j_c
13921404
/* see if we've got a custom user info refresh interval */
13931405
oidc_util_json_object_get_int(j_conf, OIDC_METADATA_USERINFO_REFRESH_INTERVAL, &ivalue,
13941406
oidc_cfg_provider_userinfo_refresh_interval_get(oidc_cfg_provider_get(cfg)));
1395-
OIDC_METADATA_PROVIDER_SET_INT(userinfo_refresh_interval, ivalue, rv)
1407+
OIDC_METADATA_PROVIDER_SET_INT(provider, userinfo_refresh_interval, ivalue, rv)
13961408

13971409
/* TLS client cert auth settings */
13981410
oidc_util_json_object_get_string(
@@ -1432,14 +1444,14 @@ apr_byte_t oidc_metadata_conf_parse(request_rec *r, oidc_cfg_t *cfg, json_t *j_c
14321444
if (rv != NULL)
14331445
oidc_error(r, "oidc_cfg_provider_auth_request_method_set: %s", rv);
14341446
} else {
1435-
oidc_cfg_provider_auth_request_method_int_set(
1436-
provider, oidc_cfg_provider_auth_request_method_get(oidc_cfg_provider_get(cfg)));
1447+
oidc_cfg_provider_auth_request_method_int_set(provider,
1448+
oidc_proto_profile_auth_request_method_get(provider));
14371449
}
14381450

14391451
/* get the issuer specific redirect URI option */
14401452
oidc_metadata_parse_boolean(r, j_conf, OIDC_METADATA_RESPONSE_REQUIRE_ISS, &ivalue,
1441-
oidc_cfg_provider_response_require_iss_get(oidc_cfg_provider_get(cfg)));
1442-
OIDC_METADATA_PROVIDER_SET_INT(response_require_iss, ivalue, rv)
1453+
oidc_proto_profile_response_require_iss_get(provider));
1454+
OIDC_METADATA_PROVIDER_SET_INT(provider, response_require_iss, ivalue, rv)
14431455

14441456
return TRUE;
14451457
}

src/mod_auth_openidc.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1470,7 +1470,7 @@ static int oidc_check_config_openid_openidc(server_rec *s, oidc_cfg_t *c) {
14701470
}
14711471
}
14721472

1473-
if (oidc_cfg_provider_dpop_mode_get(oidc_cfg_provider_get(c)) != OIDC_DPOP_MODE_OFF) {
1473+
if (oidc_proto_profile_dpop_mode_get(oidc_cfg_provider_get(c)) != OIDC_DPOP_MODE_OFF) {
14741474
if (oidc_util_key_list_first(oidc_cfg_private_keys_get(c), -1, OIDC_JOSE_JWK_SIG_STR) == NULL) {
14751475
oidc_serror(s, "'" OIDCDPoPMode "' is configured but the required signing keys have not been "
14761476
"provided in '" OIDCPrivateKeyFiles "'/'" OIDCPublicKeyFiles "'");

0 commit comments

Comments
 (0)