Skip to content

Commit 2163948

Browse files
committed
refresh: fix releasing the best effort distributed refresh lock
immediately after refreshing the access token fails to avoid parallel requests queuing up and blocking threads; also avoid trying the same prevously failed refresh request again, at least for 30 seconds Signed-off-by: Hans Zandbelt <[email protected]>
1 parent 635f3b6 commit 2163948

2 files changed

Lines changed: 44 additions & 9 deletions

File tree

ChangeLog

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
01/28/2026
2+
- refresh: fix releasing the best effort distributed refresh lock immediately after refreshing
3+
the access token fails to avoid parallel requests queuing up and blocking threads; also
4+
avoid trying the same prevously failed refresh request again, at least for 30 seconds
5+
16
01/03/2026
27
- test: fix long int test in test_util.c so that it fits within a 32bit long int size; see #1375; thanks @moschlar
38
- update copyright year to 2026

src/handle/refresh.c

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@
6969
/* needs to be larger than a few characters for cache compression to work... */
7070
#define OIDC_REFRESH_LOCK_VALUE "needstobelargerthanafewcharacters"
7171

72+
/* needs to be larger than a few characters for cache compression to work... */
73+
#define OIDC_REFRESH_FAILED_LOCK_VALUE "alsoneedstobelargerthanafewcharactersbutdifferent"
74+
7275
/*
7376
* cache refresh token grant results for a while to avoid (almost) parallel requests
7477
*/
@@ -103,16 +106,23 @@ static void oidc_refresh_token_cache_set(request_rec *r, oidc_cfg_t *c, const ch
103106
json_decref(json);
104107
}
105108

109+
typedef enum {
110+
OIDC_REFRESH_CACHE_SUCCESS,
111+
OIDC_REFRESH_CACHE_ERROR,
112+
OIDC_REFRESH_CACHE_ABORT
113+
} oidc_refresh_token_cache_result_t;
106114
/*
107115
* obtain recent refresh token grant results from the cache
108116
*/
109-
static apr_byte_t oidc_refresh_token_cache_get(request_rec *r, oidc_cfg_t *c, const char *refresh_token,
110-
char **s_access_token, char **s_token_type, int *expires_in,
111-
char **s_id_token, char **s_refresh_token, apr_time_t *ts) {
117+
static oidc_refresh_token_cache_result_t oidc_refresh_token_cache_get(request_rec *r, oidc_cfg_t *c,
118+
const char *refresh_token, char **s_access_token,
119+
char **s_token_type, int *expires_in,
120+
char **s_id_token, char **s_refresh_token,
121+
apr_time_t *ts) {
112122

113123
char *s_json = NULL;
114124
json_t *json = NULL, *v = NULL;
115-
apr_byte_t rv = FALSE;
125+
oidc_refresh_token_cache_result_t rv = OIDC_REFRESH_CACHE_ERROR;
116126

117127
oidc_cache_mutex_lock(r->pool, r->server, oidc_cfg_refresh_mutex_get(c));
118128

@@ -130,9 +140,20 @@ static apr_byte_t oidc_refresh_token_cache_get(request_rec *r, oidc_cfg_t *c, co
130140
oidc_cache_get_refresh_token(r, refresh_token, &s_json);
131141
}
132142

143+
if (s_json == NULL)
144+
goto no_cache_found;
145+
146+
/* check if we have run into an error */
147+
if (_oidc_strcmp(s_json, OIDC_REFRESH_FAILED_LOCK_VALUE) == 0) {
148+
oidc_debug(r, "refresh token %s failed to refresh before, do not try to refresh it again but fail",
149+
refresh_token);
150+
rv = OIDC_REFRESH_CACHE_ABORT;
151+
goto end;
152+
}
153+
133154
/* check if we have run into a timeout */
134-
if ((s_json == NULL) || (_oidc_strcmp(s_json, OIDC_REFRESH_LOCK_VALUE) == 0)) {
135-
oidc_warn(r, "timeout waiting for refresh grant cache results");
155+
if (_oidc_strcmp(s_json, OIDC_REFRESH_LOCK_VALUE) == 0) {
156+
oidc_warn(r, "timeout waiting for refresh token %s cache to unlock", refresh_token);
136157
// TODO: now we are going to refresh ourselves with a refresh token that has already been
137158
// tried before; that is not great in rolling refresh token setups but I guess we have no
138159
// other choice anyhow...
@@ -162,7 +183,7 @@ static apr_byte_t oidc_refresh_token_cache_get(request_rec *r, oidc_cfg_t *c, co
162183
/* cleanup */
163184
json_decref(json);
164185

165-
rv = TRUE;
186+
rv = OIDC_REFRESH_CACHE_SUCCESS;
166187

167188
goto end;
168189

@@ -207,6 +228,7 @@ apr_byte_t oidc_refresh_token_grant(request_rec *r, oidc_cfg_t *c, oidc_session_
207228
oidc_jose_error_t err;
208229
const char *refresh_token = NULL;
209230
apr_time_t ts = 0;
231+
oidc_refresh_token_cache_result_t rv = OIDC_REFRESH_CACHE_ERROR;
210232

211233
oidc_debug(r, "enter");
212234

@@ -218,9 +240,13 @@ apr_byte_t oidc_refresh_token_grant(request_rec *r, oidc_cfg_t *c, oidc_session_
218240
}
219241

220242
/* see if it was refreshed very recently and we can re-use the results from the cache */
221-
if (oidc_refresh_token_cache_get(r, c, refresh_token, &s_access_token, &s_token_type, &expires_in, &s_id_token,
222-
&s_refresh_token, &ts) == TRUE)
243+
rv = oidc_refresh_token_cache_get(r, c, refresh_token, &s_access_token, &s_token_type, &expires_in, &s_id_token,
244+
&s_refresh_token, &ts);
245+
if (rv == OIDC_REFRESH_CACHE_SUCCESS)
223246
goto process;
247+
if (rv == OIDC_REFRESH_CACHE_ABORT)
248+
/* a prior refresh of the access token failed and we won't try the same again */
249+
goto end;
224250

225251
oidc_debug(r, "refreshing refresh_token: %s", refresh_token);
226252

@@ -231,6 +257,10 @@ apr_byte_t oidc_refresh_token_grant(request_rec *r, oidc_cfg_t *c, oidc_session_
231257
&expires_in, &s_refresh_token, &s_scope) == FALSE) {
232258
OIDC_METRICS_COUNTER_INC(r, c, OM_PROVIDER_REFRESH_ERROR);
233259
oidc_error(r, "access_token could not be refreshed with refresh_token: %s", refresh_token);
260+
/* release the refresh lock and indicate this refresh token should not be refreshed anymore for at least
261+
* 30 seconds */
262+
oidc_cache_set_refresh_token(r, refresh_token, OIDC_REFRESH_FAILED_LOCK_VALUE,
263+
apr_time_now() + apr_time_from_sec(OIDC_REFRESH_CACHE_TTL));
234264
goto end;
235265
}
236266

0 commit comments

Comments
 (0)