Skip to content

Commit

Permalink
refactor proto JWT create and sign/serialize
Browse files Browse the repository at this point in the history
Signed-off-by: Hans Zandbelt <[email protected]>
  • Loading branch information
zandbelt committed Jun 6, 2024
1 parent 731cdbd commit b7d7f32
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 100 deletions.
33 changes: 3 additions & 30 deletions src/mod_auth_openidc.c
Original file line number Diff line number Diff line change
Expand Up @@ -873,28 +873,9 @@ static apr_byte_t oidc_userinfo_create_signed_jwt(request_rec *r, oidc_cfg_t *cf

oidc_debug(r, "enter: %s", s_claims);

jwk = oidc_util_key_list_first(oidc_cfg_private_keys_get(cfg), -1, OIDC_JOSE_JWK_SIG_STR);
// TODO: detect at config time
if (jwk == NULL) {
oidc_error(r, "no RSA/EC private signing keys have been configured (in " OIDCPrivateKeyFiles ")");
goto end;
}

jwt = oidc_jwt_new(r->pool, TRUE, TRUE);
if (jwt == NULL)
if (oidc_proto_jwt_create_from_first_pkey(r, cfg, &jwk, &jwt, FALSE) == FALSE)
goto end;

jwt->header.kid = apr_pstrdup(r->pool, jwk->kid);

if (jwk->kty == CJOSE_JWK_KTY_RSA)
jwt->header.alg = apr_pstrdup(r->pool, CJOSE_HDR_ALG_RS256);
else if (jwk->kty == CJOSE_JWK_KTY_EC)
jwt->header.alg = apr_pstrdup(r->pool, CJOSE_HDR_ALG_ES256);
else {
oidc_error(r, "no usable RSA/EC signing keys has been configured (in " OIDCPrivateKeyFiles ")");
goto end;
}

json_object_set_new(jwt->payload.value.json, OIDC_CLAIM_AUD,
json_string(oidc_util_current_url(r, oidc_cfg_x_forwarded_headers_get(cfg))));
json_object_set_new(jwt->payload.value.json, OIDC_CLAIM_ISS,
Expand Down Expand Up @@ -923,7 +904,7 @@ static apr_byte_t oidc_userinfo_create_signed_jwt(request_rec *r, oidc_cfg_t *cf
}

if (json_object_get(jwt->payload.value.json, OIDC_CLAIM_JTI) == NULL) {
oidc_util_generate_random_string(r, &jti, 16);
oidc_util_generate_random_string(r, &jti, OIDC_PROTO_JWT_JTI_LEN);
json_object_set_new(jwt->payload.value.json, OIDC_CLAIM_JTI, json_string(jti));
}
if (json_object_get(jwt->payload.value.json, OIDC_CLAIM_IAT) == NULL) {
Expand All @@ -938,16 +919,8 @@ static apr_byte_t oidc_userinfo_create_signed_jwt(request_rec *r, oidc_cfg_t *cf
OIDC_USERINFO_SIGNED_JWT_EXP_DEFAULT));
}

if (oidc_jwt_sign(r->pool, jwt, jwk, FALSE, &err) == FALSE) {
oidc_error(r, "oidc_jwt_sign failed: %s", oidc_jose_e2s(r->pool, err));
if (oidc_proto_jwt_sign_and_serialize(r, jwk, jwt, cser) == FALSE)
goto end;
}

*cser = oidc_jwt_serialize(r->pool, jwt, &err);
if (*cser == NULL) {
oidc_error(r, "oidc_jwt_serialize failed: %s", oidc_jose_e2s(r->pool, err));
goto end;
}

rv = TRUE;

Expand Down
16 changes: 3 additions & 13 deletions src/proto/auth.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,6 @@ static apr_byte_t oidc_proto_endpoint_client_secret_post(request_rec *r, const c
return TRUE;
}

#define OIDC_PROTO_ASSERTION_JTI_LEN 16

/*
* helper function to create a JWT assertion for endpoint authentication
*/
Expand All @@ -95,7 +93,7 @@ static apr_byte_t oidc_proto_jwt_create(request_rec *r, const char *client_id, c
oidc_jwt_t *jwt = *out;

char *jti = NULL;
oidc_util_generate_random_string(r, &jti, OIDC_PROTO_ASSERTION_JTI_LEN);
oidc_util_generate_random_string(r, &jti, OIDC_PROTO_JWT_JTI_LEN);

json_object_set_new(jwt->payload.value.json, OIDC_CLAIM_ISS, json_string(client_id));
json_object_set_new(jwt->payload.value.json, OIDC_CLAIM_SUB, json_string(client_id));
Expand All @@ -111,18 +109,10 @@ static apr_byte_t oidc_proto_jwt_create(request_rec *r, const char *client_id, c
* helper function to add a JWT assertion to the HTTP request as endpoint authentication
*/
static apr_byte_t oidc_proto_jwt_sign_and_add(request_rec *r, apr_table_t *params, oidc_jwt_t *jwt, oidc_jwk_t *jwk) {
oidc_jose_error_t err;

if (oidc_jwt_sign(r->pool, jwt, jwk, FALSE, &err) == FALSE) {
oidc_error(r, "signing JWT failed: %s", oidc_jose_e2s(r->pool, err));
return FALSE;
}
char *cser = NULL;

char *cser = oidc_jwt_serialize(r->pool, jwt, &err);
if (cser == NULL) {
oidc_error(r, "oidc_jwt_serialize failed: %s", oidc_jose_e2s(r->pool, err));
if (oidc_proto_jwt_sign_and_serialize(r, jwk, jwt, &cser) == FALSE)
return FALSE;
}

apr_table_setn(params, OIDC_PROTO_CLIENT_ASSERTION_TYPE, OIDC_PROTO_CLIENT_ASSERTION_TYPE_JWT_BEARER);
apr_table_set(params, OIDC_PROTO_CLIENT_ASSERTION, cser);
Expand Down
36 changes: 6 additions & 30 deletions src/proto/dpop.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
#include "proto/proto.h"
#include "util.h"

#define OIDC_PROTO_DPOP_JWT_TYP "dpop+jwt"

/*
* generate a DPoP proof for the specified URL/method/access_token
*/
Expand All @@ -61,32 +63,14 @@ char *oidc_proto_dpop_create(request_rec *r, oidc_cfg_t *cfg, const char *url, c

oidc_debug(r, "enter");

jwk = oidc_util_key_list_first(oidc_cfg_private_keys_get(cfg), -1, OIDC_JOSE_JWK_SIG_STR);
if (jwk == NULL) {
oidc_debug(r, "no RSA/EC private signing keys have been configured (in " OIDCPrivateKeyFiles ")");
goto end;
}

jwt = oidc_jwt_new(r->pool, TRUE, TRUE);
if (jwt == NULL)
goto end;

jwt->header.kid = apr_pstrdup(r->pool, jwk->kid);

if (jwk->kty == CJOSE_JWK_KTY_RSA)
jwt->header.alg = apr_pstrdup(r->pool, CJOSE_HDR_ALG_PS256);
else if (jwk->kty == CJOSE_JWK_KTY_EC)
jwt->header.alg = apr_pstrdup(r->pool, CJOSE_HDR_ALG_ES256);
else {
oidc_error(r, "no usable RSA/EC signing keys has been configured (in " OIDCPrivateKeyFiles ")");
if (oidc_proto_jwt_create_from_first_pkey(r, cfg, &jwk, &jwt, TRUE) == FALSE)
goto end;
}

json_object_set_new(jwt->header.value.json, OIDC_CLAIM_TYP, json_string("dpop+jwt"));
json_object_set_new(jwt->header.value.json, OIDC_CLAIM_TYP, json_string(OIDC_PROTO_DPOP_JWT_TYP));
s_jwk = cjose_jwk_to_json(jwk->cjose_jwk, 0, &cjose_err);
cjose_header_set_raw(jwt->header.value.json, OIDC_CLAIM_JWK, s_jwk, &cjose_err);

oidc_util_generate_random_string(r, &jti, 16);
oidc_util_generate_random_string(r, &jti, OIDC_PROTO_JWT_JTI_LEN);
json_object_set_new(jwt->payload.value.json, OIDC_CLAIM_JTI, json_string(jti));
json_object_set_new(jwt->payload.value.json, OIDC_CLAIM_HTM, json_string(method));
json_object_set_new(jwt->payload.value.json, OIDC_CLAIM_HTU, json_string(url));
Expand All @@ -101,16 +85,8 @@ char *oidc_proto_dpop_create(request_rec *r, oidc_cfg_t *cfg, const char *url, c
json_object_set_new(jwt->payload.value.json, OIDC_CLAIM_ATH, json_string(ath));
}

if (oidc_jwt_sign(r->pool, jwt, jwk, FALSE, &err) == FALSE) {
oidc_error(r, "oidc_jwt_sign failed: %s", oidc_jose_e2s(r->pool, err));
goto end;
}

cser = oidc_jwt_serialize(r->pool, jwt, &err);
if (cser == NULL) {
oidc_error(r, "oidc_jwt_serialize failed: %s", oidc_jose_e2s(r->pool, err));
if (oidc_proto_jwt_sign_and_serialize(r, jwk, jwt, &cser) == FALSE)
goto end;
}

end:

Expand Down
58 changes: 58 additions & 0 deletions src/proto/jwt.c
Original file line number Diff line number Diff line change
Expand Up @@ -244,3 +244,61 @@ char *oidc_proto_jwt_header_peek(request_rec *r, const char *compact_encoded_jwt
}
return result;
}

apr_byte_t oidc_proto_jwt_create_from_first_pkey(request_rec *r, oidc_cfg_t *cfg, oidc_jwk_t **jwk, oidc_jwt_t **jwt,
apr_byte_t use_psa_for_rsa) {
apr_byte_t rv = FALSE;

oidc_debug(r, "enter");

*jwk = oidc_util_key_list_first(oidc_cfg_private_keys_get(cfg), -1, OIDC_JOSE_JWK_SIG_STR);
// TODO: detect at config time
if (jwk == NULL) {
oidc_error(r, "no RSA/EC private signing keys have been configured (in " OIDCPrivateKeyFiles ")");
goto end;
}

*jwt = oidc_jwt_new(r->pool, TRUE, TRUE);
if (*jwt == NULL)
goto end;

(*jwt)->header.kid = apr_pstrdup(r->pool, (*jwk)->kid);

if ((*jwk)->kty == CJOSE_JWK_KTY_RSA)
(*jwt)->header.alg = apr_pstrdup(r->pool, use_psa_for_rsa ? CJOSE_HDR_ALG_PS256 : CJOSE_HDR_ALG_RS256);
else if ((*jwk)->kty == CJOSE_JWK_KTY_EC)
(*jwt)->header.alg = apr_pstrdup(r->pool, CJOSE_HDR_ALG_ES256);
else {
oidc_error(r, "no usable RSA/EC signing keys has been configured (in " OIDCPrivateKeyFiles ")");
goto end;
}

rv = TRUE;

end:

// also in case of errors, jwt will be destroyed in the caller function
return rv;
}

apr_byte_t oidc_proto_jwt_sign_and_serialize(request_rec *r, oidc_jwk_t *jwk, oidc_jwt_t *jwt, char **cser) {
apr_byte_t rv = FALSE;
oidc_jose_error_t err;

if (oidc_jwt_sign(r->pool, jwt, jwk, FALSE, &err) == FALSE) {
oidc_error(r, "oidc_jwt_sign failed: %s", oidc_jose_e2s(r->pool, err));
goto end;
}

*cser = oidc_jwt_serialize(r->pool, jwt, &err);
if (*cser == NULL) {
oidc_error(r, "oidc_jwt_serialize failed: %s", oidc_jose_e2s(r->pool, err));
goto end;
}

rv = TRUE;

end:

return rv;
}
58 changes: 32 additions & 26 deletions src/proto/proto.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,32 +120,6 @@

typedef json_t oidc_proto_state_t;

// id_token.c
apr_byte_t oidc_proto_idtoken_parse(request_rec *r, oidc_cfg_t *cfg, oidc_provider_t *provider, const char *id_token,
const char *nonce, oidc_jwt_t **jwt, apr_byte_t is_code_flow);
apr_byte_t oidc_proto_idtoken_validate_aud_and_azp(request_rec *r, oidc_cfg_t *cfg, oidc_provider_t *provider,
oidc_jwt_payload_t *id_token_payload);
// non-static for test.c
apr_byte_t oidc_proto_idtoken_validate_access_token(request_rec *r, oidc_provider_t *provider, oidc_jwt_t *jwt,
const char *response_type, const char *access_token);
apr_byte_t oidc_proto_idtoken_validate_code(request_rec *r, oidc_provider_t *provider, oidc_jwt_t *jwt,
const char *response_type, const char *code);
apr_byte_t oidc_proto_idtoken_validate_nonce(request_rec *r, oidc_cfg_t *cfg, oidc_provider_t *provider,
const char *nonce, oidc_jwt_t *jwt);

// jwt.c
char *oidc_proto_jwt_header_peek(request_rec *r, const char *jwt, char **alg, char **enc, char **kid);
apr_byte_t oidc_proto_jwt_verify(request_rec *r, oidc_cfg_t *cfg, oidc_jwt_t *jwt, const oidc_jwks_uri_t *jwks_uri,
int ssl_validate_server, apr_hash_t *symmetric_keys, const char *alg);
apr_byte_t oidc_proto_jwt_validate(request_rec *r, oidc_jwt_t *jwt, const char *iss, apr_byte_t exp_is_mandatory,
apr_byte_t iat_is_mandatory, int iat_slack);

// proto.c
apr_byte_t oidc_proto_generate_nonce(request_rec *r, char **nonce, int len);
apr_array_header_t *oidc_proto_supported_flows(apr_pool_t *pool);
apr_byte_t oidc_proto_flow_is_supported(apr_pool_t *pool, const char *flow);
int oidc_proto_return_www_authenticate(request_rec *r, const char *error, const char *error_description);

// auth.c
apr_byte_t oidc_proto_token_endpoint_auth(request_rec *r, oidc_cfg_t *cfg, const char *token_endpoint_auth,
const char *client_id, const char *client_secret,
Expand All @@ -161,10 +135,36 @@ apr_byte_t oidc_proto_discovery_url_based(request_rec *r, oidc_cfg_t *cfg, const
char *oidc_proto_dpop_create(request_rec *r, oidc_cfg_t *cfg, const char *url, const char *method,
const char *access_token);

// id_token.c
apr_byte_t oidc_proto_idtoken_parse(request_rec *r, oidc_cfg_t *cfg, oidc_provider_t *provider, const char *id_token,
const char *nonce, oidc_jwt_t **jwt, apr_byte_t is_code_flow);
apr_byte_t oidc_proto_idtoken_validate_aud_and_azp(request_rec *r, oidc_cfg_t *cfg, oidc_provider_t *provider,
oidc_jwt_payload_t *id_token_payload);
// non-static for test.c
apr_byte_t oidc_proto_idtoken_validate_access_token(request_rec *r, oidc_provider_t *provider, oidc_jwt_t *jwt,
const char *response_type, const char *access_token);
apr_byte_t oidc_proto_idtoken_validate_code(request_rec *r, oidc_provider_t *provider, oidc_jwt_t *jwt,
const char *response_type, const char *code);
apr_byte_t oidc_proto_idtoken_validate_nonce(request_rec *r, oidc_cfg_t *cfg, oidc_provider_t *provider,
const char *nonce, oidc_jwt_t *jwt);

// jwks.c
apr_byte_t oidc_proto_jwks_uri_keys(request_rec *r, oidc_cfg_t *cfg, oidc_jwt_t *jwt, const oidc_jwks_uri_t *jwks_uri,
int ssl_validate_server, apr_hash_t *keys, apr_byte_t *force_refresh);

// jwt.c

#define OIDC_PROTO_JWT_JTI_LEN 16

apr_byte_t oidc_proto_jwt_verify(request_rec *r, oidc_cfg_t *cfg, oidc_jwt_t *jwt, const oidc_jwks_uri_t *jwks_uri,
int ssl_validate_server, apr_hash_t *symmetric_keys, const char *alg);
apr_byte_t oidc_proto_jwt_validate(request_rec *r, oidc_jwt_t *jwt, const char *iss, apr_byte_t exp_is_mandatory,
apr_byte_t iat_is_mandatory, int iat_slack);
char *oidc_proto_jwt_header_peek(request_rec *r, const char *jwt, char **alg, char **enc, char **kid);
apr_byte_t oidc_proto_jwt_create_from_first_pkey(request_rec *r, oidc_cfg_t *cfg, oidc_jwk_t **jwk, oidc_jwt_t **jwt,
apr_byte_t use_psa_for_rsa);
apr_byte_t oidc_proto_jwt_sign_and_serialize(request_rec *r, oidc_jwk_t *jwk, oidc_jwt_t *jwt, char **cser);

// pkce.c
#define OIDC_PKCE_METHOD_PLAIN "plain"
#define OIDC_PKCE_METHOD_S256 "S256"
Expand All @@ -179,6 +179,12 @@ extern oidc_proto_pkce_t oidc_pkce_s256;
const char *oidc_proto_state_get_pkce_state(oidc_proto_state_t *proto_state);
void oidc_proto_state_set_pkce_state(oidc_proto_state_t *proto_state, const char *pkce_state);

// proto.c
apr_byte_t oidc_proto_generate_nonce(request_rec *r, char **nonce, int len);
apr_array_header_t *oidc_proto_supported_flows(apr_pool_t *pool);
apr_byte_t oidc_proto_flow_is_supported(apr_pool_t *pool, const char *flow);
int oidc_proto_return_www_authenticate(request_rec *r, const char *error, const char *error_description);

// request.c
int oidc_proto_request_auth(request_rec *r, struct oidc_provider_t *provider, const char *login_hint,
const char *redirect_uri, const char *state, oidc_proto_state_t *proto_state,
Expand Down
4 changes: 3 additions & 1 deletion src/proto/request.c
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,8 @@ static char *oidc_request_uri_request_object(request_rec *r, struct oidc_provide
return serialized_request_object;
}

#define OIDC_PROTO_REQUEST_URI_REF_LEN 16

/*
* generate a request object and pass it by reference in the authorization request
*/
Expand All @@ -527,7 +529,7 @@ static char *oidc_proto_request_uri_create(request_rec *r, struct oidc_provider_
char *request_uri = NULL;
if (serialized_request_object != NULL) {
char *request_ref = NULL;
if (oidc_util_generate_random_string(r, &request_ref, 16) == TRUE) {
if (oidc_util_generate_random_string(r, &request_ref, OIDC_PROTO_REQUEST_URI_REF_LEN) == TRUE) {
oidc_cache_set_request_uri(r, request_ref, serialized_request_object,
apr_time_now() + apr_time_from_sec(ttl));
request_uri = apr_psprintf(r->pool, "%s?%s=%s", resolver_url, OIDC_PROTO_REQUEST_URI,
Expand Down

0 comments on commit b7d7f32

Please sign in to comment.