Skip to content

Commit

Permalink
add support for RFC 9126 OAuth 2.0 Pushed Authorization Requests
Browse files Browse the repository at this point in the history
Signed-off-by: Hans Zandbelt <[email protected]>
  • Loading branch information
zandbelt committed May 30, 2024
1 parent 652ab1b commit edc5ef8
Show file tree
Hide file tree
Showing 8 changed files with 103 additions and 5 deletions.
3 changes: 3 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
05/30/2024
- add support for RFC 9126 OAuth 2.0 Pushed Authorization Requests

04/23/2024
- disable support for the RSA PKCS v1.5 JWE encryption algorithm as it is deemed unsafe
due to the Marvin attack and is removed from libcjose as well
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,10 @@ Interoperability
- [OpenID Connect Core 1.0](http://openid.net/specs/openid-connect-core-1_0.html) *(Basic, Implicit, Hybrid and Refresh flows)*
- [OpenID Connect Discovery 1.0](http://openid.net/specs/openid-connect-discovery-1_0.html)
- [OpenID Connect Dynamic Client Registration 1.0](http://openid.net/specs/openid-connect-registration-1_0.html)
- [OAuth 2.0 Multiple Response Type Encoding Practices 1.0](http://openid.net/specs/oauth-v2-multiple-response-types-1_0.html)
- [RFC 7636 - Proof Key for Code Exchange by OAuth Public Clients](https://datatracker.ietf.org/doc/html/rfc7636)
- [RFC 9126 - OAuth 2.0 Pushed Authorization Requests](https://datatracker.ietf.org/doc/html/rfc9126)
- [OAuth 2.0 Form Post Response Mode 1.0](http://openid.net/specs/oauth-v2-form-post-response-mode-1_0.html)
- [RFC7 7636 - Proof Key for Code Exchange by OAuth Public Clients](https://tools.ietf.org/html/rfc7636)
- [OAuth 2.0 Multiple Response Type Encoding Practices 1.0](http://openid.net/specs/oauth-v2-multiple-response-types-1_0.html)
- [OpenID Connect Session Management 1.0](http://openid.net/specs/openid-connect-session-1_0.html) *see the [Wiki](https://github.com/OpenIDC/mod_auth_openidc/wiki/OpenID-Connect-Session-Management) for information on how to configure it)*
- [OpenID Connect Front-Channel Logout 1.0](http://openid.net/specs/openid-connect-frontchannel-1_0.html)
- [OpenID Connect Back-Channel Logout 1.0](https://openid.net/specs/openid-connect-backchannel-1_0.html)
Expand Down
8 changes: 7 additions & 1 deletion auth_openidc.conf
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,11 @@
# Used when OIDCProviderMetadataURL is not defined or the metadata obtained from that URL does not set it.
#OIDCProviderRevocationEndpoint <url>

# The RFC 9126 Pushed Authorization Request endpoint URL.
# When not defined, PAR cannot be used to send authentication requests, see also OIDCProviderAuthRequestMethod
# Used when OIDCProviderMetadataURL is not defined or the metadata obtained from that URL does not set it.
#OIDCProviderPushedAuthorizationRequestEndpoint <url>

# Define whether the OP supports OpenID Connect Back Channel Logout.
# According to: https://openid.net/specs/openid-connect-backchannel-1_0.html
# Used when OIDCProviderMetadataURL is not defined or the metadata obtained from that URL does not set it.
Expand Down Expand Up @@ -214,9 +219,10 @@
# Defines the HTTP method used to pass the parameters in the Authentication Request to the Authorization Endpoint.
# "GET" means that the parameters will be passed as query parameters in an HTTP GET
# "POST" means that the parameters will be passed as form-post parameters in an HTTP POST
# "PAR" means that parameters will be sent to the Pushed Authorization Endpoint
# When not defined the default is "GET".
# NB: this can be overridden on a per-OP basis in the .conf file using the key: auth_request_method
# OIDCProviderAuthRequestMethod [ GET | POST ]
# OIDCProviderAuthRequestMethod [ GET | POST | PAR ]

# The fully qualified names of the files that contain the PEM-formatted RSA/EC Public key or a X.509 certificates
# that contain the RSA/EC public keys to be used for JWT (OP state/id_token) encryption by the OP.
Expand Down
5 changes: 5 additions & 0 deletions src/cfg/cmds.c
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,11 @@ const command_rec oidc_cfg_cmds[] = {
OIDCProviderRevocationEndpoint,
revocation_endpoint_url,
"Define the RFC 7009 Token Revocation Endpoint URL (e.g.: https://localhost:9031/as/revoke_token.oauth2)"),
OIDC_CFG_CMD_PROVIDER(
AP_INIT_TAKE1,
OIDCProviderPushedAuthorizationRequestEndpoint,
pushed_authorization_request_endpoint_url,
"Define the OAuth 2.0 Pushed Authorization Endpoint URL (e.g.: https://localhost:9031/as/par.oauth2)"),
OIDC_CFG_CMD_PROVIDER(
AP_INIT_TAKE1,
OIDCProviderCheckSessionIFrame,
Expand Down
14 changes: 12 additions & 2 deletions src/cfg/provider.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ struct oidc_provider_t {
char *userinfo_endpoint_url;
char *revocation_endpoint_url;
char *registration_endpoint_url;
char *pushed_authorization_request_endpoint_url;
char *check_session_iframe;
char *end_session_endpoint;
oidc_jwks_uri_t jwks_uri;
Expand Down Expand Up @@ -267,6 +268,7 @@ OIDC_PROVIDER_MEMBER_FUNCS_URL(token_endpoint_url)
OIDC_PROVIDER_MEMBER_FUNCS_STR(token_endpoint_params, NULL)
OIDC_PROVIDER_MEMBER_FUNCS_URL(userinfo_endpoint_url)
OIDC_PROVIDER_MEMBER_FUNCS_URL(registration_endpoint_url)
OIDC_PROVIDER_MEMBER_FUNCS_URL(pushed_authorization_request_endpoint_url)
OIDC_PROVIDER_MEMBER_FUNCS_URL(check_session_iframe)
OIDC_PROVIDER_MEMBER_FUNCS_URL(end_session_endpoint)
OIDC_PROVIDER_MEMBER_FUNCS_STR(client_contact, NULL)
Expand Down Expand Up @@ -321,11 +323,15 @@ OIDC_PROVIDER_MEMBER_FUNCS_PARSE_STR(userinfo_encrypted_response_enc, oidc_cfg_p

#define OIDC_AUTH_REQUEST_METHOD_GET_STR "GET"
#define OIDC_AUTH_REQUEST_METHOD_POST_STR "POST"
#define OIDC_AUTH_REQUEST_METHOD_PAR_STR "PAR"

static const char *oidc_cfg_provider_parse_auth_request_method(apr_pool_t *pool, const char *arg,
oidc_auth_request_method_t *method) {
static const oidc_cfg_option_t options[] = {{OIDC_AUTH_REQUEST_METHOD_GET, OIDC_AUTH_REQUEST_METHOD_GET_STR},
{OIDC_AUTH_REQUEST_METHOD_POST, OIDC_AUTH_REQUEST_METHOD_POST_STR}};
static const oidc_cfg_option_t options[] = {
{OIDC_AUTH_REQUEST_METHOD_GET, OIDC_AUTH_REQUEST_METHOD_GET_STR},
{OIDC_AUTH_REQUEST_METHOD_POST, OIDC_AUTH_REQUEST_METHOD_POST_STR},
{OIDC_AUTH_REQUEST_METHOD_PAR, OIDC_AUTH_REQUEST_METHOD_PAR_STR},
};
return oidc_cfg_parse_option(pool, options, OIDC_CFG_OPTIONS_SIZE(options), arg, (int *)method);
}

Expand Down Expand Up @@ -590,6 +596,7 @@ static void oidc_cfg_provider_init(oidc_provider_t *provider) {
provider->token_endpoint_tls_client_key_pwd = NULL;
provider->registration_endpoint_url = NULL;
provider->registration_endpoint_json = NULL;
provider->pushed_authorization_request_endpoint_url = NULL;
provider->check_session_iframe = NULL;
provider->end_session_endpoint = NULL;
provider->jwks_uri.uri = NULL;
Expand Down Expand Up @@ -673,6 +680,9 @@ void oidc_cfg_provider_merge(apr_pool_t *pool, oidc_provider_t *dst, const oidc_
add->registration_endpoint_url != NULL ? add->registration_endpoint_url : base->registration_endpoint_url;
dst->registration_endpoint_json = add->registration_endpoint_json != NULL ? add->registration_endpoint_json
: base->registration_endpoint_json;
dst->pushed_authorization_request_endpoint_url = add->pushed_authorization_request_endpoint_url != NULL
? add->pushed_authorization_request_endpoint_url
: base->pushed_authorization_request_endpoint_url;

dst->check_session_iframe =
add->check_session_iframe != NULL ? add->check_session_iframe : base->check_session_iframe;
Expand Down
3 changes: 3 additions & 0 deletions src/cfg/provider.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ typedef struct oidc_proto_pkce_t {
typedef enum {
OIDC_AUTH_REQUEST_METHOD_GET = 1,
OIDC_AUTH_REQUEST_METHOD_POST = 2,
OIDC_AUTH_REQUEST_METHOD_PAR = 3,
} oidc_auth_request_method_t;

typedef enum {
Expand Down Expand Up @@ -98,6 +99,7 @@ typedef struct oidc_jwks_uri_t {
#define OIDCProviderRegistrationEndpointJson "OIDCProviderRegistrationEndpointJson"
#define OIDCProviderUserInfoEndpoint "OIDCProviderUserInfoEndpoint"
#define OIDCProviderRevocationEndpoint "OIDCProviderRevocationEndpoint"
#define OIDCProviderPushedAuthorizationRequestEndpoint "OIDCProviderPushedAuthorizationRequestEndpoint"
#define OIDCProviderCheckSessionIFrame "OIDCProviderCheckSessionIFrame"
#define OIDCProviderEndSessionEndpoint "OIDCProviderEndSessionEndpoint"
#define OIDCProviderBackChannelLogoutSupported "OIDCProviderBackChannelLogoutSupported"
Expand Down Expand Up @@ -176,6 +178,7 @@ OIDC_CFG_PROVIDER_MEMBER_FUNCS_STR_DECL(token_endpoint_params)
OIDC_CFG_PROVIDER_MEMBER_FUNCS_STR_DECL(userinfo_endpoint_url)
OIDC_CFG_PROVIDER_MEMBER_FUNCS_STR_DECL(revocation_endpoint_url)
OIDC_CFG_PROVIDER_MEMBER_FUNCS_STR_DECL(registration_endpoint_url)
OIDC_CFG_PROVIDER_MEMBER_FUNCS_STR_DECL(pushed_authorization_request_endpoint_url);
OIDC_CFG_PROVIDER_MEMBER_FUNCS_STR_DECL(check_session_iframe)
OIDC_CFG_PROVIDER_MEMBER_FUNCS_STR_DECL(end_session_endpoint)
OIDC_CFG_PROVIDER_MEMBER_FUNCS_STR_DECL(client_id)
Expand Down
7 changes: 7 additions & 0 deletions src/metadata.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
#define OIDC_METADATA_INTROSPECTION_ENDPOINT "introspection_endpoint"
#define OIDC_METADATA_USERINFO_ENDPOINT "userinfo_endpoint"
#define OIDC_METADATA_REVOCATION_ENDPOINT "revocation_endpoint"
#define OIDC_METADATA_PAR_ENDPOINT "pushed_authorization_request_endpoint"
#define OIDC_METADATA_JWKS_URI "jwks_uri"
#define OIDC_METADATA_SIGNED_JWKS_URI "signed_jwks_uri"
#define OIDC_METADATA_TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED "token_endpoint_auth_methods_supported"
Expand Down Expand Up @@ -1049,6 +1050,12 @@ apr_byte_t oidc_metadata_provider_parse(request_rec *r, oidc_cfg_t *cfg, json_t
OIDC_METADATA_PROVIDER_SET(revocation_endpoint_url, value, rv)
}

if (oidc_cfg_provider_pushed_authorization_request_endpoint_url_get(provider) == NULL) {
oidc_metadata_parse_url(r, OIDC_METADATA_SUFFIX_PROVIDER, oidc_cfg_provider_issuer_get(provider),
j_provider, OIDC_METADATA_PAR_ENDPOINT, &value, NULL);
OIDC_METADATA_PROVIDER_SET(pushed_authorization_request_endpoint_url, value, rv)
}

if (oidc_cfg_provider_jwks_uri_uri_get(provider) == NULL) {
oidc_metadata_parse_url(r, OIDC_METADATA_SUFFIX_PROVIDER, oidc_cfg_provider_issuer_get(provider),
j_provider, OIDC_METADATA_JWKS_URI, &value, NULL);
Expand Down
63 changes: 63 additions & 0 deletions src/proto.c
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,65 @@ static void oidc_proto_auth_request_params_add(request_rec *r, apr_table_t *para
}
}

/*
* send a Pushed Authorization Request tot the Provider
*/
static int oidc_proto_pushed_authorization_request(request_rec *r, struct oidc_provider_t *provider,
apr_table_t *params) {
oidc_cfg_t *cfg = ap_get_module_config(r->server->module_config, &auth_openidc_module);
char *response = NULL, *basic_auth = NULL, *bearer_auth = NULL;
char *request_uri = NULL;
int expires_in = 0;
char *authorization_request = NULL;
json_t *j_result = NULL;
int rv = HTTP_INTERNAL_SERVER_ERROR;
const char *endpoint_url = oidc_cfg_provider_pushed_authorization_request_endpoint_url_get(provider);

oidc_debug(r, "enter");

if (endpoint_url == NULL) {
oidc_error(r, "the Provider's OAuth 2.0 Pushed Authorization Request endpoint URL is not set, PAR "
"cannot be used");
goto out;
}

/* add the token endpoint authentication credentials to the pushed authorization request */
if (oidc_proto_token_endpoint_auth(
r, cfg, oidc_cfg_provider_token_endpoint_auth_get(provider), oidc_cfg_provider_client_id_get(provider),
oidc_cfg_provider_client_secret_get(provider), oidc_cfg_provider_client_keys_get(provider),
oidc_cfg_provider_token_endpoint_url_get(provider), params, NULL, &basic_auth, &bearer_auth) == FALSE)
goto out;

if (oidc_http_post_form(r, endpoint_url, params, basic_auth, bearer_auth,
oidc_cfg_provider_ssl_validate_server_get(provider), &response, NULL,
oidc_cfg_http_timeout_long_get(cfg), oidc_cfg_outgoing_proxy_get(cfg),
oidc_cfg_dir_pass_cookies_get(r), NULL, NULL, NULL) == FALSE)
goto out;

/* check for errors, the response itself will have been logged already */
if (oidc_util_decode_json_and_check_error(r, response, &j_result) == FALSE)
goto out;

/* get the request_uri from the parsed response */
oidc_util_json_object_get_string(r->pool, j_result, OIDC_PROTO_REQUEST_URI, &request_uri, NULL);

/* get the expires_in value from the parsed response */
oidc_util_json_object_get_int(j_result, OIDC_PROTO_EXPIRES_IN, &expires_in, 60);

/* assemble the resulting authentication request and redirect */
apr_table_clear(params);
apr_table_setn(params, OIDC_PROTO_CLIENT_ID, oidc_cfg_provider_client_id_get(provider));
apr_table_setn(params, OIDC_PROTO_REQUEST_URI, request_uri);
authorization_request =
oidc_http_query_encoded_url(r, oidc_cfg_provider_authorization_endpoint_url_get(provider), params);
oidc_http_hdr_out_location_set(r, authorization_request);
rv = HTTP_MOVED_TEMPORARILY;

out:

return rv;
}

/*
* send an OpenID Connect authorization request to the specified provider
*/
Expand Down Expand Up @@ -233,6 +292,10 @@ int oidc_proto_authorization_request(request_rec *r, struct oidc_provider_t *pro
/* construct a HTML POST auto-submit page with the authorization request parameters */
rv = oidc_proto_html_post(r, oidc_cfg_provider_authorization_endpoint_url_get(provider), params);

} else if (oidc_cfg_provider_auth_request_method_get(provider) == OIDC_AUTH_REQUEST_METHOD_PAR) {

rv = oidc_proto_pushed_authorization_request(r, provider, params);

} else if (oidc_cfg_provider_auth_request_method_get(provider) == OIDC_AUTH_REQUEST_METHOD_GET) {

/* construct the full authorization request URL */
Expand Down

0 comments on commit edc5ef8

Please sign in to comment.