Skip to content

Commit

Permalink
refactoring: move oidc_userinfo_pass_as/create_signed_jwt to userinfo.c
Browse files Browse the repository at this point in the history
Signed-off-by: Hans Zandbelt <[email protected]>
  • Loading branch information
zandbelt committed Sep 21, 2024
1 parent ea3af87 commit f876f9f
Show file tree
Hide file tree
Showing 4 changed files with 186 additions and 181 deletions.
3 changes: 3 additions & 0 deletions src/handle/handle.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
#ifndef _MOD_AUTH_OPENIDC_HANDLE_H_
#define _MOD_AUTH_OPENIDC_HANDLE_H_

#include "cfg/dir.h"
#include "const.h" // for the PACKAGE_* defines
#include "jose.h"
#include "session.h"
Expand Down Expand Up @@ -136,5 +137,7 @@ const char *oidc_userinfo_retrieve_claims(request_rec *r, oidc_cfg_t *c, oidc_pr
oidc_session_t *session, char *id_token_sub, char **userinfo_jwt);
apr_byte_t oidc_userinfo_refresh_claims(request_rec *r, oidc_cfg_t *cfg, oidc_session_t *session,
apr_byte_t *needs_save);
void oidc_userinfo_pass_as(request_rec *r, oidc_cfg_t *cfg, oidc_session_t *session, const char *s_claims,
oidc_appinfo_pass_in_t pass_in, oidc_appinfo_encoding_t encoding);

#endif // _MOD_AUTH_OPENIDC_HANDLE_H_
180 changes: 180 additions & 0 deletions src/handle/userinfo.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
#include "handle/handle.h"
#include "mod_auth_openidc.h"
#include "proto/proto.h"
#include "util.h"

/*
* store claims resolved from the userinfo endpoint in the session
Expand Down Expand Up @@ -239,3 +240,182 @@ apr_byte_t oidc_userinfo_refresh_claims(request_rec *r, oidc_cfg_t *cfg, oidc_se

return rc;
}

#define OIDC_USERINFO_SIGNED_JWT_EXP_DEFAULT 60
#define OIDC_USERINFO_SIGNED_JWT_CACHE_TTL_DEFAULT -1
#define OIDC_USERINFO_SIGNED_JWT_CACHE_TTL_ENVVAR "OIDC_USERINFO_SIGNED_JWT_CACHE_TTL"

/*
* obtain the signed JWT cache TTL from the environment variables
*/
static int oidc_userinfo_signed_jwt_cache_ttl(request_rec *r) {
const char *s_ttl = apr_table_get(r->subprocess_env, OIDC_USERINFO_SIGNED_JWT_CACHE_TTL_ENVVAR);
return _oidc_str_to_int(s_ttl, OIDC_USERINFO_SIGNED_JWT_CACHE_TTL_DEFAULT);
}

/*
* create a signed JWT with s_claims payload and return the serialized form in cser
*/
static apr_byte_t oidc_userinfo_create_signed_jwt(request_rec *r, oidc_cfg_t *cfg, oidc_session_t *session,
const char *s_claims, char **cser) {
apr_byte_t rv = FALSE;
oidc_jwt_t *jwt = NULL;
oidc_jwk_t *jwk = NULL;
oidc_jose_error_t err;
apr_time_t access_token_expires = -1;
char *jti = NULL;
char *key = NULL;
json_t *json = NULL;
int ttl = 0;
int exp = 0;
apr_time_t expiry = 0;

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

if (oidc_proto_jwt_create_from_first_pkey(r, cfg, &jwk, &jwt, FALSE) == FALSE)
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,
json_string(oidc_cfg_provider_issuer_get(oidc_cfg_provider_get(cfg))));

oidc_util_decode_json_object(r, s_claims, &json);
if (json == NULL)
goto end;
if (oidc_util_json_merge(r, json, jwt->payload.value.json) == FALSE)
goto end;
s_claims = oidc_util_encode_json_object(r, jwt->payload.value.json, JSON_PRESERVE_ORDER | JSON_COMPACT);
if (oidc_jose_hash_and_base64url_encode(r->pool, OIDC_JOSE_ALG_SHA256, s_claims, _oidc_strlen(s_claims) + 1,
&key, &err) == FALSE) {
oidc_error(r, "oidc_jose_hash_and_base64url_encode failed: %s", oidc_jose_e2s(r->pool, err));
goto end;
}

ttl = oidc_userinfo_signed_jwt_cache_ttl(r);
if (ttl > -1)
oidc_cache_get_signed_jwt(r, key, cser);

if (*cser != NULL) {
oidc_debug(r, "signed JWT found in cache");
rv = TRUE;
goto end;
}

if (json_object_get(jwt->payload.value.json, OIDC_CLAIM_JTI) == NULL) {
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) {
json_object_set_new(jwt->payload.value.json, OIDC_CLAIM_IAT,
json_integer(apr_time_sec(apr_time_now())));
}
if (json_object_get(jwt->payload.value.json, OIDC_CLAIM_EXP) == NULL) {
access_token_expires = oidc_session_get_access_token_expires(r, session);
json_object_set_new(jwt->payload.value.json, OIDC_CLAIM_EXP,
json_integer(access_token_expires > 0 ? apr_time_sec(access_token_expires)
: apr_time_sec(apr_time_now()) +
OIDC_USERINFO_SIGNED_JWT_EXP_DEFAULT));
}

if (oidc_proto_jwt_sign_and_serialize(r, jwk, jwt, cser) == FALSE)
goto end;

rv = TRUE;

if (ttl < 0)
goto end;

if (ttl == 0) {
// need to get the cache ttl from the exp claim
oidc_util_json_object_get_int(jwt->payload.value.json, OIDC_CLAIM_EXP, &exp, 0);
// actually the exp claim always exists by now
expiry = (exp > 0) ? apr_time_from_sec(exp)
: apr_time_now() + apr_time_from_sec(OIDC_USERINFO_SIGNED_JWT_EXP_DEFAULT);
} else {
// ttl > 0
expiry = apr_time_now() + apr_time_from_sec(ttl);
}

oidc_debug(r, "caching signed JWT with ~ttl(%ld)", apr_time_sec(expiry - apr_time_now()));
oidc_cache_set_signed_jwt(r, key, *cser, expiry);

end:

if (json)
json_decref(json);

if (jwt)
oidc_jwt_destroy(jwt);

return rv;
}

/*
* pass the userinfo claims to headers and/or environment variables, encoded as configured
*/
void oidc_userinfo_pass_as(request_rec *r, oidc_cfg_t *cfg, oidc_session_t *session, const char *s_claims,
oidc_appinfo_pass_in_t pass_in, oidc_appinfo_encoding_t encoding) {
const apr_array_header_t *pass_userinfo_as = NULL;
oidc_pass_user_info_as_t *p = NULL;
int i = 0;
char *cser = NULL;

pass_userinfo_as = oidc_cfg_dir_pass_userinfo_as_get(r);

#ifdef USE_LIBJQ
s_claims = oidc_util_jq_filter(r, s_claims, oidc_cfg_dir_userinfo_claims_expr_get(r));
#endif

for (i = 0; (pass_userinfo_as != NULL) && (i < pass_userinfo_as->nelts); i++) {

p = APR_ARRAY_IDX(pass_userinfo_as, i, oidc_pass_user_info_as_t *);

switch (p->type) {

case OIDC_PASS_USERINFO_AS_CLAIMS:
/* set the userinfo claims in the app headers */
oidc_set_app_claims(r, cfg, s_claims);
break;

case OIDC_PASS_USERINFO_AS_JSON_OBJECT:
/* pass the userinfo JSON object to the app in a header or environment variable */
oidc_util_set_app_info(r, p->name ? p->name : OIDC_APP_INFO_USERINFO_JSON, s_claims,
p->name ? "" : OIDC_DEFAULT_HEADER_PREFIX, pass_in, encoding);
break;

case OIDC_PASS_USERINFO_AS_JWT:
if (oidc_cfg_session_type_get(cfg) != OIDC_SESSION_TYPE_CLIENT_COOKIE) {
/* get the compact serialized JWT from the session */
const char *s_userinfo_jwt = oidc_session_get_userinfo_jwt(r, session);
if (s_userinfo_jwt != NULL) {
/* pass the compact serialized JWT to the app in a header or environment
* variable */
oidc_util_set_app_info(
r, p->name ? p->name : OIDC_APP_INFO_USERINFO_JWT, s_userinfo_jwt,
p->name ? "" : OIDC_DEFAULT_HEADER_PREFIX, pass_in, encoding);
} else {
oidc_debug(
r,
"configured to pass userinfo in a JWT, but no such JWT was found in the "
"session (probably no such JWT was returned from the userinfo endpoint)");
}
} else {
oidc_error(r, "session type \"client-cookie\" does not allow storing/passing a "
"userinfo JWT; use \"" OIDCSessionType " server-cache\" for that");
}
break;

case OIDC_PASS_USERINFO_AS_SIGNED_JWT:

if (oidc_userinfo_create_signed_jwt(r, cfg, session, s_claims, &cser) == TRUE) {
oidc_util_set_app_info(r, p->name ? p->name : OIDC_APP_INFO_SIGNED_JWT, cser,
p->name ? "" : OIDC_DEFAULT_HEADER_PREFIX, pass_in, encoding);
}
break;

default:
break;
}
}
}
Loading

0 comments on commit f876f9f

Please sign in to comment.