Coverage Report

Created: 2026-10-17 07:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mod_auth_openidc/src/session.c
Line
Count
Source
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one
3
 * or more contributor license agreements.  See the NOTICE file
4
 * distributed with this work for additional information
5
 * regarding copyright ownership.  The ASF licenses this file
6
 * to you under the Apache License, Version 2.0 (the
7
 * "License"); you may not use this file except in compliance
8
 * with the License.  You may obtain a copy of the License at
9
 *
10
 *   http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing,
13
 * software distributed under the License is distributed on an
14
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
 * KIND, either express or implied.  See the License for the
16
 * specific language governing permissions and limitations
17
 * under the License.
18
 */
19
20
/***************************************************************************
21
 * Copyright (C) 2017-2026 ZmartZone Holding BV
22
 * Copyright (C) 2013-2017 Ping Identity Corporation
23
 * All rights reserved.
24
 *
25
 * DISCLAIMER OF WARRANTIES:
26
 *
27
 * THE SOFTWARE PROVIDED HEREUNDER IS PROVIDED ON AN "AS IS" BASIS, WITHOUT
28
 * ANY WARRANTIES OR REPRESENTATIONS EXPRESS, IMPLIED OR STATUTORY; INCLUDING,
29
 * WITHOUT LIMITATION, WARRANTIES OF QUALITY, PERFORMANCE, NONINFRINGEMENT,
30
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  NOR ARE THERE ANY
31
 * WARRANTIES CREATED BY A COURSE OR DEALING, COURSE OF PERFORMANCE OR TRADE
32
 * USAGE.  FURTHERMORE, THERE ARE NO WARRANTIES THAT THE SOFTWARE WILL MEET
33
 * YOUR NEEDS OR BE FREE FROM ERRORS, OR THAT THE OPERATION OF THE SOFTWARE
34
 * WILL BE UNINTERRUPTED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
35
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
36
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES HOWEVER CAUSED AND ON ANY THEORY OF
37
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
38
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
39
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40
 *
41
 * @Author: Hans Zandbelt - hans.zandbelt@openidc.com
42
 */
43
44
#include "session.h"
45
#include "cfg/dir.h"
46
#include "jose.h"
47
#include "metrics.h"
48
#include "util/util.h"
49
#include "util/util_cfg.h"
50
51
/* the name of the remote-user attribute in the session  */
52
0
#define OIDC_SESSION_REMOTE_USER_KEY "r"
53
/* the name of the session expiry attribute in the session */
54
0
#define OIDC_SESSION_EXPIRY_KEY "e"
55
/* the name of the session identifier in the session */
56
0
#define OIDC_SESSION_SESSION_ID "i"
57
/* the name of the sub attribute in the session */
58
0
#define OIDC_SESSION_SUB_KEY "sub"
59
/* the name of the sid attribute in the session */
60
0
#define OIDC_SESSION_SID_KEY "sid"
61
/* key for storing the userinfo claims in the session context */
62
0
#define OIDC_SESSION_KEY_USERINFO_CLAIMS "uic"
63
/* key for storing the id_token claims in the session context */
64
0
#define OIDC_SESSION_KEY_IDTOKEN_CLAIMS "idc"
65
/* the name of the payload format version attribute in the session */
66
0
#define OIDC_SESSION_FORMAT_VERSION_KEY "v"
67
68
/*
69
 * Bump only when older readers cannot safely interpret the payload. Adding keys is compatible
70
 * because readers ignore unknown keys. A missing version means the original, version-1 layout.
71
 */
72
0
#define OIDC_SESSION_FORMAT_VERSION 1
73
74
/*
75
 * encode/serialize the session object/data into a string, possibly a serialized encrypted JWT when encryption is
76
 * requested
77
 */
78
static apr_byte_t oidc_session_encode(request_rec *r, const oidc_cfg_t *c, const oidc_session_t *z, char **s_value,
79
0
              apr_byte_t encrypt) {
80
81
0
  if (encrypt == FALSE) {
82
0
    *s_value = oidc_json_encode(r->pool, z->state, OIDC_JSON_COMPACT);
83
0
    return (*s_value != NULL);
84
0
  } else if (oidc_cfg_crypto_passphrase_secret1_get(c) == NULL) {
85
0
    oidc_error(r, "cannot encrypt session state because " OIDCCryptoPassphrase " is not set");
86
0
    return FALSE;
87
0
  }
88
89
0
  if (oidc_util_jwt_create(r, oidc_cfg_crypto_passphrase_get(c),
90
0
         oidc_json_encode(r->pool, z->state, OIDC_JSON_COMPACT), s_value) == FALSE)
91
0
    return FALSE;
92
93
0
  return TRUE;
94
0
}
95
96
/* Reject payloads from newer layouts so the caller re-authenticates instead of misreading them. */
97
0
static apr_byte_t oidc_session_version_supported(request_rec *r, oidc_session_t *z) {
98
0
  int version = 0;
99
  /* absent: written before the field existed, i.e. the original layout */
100
0
  oidc_json_object_get_int(z->state, OIDC_SESSION_FORMAT_VERSION_KEY, &version, 1);
101
0
  if (version > OIDC_SESSION_FORMAT_VERSION) {
102
0
    oidc_warn(r,
103
0
        "discarding a session in payload format version %d: this module writes and understands "
104
0
        "up to version %d, so it was written by a newer mod_auth_openidc sharing this cache",
105
0
        version, OIDC_SESSION_FORMAT_VERSION);
106
    /* the rejected object is refcounted, not pool-owned: drop it here, because the
107
     * callers treat FALSE as "no session" and a fallback load
108
     * (OIDCSessionCacheFallbackToCookie) overwrites the pointer and leaks it */
109
0
    oidc_json_decref(z->state);
110
0
    z->state = NULL;
111
0
    return FALSE;
112
0
  }
113
0
  return TRUE;
114
0
}
115
116
/*
117
 * parse a session object from the provided string, which may be an encrypted JWT is encryption is on
118
 */
119
static apr_byte_t oidc_session_decode(request_rec *r, const oidc_cfg_t *c, oidc_session_t *z, const char *s_json,
120
0
              apr_byte_t encrypt) {
121
0
  char *s_payload = NULL;
122
123
0
  if (encrypt == FALSE) {
124
0
    if (oidc_json_decode_object(r, s_json, &z->state) == FALSE)
125
0
      return FALSE;
126
0
    return oidc_session_version_supported(r, z);
127
0
  } else if (oidc_cfg_crypto_passphrase_secret1_get(c) == NULL) {
128
0
    oidc_error(r, "cannot decrypt session state because " OIDCCryptoPassphrase " is not set");
129
0
    return FALSE;
130
0
  }
131
132
0
  if (oidc_util_jwt_verify(r, oidc_cfg_crypto_passphrase_get(c), s_json, &s_payload) == FALSE) {
133
0
    oidc_error(r, "could not verify secure JWT: cache value possibly corrupted");
134
0
    return FALSE;
135
0
  }
136
137
0
  if (oidc_json_decode_object(r, s_payload, &z->state) == FALSE)
138
0
    return FALSE;
139
140
0
  return oidc_session_version_supported(r, z);
141
0
}
142
143
0
#define OIDC_SESSION_ID_LEN 20
144
145
/*
146
 * generate a unique identifier for a session
147
 */
148
0
void oidc_session_id_new(request_rec *r, oidc_session_t *z) {
149
0
  z->uuid = oidc_util_rand_hex_str(r, r->pool, OIDC_SESSION_ID_LEN);
150
0
}
151
152
/*
153
 * clear contents of a session
154
 */
155
0
static void oidc_session_clear(request_rec *r, oidc_session_t *z) {
156
0
  z->remote_user = NULL;
157
  // NB: don't clear sid or uuid
158
0
  z->expiry = 0;
159
0
  if (z->state) {
160
0
    oidc_json_decref(z->state);
161
0
    z->state = NULL;
162
0
  }
163
0
}
164
165
/*
166
 * Replace the presented session on login. Reusing its id or contents would allow a planted
167
 * server-cache cookie to survive authentication; client-cookie sessions are re-keyed for
168
 * consistent behavior.
169
 */
170
0
void oidc_session_reset(request_rec *r, const oidc_cfg_t *c, oidc_session_t *z) {
171
0
  if ((oidc_cfg_session_type_get(c) == OIDC_SESSION_TYPE_SERVER_CACHE) && (z->uuid != NULL))
172
0
    oidc_cache_set_session(r, z->uuid, NULL, 0);
173
0
  oidc_session_clear(r, z);
174
  /* these two index the session for back-channel logout and belong to the previous occupant */
175
0
  z->sid = NULL;
176
0
  z->sub = NULL;
177
0
  oidc_session_id_new(r, z);
178
0
}
179
180
/*
181
 * get a value from the session based on the name from a name/value pair
182
 */
183
0
static apr_byte_t oidc_session_get(request_rec *r, const oidc_session_t *z, const char *key, char **value) {
184
185
  /* just return the value for the key */
186
0
  oidc_json_object_get_string(r->pool, z->state, key, value, NULL);
187
188
0
  return TRUE;
189
0
}
190
191
/*
192
 * set a name/value key pair in the session
193
 */
194
0
static apr_byte_t oidc_session_json_set(request_rec *r, oidc_session_t *z, const char *key, oidc_json_t *value) {
195
196
  /* only set it if non-NULL, otherwise delete the entry */
197
0
  if (value) {
198
0
    if (z->state == NULL)
199
0
      z->state = oidc_json_object();
200
0
    oidc_json_object_set_new(z->state, key, value);
201
0
  } else if (z->state != NULL) {
202
0
    oidc_json_object_del(z->state, key);
203
0
  }
204
205
0
  return TRUE;
206
0
}
207
208
/*
209
 * get a json object from the session based on the name from a name/value pair
210
 */
211
0
static oidc_json_t *oidc_session_json_get(request_rec *r, const oidc_session_t *z, const char *key) {
212
0
  return oidc_json_object_get(z->state, key);
213
0
}
214
215
/*
216
 * set a name/string key pair in the session
217
 */
218
0
static apr_byte_t oidc_session_set(request_rec *r, oidc_session_t *z, const char *key, const char *value) {
219
0
  return oidc_session_json_set(r, z, key, value ? oidc_json_string(value) : NULL);
220
0
}
221
222
/*
223
 * load the session from the session cache, indexed by its uuid session id
224
 */
225
0
apr_byte_t oidc_session_load_cache_by_uuid(request_rec *r, const oidc_cfg_t *c, const char *uuid, oidc_session_t *z) {
226
0
  char *stored_uuid = NULL;
227
0
  char *s_json = NULL;
228
0
  apr_byte_t rc = FALSE;
229
230
0
  rc = oidc_cache_get_session(r, uuid, &s_json);
231
232
0
  if ((rc == TRUE) && (s_json != NULL)) {
233
0
    rc = oidc_session_decode(r, c, z, s_json, FALSE);
234
0
    if (rc == TRUE) {
235
0
      z->uuid = apr_pstrdup(r->pool, uuid);
236
237
      /* compare the session id in the cache value so it allows  us to detect cache corruption */
238
0
      oidc_session_get(r, z, OIDC_SESSION_SESSION_ID, &stored_uuid);
239
0
      if ((stored_uuid == NULL) || (_oidc_strcmp(stored_uuid, uuid) != 0)) {
240
0
        oidc_error(r,
241
0
             "cache corruption detected: stored session id (%s) is not equal to "
242
0
             "requested session id (%s)",
243
0
             oidc_session_id_fingerprint(r, stored_uuid),
244
0
             oidc_session_id_fingerprint(r, uuid));
245
246
        /* delete the cache entry */
247
0
        oidc_cache_set_session(r, z->uuid, NULL, 0);
248
        /* clear the session */
249
0
        oidc_session_clear(r, z);
250
251
0
        rc = FALSE;
252
0
      }
253
0
    }
254
0
  }
255
256
0
  return rc;
257
0
}
258
259
/*
260
 * load the session from the cache using the cookie as the index
261
 */
262
0
static apr_byte_t oidc_session_load_cache(request_rec *r, oidc_session_t *z) {
263
0
  const oidc_cfg_t *c = ap_get_module_config(r->server->module_config, &auth_openidc_module);
264
265
0
  apr_byte_t rc = FALSE;
266
267
  /* get the cookie that should be our uuid/key */
268
0
  const char *uuid = oidc_http_get_cookie(r, oidc_cfg_dir_cookie_get(r));
269
270
  /* get the string-encoded session from the cache based on the key; decryption is based on the cache backend
271
   * config */
272
0
  if (uuid != NULL) {
273
274
0
    rc = oidc_session_load_cache_by_uuid(r, c, uuid, z);
275
276
    /* cache backend experienced an error while attempting lookup */
277
0
    if (rc == FALSE) {
278
0
      oidc_error(r, "cache backend failure for key %s", oidc_session_id_fingerprint(r, uuid));
279
0
      return FALSE;
280
0
    }
281
282
    /* cache backend does not contain an entry for the given key */
283
0
    if (z->state == NULL) {
284
285
      /*
286
       * With OIDCSessionCacheFallbackToCookie this may be a complete session, not a cache key.
287
       * Preserve it and report failure so oidc_session_load() can try the cookie fallback.
288
       */
289
0
      if (oidc_cfg_session_cache_fallback_to_cookie_get(c))
290
0
        return FALSE;
291
292
      /* delete the session cookie */
293
0
      oidc_http_set_cookie(r, oidc_cfg_dir_cookie_get(r), "", 0,
294
0
               OIDC_HTTP_COOKIE_SAMESITE_NONE(c, r));
295
0
    }
296
0
  }
297
298
0
  return rc;
299
0
}
300
301
0
static const char *oidc_session_cookie_samesite(const request_rec *r, const struct oidc_cfg_t *c, int first_time) {
302
0
  const char *rv = NULL;
303
0
  switch (oidc_cfg_cookie_same_site_session_get(c)) {
304
0
  case OIDC_SAMESITE_COOKIE_STRICT:
305
0
    rv = first_time ? OIDC_HTTP_COOKIE_SAMESITE_LAX : OIDC_HTTP_COOKIE_SAMESITE_STRICT;
306
0
    break;
307
0
  case OIDC_SAMESITE_COOKIE_LAX:
308
0
    rv = OIDC_HTTP_COOKIE_SAMESITE_LAX;
309
0
    break;
310
0
  case OIDC_SAMESITE_COOKIE_NONE:
311
0
    rv = OIDC_HTTP_COOKIE_SAMESITE_NONE(c, r);
312
0
    break;
313
0
  case OIDC_SAMESITE_COOKIE_DISABLED:
314
0
    break;
315
0
  default:
316
0
    break;
317
0
  }
318
0
  return rv;
319
0
}
320
321
/*
322
 * save the session to the cache using a cookie for the index
323
 */
324
0
static apr_byte_t oidc_session_save_cache(request_rec *r, oidc_session_t *z, oidc_session_save_t first_time) {
325
0
  const oidc_cfg_t *c = ap_get_module_config(r->server->module_config, &auth_openidc_module);
326
327
0
  apr_byte_t rc = TRUE;
328
329
0
  if (z->state != NULL) {
330
331
0
    if (z->sid != NULL) {
332
0
      oidc_cache_set_sid(r, z->sid, z->uuid, z->expiry);
333
0
      oidc_session_set(r, z, OIDC_SESSION_SID_KEY, z->sid);
334
0
    }
335
336
    /* secondary "sub"-based logout index (shares the sid cache namespace, distinct key) */
337
0
    if (z->sub != NULL) {
338
0
      oidc_cache_set_sid(r, z->sub, z->uuid, z->expiry);
339
0
      oidc_session_set(r, z, OIDC_SESSION_SUB_KEY, z->sub);
340
0
    }
341
342
    /* store the string-encoded session in the cache; encryption depends on cache backend settings */
343
0
    char *s_value = NULL;
344
0
    if (oidc_session_encode(r, c, z, &s_value, FALSE) == FALSE)
345
0
      return FALSE;
346
347
0
    rc = oidc_cache_set_session(r, z->uuid, s_value, z->expiry);
348
0
    if (rc == TRUE)
349
      /* set the uuid in the cookie */
350
0
      oidc_http_set_cookie(r, oidc_cfg_dir_cookie_get(r), z->uuid,
351
0
               oidc_cfg_persistent_session_cookie_get(c) ? z->expiry : -1,
352
0
               oidc_session_cookie_samesite(r, c, first_time));
353
354
0
  } else {
355
356
0
    if (z->sid != NULL)
357
0
      oidc_cache_set_sid(r, z->sid, NULL, 0);
358
0
    if (z->sub != NULL) {
359
      /* Delete the shared sub index only when it still points to this session. */
360
0
      char *sub_uuid = NULL;
361
0
      oidc_cache_get_sid(r, z->sub, &sub_uuid);
362
0
      if ((sub_uuid == NULL) || (_oidc_strcmp(sub_uuid, z->uuid) == 0))
363
0
        oidc_cache_set_sid(r, z->sub, NULL, 0);
364
0
    }
365
366
    /* clear the cookie */
367
0
    oidc_http_set_cookie(r, oidc_cfg_dir_cookie_get(r), "", 0, OIDC_HTTP_COOKIE_SAMESITE_NONE(c, r));
368
369
    /* remove the session from the cache */
370
0
    rc = oidc_cache_set_session(r, z->uuid, NULL, 0);
371
0
  }
372
373
0
  return rc;
374
0
}
375
376
/*
377
 * load the session from a self-contained client-side cookie
378
 */
379
0
static apr_byte_t oidc_session_load_cookie(request_rec *r, const oidc_cfg_t *c, oidc_session_t *z) {
380
0
  const char *cookieValue =
381
0
      oidc_http_get_chunked_cookie(r, oidc_cfg_dir_cookie_get(r), oidc_cfg_session_cookie_chunk_size_get(c));
382
383
0
  if (cookieValue == NULL)
384
0
    return TRUE;
385
386
0
  if (oidc_session_decode(r, c, z, cookieValue, TRUE) == FALSE)
387
0
    return FALSE;
388
389
0
  return TRUE;
390
0
}
391
392
/*
393
 * store the session in a self-contained client-side-only cookie storage
394
 */
395
0
static apr_byte_t oidc_session_save_cookie(request_rec *r, const oidc_session_t *z, oidc_session_save_t first_time) {
396
0
  const oidc_cfg_t *c = ap_get_module_config(r->server->module_config, &auth_openidc_module);
397
0
  char *cookieValue = "";
398
0
  if ((z->state != NULL) && (oidc_session_encode(r, c, z, &cookieValue, TRUE) == FALSE))
399
0
    return FALSE;
400
401
  /* Reject sessions that exceed the cookie-chunk limit to avoid an authentication loop. */
402
0
  return oidc_http_set_chunked_cookie(
403
0
      r, oidc_cfg_dir_cookie_get(r), cookieValue, oidc_cfg_persistent_session_cookie_get(c) ? z->expiry : -1,
404
0
      oidc_cfg_session_cookie_chunk_size_get(c),
405
0
      (z->state == NULL) ? OIDC_HTTP_COOKIE_SAMESITE_NONE(c, r) : oidc_session_cookie_samesite(r, c, first_time));
406
0
}
407
408
/*
409
 * retrieve an integer from the session state
410
 */
411
0
static inline int oidc_session_get_int(request_rec *r, const oidc_session_t *z, const char *key, int def_val) {
412
0
  int v;
413
0
  oidc_json_object_get_int(z->state, key, &v, def_val);
414
0
  return v;
415
0
}
416
417
/*
418
 * retrieve a timestamp from the session state
419
 */
420
0
static inline apr_time_t oidc_session_get_key2timestamp(request_rec *r, const oidc_session_t *z, const char *key) {
421
0
  oidc_json_int_t value = -1;
422
0
  oidc_json_object_get_int64(z->state, key, &value, -1);
423
  /* saturate rather than wrap, guarding a tampered client-cookie session with an oversized value */
424
0
  return (value > -1) ? oidc_util_apr_time_from_sec((double)value) : -1;
425
0
}
426
427
/*
428
 * parse data from the session state into the session struct members
429
 */
430
0
apr_byte_t oidc_session_extract(request_rec *r, oidc_session_t *z) {
431
0
  apr_byte_t rc = FALSE;
432
0
  const oidc_json_t *json = NULL;
433
434
0
  if (z->state == NULL)
435
0
    goto out;
436
437
  /* sanity check also taking into account OIDC_DONT_STORE_ID_TOKEN_CLAIMS_IN_SESSION=true */
438
0
  json = oidc_session_json_get(r, z, OIDC_SESSION_KEY_IDTOKEN_CLAIMS);
439
0
  if ((json != NULL) && (!oidc_json_is_object(json))) {
440
441
0
    oidc_error(r, "session is corrupted/incompatible: id_token claims is not a JSON object");
442
0
    oidc_session_kill(r, z);
443
444
0
    goto out;
445
0
  }
446
447
  /* check whether it has expired */
448
0
  z->expiry = oidc_session_get_key2timestamp(r, z, OIDC_SESSION_EXPIRY_KEY);
449
0
  if (apr_time_now() > z->expiry) {
450
451
0
    oidc_warn(r, "session restored from cache has expired");
452
0
    oidc_session_kill(r, z);
453
454
0
    goto out;
455
0
  }
456
457
0
  oidc_session_get(r, z, OIDC_SESSION_REMOTE_USER_KEY, &z->remote_user);
458
0
  oidc_session_get(r, z, OIDC_SESSION_SID_KEY, &z->sid);
459
0
  oidc_session_get(r, z, OIDC_SESSION_SUB_KEY, &z->sub);
460
0
  oidc_session_get(r, z, OIDC_SESSION_SESSION_ID, &z->uuid);
461
462
0
  rc = TRUE;
463
464
0
out:
465
466
0
  return rc;
467
0
}
468
469
0
#define OIDC_SESSION_ID_LOG_VAR "OIDC_SESSION_ID"
470
0
#define OIDC_SESSION_ID_FINGERPRINT_LEN 16
471
472
/*
473
 * non-reversible fingerprint of a session identifier, for the OIDC_SESSION_ID export and for log
474
 * lines: with server-side caching the identifier is the session cookie value itself, so the raw
475
 * value must never reach a log where it could be replayed as that cookie; the first 16 characters
476
 * (96 bits) of the base64url-encoded SHA-256 hash correlate just as well
477
 */
478
0
const char *oidc_session_id_fingerprint(request_rec *r, const char *uuid) {
479
0
  char *hash = NULL;
480
0
  if (uuid == NULL)
481
0
    return NULL;
482
0
  if ((oidc_util_hash_string_and_base64url_encode(r, OIDC_JOSE_ALG_SHA256, uuid, &hash) == FALSE) ||
483
0
      (hash == NULL))
484
    /* never fall back to the identifier itself */
485
0
    return "***";
486
0
  return apr_pstrndup(r->pool, hash, OIDC_SESSION_ID_FINGERPRINT_LEN);
487
0
}
488
489
/*
490
 * export a fingerprint of the session identifier into the request notes and environment so log
491
 * lines can be correlated per session with ErrorLogFormat %{OIDC_SESSION_ID}n and LogFormat
492
 * %{OIDC_SESSION_ID}e
493
 */
494
0
static void oidc_session_id_export(request_rec *r, const oidc_session_t *z) {
495
0
  const char *fingerprint = oidc_session_id_fingerprint(r, z->uuid);
496
0
  if (fingerprint == NULL)
497
0
    return;
498
0
  if (r->notes != NULL)
499
0
    apr_table_set(r->notes, OIDC_SESSION_ID_LOG_VAR, fingerprint);
500
0
  if (r->subprocess_env != NULL)
501
0
    apr_table_set(r->subprocess_env, OIDC_SESSION_ID_LOG_VAR, fingerprint);
502
0
}
503
504
/*
505
 * load a session from the cache/cookie
506
 */
507
0
apr_byte_t oidc_session_load(request_rec *r, oidc_session_t **zz) {
508
0
  const oidc_cfg_t *c = ap_get_module_config(r->server->module_config, &auth_openidc_module);
509
510
0
  apr_byte_t rc = FALSE;
511
512
  /* allocate space for the session object and fill it */
513
0
  oidc_session_t *z = (*zz = apr_pcalloc(r->pool, sizeof(oidc_session_t)));
514
0
  oidc_session_clear(r, z);
515
0
  oidc_session_id_new(r, z);
516
0
  z->sid = NULL;
517
0
  z->sub = NULL;
518
519
0
  if (oidc_cfg_session_type_get(c) == OIDC_SESSION_TYPE_SERVER_CACHE)
520
    /* load the session from the cache */
521
0
    rc = oidc_session_load_cache(r, z);
522
523
  /* if we get here we configured client-cookie or retrieving from the cache failed */
524
0
  if ((oidc_cfg_session_type_get(c) == OIDC_SESSION_TYPE_CLIENT_COOKIE) ||
525
0
      ((rc == FALSE) && oidc_cfg_session_cache_fallback_to_cookie_get(c)))
526
    /* load the session from a self-contained cookie */
527
0
    rc = oidc_session_load_cookie(r, c, z);
528
529
0
  if (rc == TRUE) {
530
0
    rc = oidc_session_extract(r, z);
531
0
    if (rc == TRUE)
532
0
      oidc_session_id_export(r, z);
533
0
  }
534
535
0
  oidc_util_set_trace_parent(r, c, z->uuid);
536
537
0
  return rc;
538
0
}
539
540
/*
541
 * store an integer value into the session state
542
 */
543
0
static void oidc_session_set_int(request_rec *r, oidc_session_t *z, const char *key, int v) {
544
0
  if (z->state == NULL)
545
0
    z->state = oidc_json_object();
546
0
  oidc_json_object_set_new(z->state, key, oidc_json_integer(v));
547
0
}
548
549
/*
550
 * store a timestamp value into the session state
551
 */
552
0
static void oidc_session_set_timestamp(request_rec *r, oidc_session_t *z, const char *key, const apr_time_t timestamp) {
553
0
  if (timestamp <= -1)
554
0
    return;
555
0
  if (z->state == NULL)
556
0
    z->state = oidc_json_object();
557
  /* store the full seconds value as a 64-bit JSON integer: the previous (int) cast wrapped any
558
   * timestamp past January 2038 into a negative, i.e. already-expired, value */
559
0
  oidc_json_object_set_new(z->state, key, oidc_json_integer(apr_time_sec(timestamp)));
560
0
}
561
562
/*
563
 * save a session to cache/cookie
564
 */
565
0
apr_byte_t oidc_session_save(request_rec *r, oidc_session_t *z, oidc_session_save_t first_time) {
566
0
  const oidc_cfg_t *c = ap_get_module_config(r->server->module_config, &auth_openidc_module);
567
568
0
  apr_byte_t rc = FALSE;
569
570
0
  if (z->state != NULL) {
571
0
    oidc_session_set_int(r, z, OIDC_SESSION_FORMAT_VERSION_KEY, OIDC_SESSION_FORMAT_VERSION);
572
0
    oidc_session_set(r, z, OIDC_SESSION_REMOTE_USER_KEY, z->remote_user);
573
0
    oidc_session_set_timestamp(r, z, OIDC_SESSION_EXPIRY_KEY, z->expiry);
574
0
    oidc_session_set(r, z, OIDC_SESSION_SESSION_ID, z->uuid);
575
0
  }
576
577
0
  if (oidc_cfg_session_type_get(c) == OIDC_SESSION_TYPE_SERVER_CACHE)
578
    /* store the session in the cache */
579
0
    rc = oidc_session_save_cache(r, z, first_time);
580
581
  /* if we get here we configured client-cookie or saving in the cache failed */
582
0
  if ((oidc_cfg_session_type_get(c) == OIDC_SESSION_TYPE_CLIENT_COOKIE) ||
583
0
      ((rc == FALSE) && oidc_cfg_session_cache_fallback_to_cookie_get(c))) {
584
0
    if (oidc_cfg_session_type_get(c) != OIDC_SESSION_TYPE_CLIENT_COOKIE)
585
0
      OIDC_METRICS_COUNTER_INC(r, c, OM_SESSION_FALLBACK_COOKIE);
586
    /* store the session in a self-contained cookie */
587
0
    rc = oidc_session_save_cookie(r, z, first_time);
588
0
  }
589
590
0
  if (rc == TRUE)
591
0
    oidc_session_id_export(r, z);
592
593
0
  return rc;
594
0
}
595
596
/*
597
 * free resources allocated for a session
598
 */
599
0
apr_byte_t oidc_session_free(request_rec *r, oidc_session_t *z) {
600
0
  oidc_session_clear(r, z);
601
0
  return TRUE;
602
0
}
603
604
/*
605
 * terminate a session
606
 */
607
0
apr_byte_t oidc_session_kill(request_rec *r, oidc_session_t *z) {
608
0
  r->user = NULL;
609
0
  if (z->state) {
610
0
    oidc_json_decref(z->state);
611
0
    z->state = NULL;
612
0
  }
613
0
  oidc_session_save(r, z, OIDC_SESSION_SAVE_UPDATE);
614
0
  return oidc_session_free(r, z);
615
0
}
616
617
/*
618
 * session object keys
619
 */
620
/* key for storing the userinfo JWT in the session context */
621
0
#define OIDC_SESSION_KEY_USERINFO_JWT "uij"
622
/* key for storing the raw id_token in the session context */
623
0
#define OIDC_SESSION_KEY_IDTOKEN "idt"
624
/* key for storing the access_token in the session context */
625
0
#define OIDC_SESSION_KEY_ACCESSTOKEN "at"
626
/* key for storing the access_token type in the session context */
627
0
#define OIDC_SESSION_KEY_ACCESSTOKEN_TYPE "att"
628
/* key for storing the access_token expiry in the session context */
629
0
#define OIDC_SESSION_KEY_ACCESSTOKEN_EXPIRES "ate"
630
/* key for storing the refresh_token in the session context */
631
0
#define OIDC_SESSION_KEY_REFRESH_TOKEN "rt"
632
/* key for storing maximum session duration in the session context */
633
0
#define OIDC_SESSION_KEY_SESSION_EXPIRES "se"
634
/* key for storing the cookie domain in the session context */
635
0
#define OIDC_SESSION_KEY_COOKIE_DOMAIN "cd"
636
/* key for storing last user info refresh timestamp in the session context */
637
0
#define OIDC_SESSION_KEY_USERINFO_LAST_REFRESH "uilr"
638
/* key for storing last access token refresh timestamp in the session context */
639
0
#define OIDC_SESSION_KEY_ACCESS_TOKEN_LAST_REFRESH "atlr"
640
/* key for storing request state */
641
0
#define OIDC_SESSION_KEY_REQUEST_STATE "rs"
642
/* key for storing the original URL */
643
0
#define OIDC_SESSION_KEY_ORIGINAL_URL "ou"
644
/* key for storing the session_state in the session context */
645
0
#define OIDC_SESSION_KEY_SESSION_STATE "ss"
646
/* key for storing the issuer in the session context */
647
0
#define OIDC_SESSION_KEY_ISSUER "iss"
648
/* key for storing the provider specific user info refresh interval */
649
0
#define OIDC_SESSION_KEY_USERINFO_REFRESH_INTERVAL "uir"
650
/* key for storing whether this is a newly created session or not */
651
0
#define OIDC_SESSION_KEY_SESSION_IS_NEW "sn"
652
/* key for storing the scope in the session context */
653
0
#define OIDC_SESSION_KEY_SCOPE "scp"
654
655
0
#define OIDC_SESSION_KEY_PATH_AUTH_REQUEST_PARAMS "parp"
656
657
0
#define OIDC_SESSION_KEY_PATH_SCOPE "psc"
658
659
/*
660
 * helper functions
661
 */
662
0
static const char *oidc_session_get_key2string(request_rec *r, const oidc_session_t *z, const char *key) {
663
0
  char *s_value = NULL;
664
0
  oidc_session_get(r, z, key, &s_value);
665
0
  return s_value;
666
0
}
667
668
0
#define OIDC_SESSION_WARN_CLAIM_SIZE (1024 * 8)
669
0
#define OIDC_SESSION_WARN_CLAIM_SIZE_VAR "OIDC_SESSION_WARN_CLAIM_SIZE"
670
671
/*
672
 * read the warning threshold for the (encoded) size of a single session claim from the environment
673
 */
674
0
static int oidc_session_warn_claim_size_get(request_rec *r) {
675
0
  const char *str = NULL;
676
677
0
  if (r->subprocess_env == NULL)
678
0
    return OIDC_SESSION_WARN_CLAIM_SIZE;
679
680
0
  str = apr_table_get(r->subprocess_env, OIDC_SESSION_WARN_CLAIM_SIZE_VAR);
681
0
  if (str == NULL)
682
0
    return OIDC_SESSION_WARN_CLAIM_SIZE;
683
684
0
  int warn_claim_size = _oidc_str_to_int(str, OIDC_SESSION_WARN_CLAIM_SIZE);
685
0
  oidc_debug(r, "warn_claim_size set to %d in environment variable %s", warn_claim_size,
686
0
       OIDC_SESSION_WARN_CLAIM_SIZE_VAR);
687
0
  return warn_claim_size;
688
0
}
689
690
/*
691
 * check whether a single claim passes the configured black/white list filters
692
 */
693
static apr_byte_t oidc_session_claim_is_allowed(request_rec *r, const oidc_cfg_t *c, const char *session_key,
694
0
            const char *name) {
695
0
  if ((oidc_cfg_black_listed_claims_get(c) != NULL) &&
696
0
      (apr_hash_get(oidc_cfg_black_listed_claims_get(c), name, APR_HASH_KEY_STRING) != NULL)) {
697
0
    oidc_debug(r, "removing blacklisted claim [%s]: '%s'", session_key, name);
698
0
    return FALSE;
699
0
  }
700
701
0
  if ((oidc_cfg_white_listed_claims_get(c) != NULL) &&
702
0
      (apr_hash_get(oidc_cfg_white_listed_claims_get(c), name, APR_HASH_KEY_STRING) == NULL)) {
703
0
    oidc_debug(r, "removing non-whitelisted claim [%s]: '%s'", session_key, name);
704
0
    return FALSE;
705
0
  }
706
707
0
  return TRUE;
708
0
}
709
710
/*
711
 * record a single allowed claim into the destination object, increment metrics and warn on oversized values
712
 */
713
static void oidc_session_filtered_claim_record(request_rec *r, const oidc_cfg_t *c, const char *session_key,
714
                 const char *name, oidc_json_t *value, oidc_json_t *dst,
715
0
                 int warn_claim_size) {
716
0
  const char *str = value ? oidc_json_encode(r->pool, value,
717
0
               OIDC_JSON_PRESERVE_ORDER | OIDC_JSON_COMPACT | OIDC_JSON_ENCODE_ANY)
718
0
        : "";
719
720
0
  if ((int)_oidc_strlen(str) > warn_claim_size)
721
0
    oidc_warn(r,
722
0
        "(encoded) value size of [%s] claim \"%s\" is larger than %d; consider "
723
0
        "blacklisting it in OIDCBlackListedClaims "
724
0
        "or increase the warning limit with environment variable %s",
725
0
        session_key, name, warn_claim_size, OIDC_SESSION_WARN_CLAIM_SIZE_VAR);
726
727
0
  oidc_json_object_set(dst, name, value);
728
729
0
  const char *metric_value = oidc_json_is_string(value) ? oidc_json_string_value(value) : str;
730
0
  if (_oidc_strcmp(session_key, OIDC_SESSION_KEY_USERINFO_CLAIMS) == 0) {
731
0
    OIDC_METRICS_COUNTER_INC_NAME_VALUE(r, c, OM_CLAIM_USER_INFO, name, metric_value);
732
0
  } else {
733
0
    OIDC_METRICS_COUNTER_INC_NAME_VALUE(r, c, OM_CLAIM_ID_TOKEN, name, metric_value);
734
0
  }
735
0
}
736
737
#ifdef USE_LIBJQ
738
/*
739
 * apply the configured JQ filter to the collected claims; replaces *dst with the filtered object on success
740
 */
741
static void oidc_session_jq_filter_apply(request_rec *r, const oidc_cfg_t *c, const char *session_key,
742
           oidc_json_t **dst) {
743
  const oidc_apr_expr_t *filter = oidc_cfg_filter_claims_expr_get(c);
744
  const char *s_filter = oidc_util_apr_expr_exec(r, filter, OIDC_APR_EXPR_RESULT_STRING);
745
746
  if (filter == NULL)
747
    return;
748
749
  const char *filtered_claims = oidc_util_jq_filter(r, *dst, s_filter);
750
  oidc_json_decref(*dst);
751
  *dst = NULL;
752
  if (oidc_json_decode_object(r, filtered_claims, dst) == FALSE)
753
    oidc_error(r, "JQ filtering of claims for [%s] resulted in invalid JSON object, filter='%s'",
754
         session_key, s_filter);
755
}
756
#endif
757
758
/*
759
 * apply whitelisting/blacklisting and a JQ filter  to the provided (serialized JSON) claims
760
 * session_key may refer to id_token claims or userinfo claims
761
 */
762
static void oidc_session_set_filtered_claims(request_rec *r, oidc_session_t *z, const char *session_key,
763
0
               oidc_json_t *claims) {
764
0
  const oidc_cfg_t *c = ap_get_module_config(r->server->module_config, &auth_openidc_module);
765
0
  oidc_json_t *dst = NULL;
766
0
  void *iter = NULL;
767
0
  int warn_claim_size = oidc_session_warn_claim_size_get(r);
768
769
  // avoid gcc 14 warning: '%s' directive argument is null [-Wformat-overflow=]
770
0
  if (session_key == NULL)
771
0
    session_key = "";
772
773
0
  if (claims != NULL) {
774
0
    dst = oidc_json_object();
775
0
    iter = oidc_json_object_iter(claims);
776
0
  }
777
778
  // NB: need this loop for all claims for the metrics
779
0
  while (iter) {
780
0
    const char *name = oidc_json_object_iter_key(iter);
781
0
    oidc_json_t *value = oidc_json_object_iter_value(iter);
782
783
0
    if (oidc_session_claim_is_allowed(r, c, session_key, name) == TRUE)
784
0
      oidc_session_filtered_claim_record(r, c, session_key, name, value, dst, warn_claim_size);
785
786
0
    iter = oidc_json_object_iter_next(claims, iter);
787
0
  }
788
789
#ifdef USE_LIBJQ
790
  oidc_session_jq_filter_apply(r, c, session_key, &dst);
791
#endif
792
793
0
  oidc_session_json_set(r, z, session_key, dst);
794
0
}
795
796
/*
797
 * userinfo claims
798
 */
799
0
void oidc_session_set_userinfo_claims(request_rec *r, oidc_session_t *z, oidc_json_t *claims_json) {
800
0
  oidc_session_set_filtered_claims(r, z, OIDC_SESSION_KEY_USERINFO_CLAIMS, claims_json);
801
0
}
802
803
0
oidc_json_t *oidc_session_get_userinfo_claims(request_rec *r, const oidc_session_t *z) {
804
0
  return oidc_session_json_get(r, z, OIDC_SESSION_KEY_USERINFO_CLAIMS);
805
0
}
806
807
0
void oidc_session_set_userinfo_jwt(request_rec *r, oidc_session_t *z, const char *s_userinfo_jwt) {
808
0
  oidc_session_set(r, z, OIDC_SESSION_KEY_USERINFO_JWT, s_userinfo_jwt);
809
0
}
810
811
0
const char *oidc_session_get_userinfo_jwt(request_rec *r, const oidc_session_t *z) {
812
0
  return oidc_session_get_key2string(r, z, OIDC_SESSION_KEY_USERINFO_JWT);
813
0
}
814
815
/*
816
 * id_token claims
817
 */
818
0
void oidc_session_set_idtoken_claims(request_rec *r, oidc_session_t *z, oidc_json_t *idtoken_claims) {
819
0
  if (apr_table_get(r->subprocess_env, "OIDC_DONT_STORE_ID_TOKEN_CLAIMS_IN_SESSION") == NULL)
820
0
    oidc_session_set_filtered_claims(r, z, OIDC_SESSION_KEY_IDTOKEN_CLAIMS, idtoken_claims);
821
0
}
822
823
0
oidc_json_t *oidc_session_get_idtoken_claims(request_rec *r, const oidc_session_t *z) {
824
0
  return oidc_session_json_get(r, z, OIDC_SESSION_KEY_IDTOKEN_CLAIMS);
825
0
}
826
827
/*
828
 * compact serialized id_token
829
 */
830
0
void oidc_session_set_idtoken(request_rec *r, oidc_session_t *z, const char *s_id_token) {
831
0
  oidc_debug(r, "storing id_token in the session");
832
0
  oidc_session_set(r, z, OIDC_SESSION_KEY_IDTOKEN, s_id_token);
833
0
}
834
835
0
const char *oidc_session_get_idtoken(request_rec *r, const oidc_session_t *z) {
836
0
  return oidc_session_get_key2string(r, z, OIDC_SESSION_KEY_IDTOKEN);
837
0
}
838
839
/*
840
 * access token
841
 */
842
0
void oidc_session_set_access_token(request_rec *r, oidc_session_t *z, const char *access_token) {
843
0
  oidc_session_set(r, z, OIDC_SESSION_KEY_ACCESSTOKEN, access_token);
844
0
}
845
846
0
const char *oidc_session_get_access_token(request_rec *r, const oidc_session_t *z) {
847
0
  return oidc_session_get_key2string(r, z, OIDC_SESSION_KEY_ACCESSTOKEN);
848
0
}
849
850
/*
851
 * access token type
852
 */
853
0
void oidc_session_set_access_token_type(request_rec *r, oidc_session_t *z, const char *token_type) {
854
0
  oidc_session_set(r, z, OIDC_SESSION_KEY_ACCESSTOKEN_TYPE, token_type);
855
0
}
856
857
0
const char *oidc_session_get_access_token_type(request_rec *r, const oidc_session_t *z) {
858
0
  return oidc_session_get_key2string(r, z, OIDC_SESSION_KEY_ACCESSTOKEN_TYPE);
859
0
}
860
861
/*
862
 * access token expires
863
 */
864
0
void oidc_session_set_access_token_expires(request_rec *r, oidc_session_t *z, const int expires_in) {
865
0
  if (expires_in > -1) {
866
0
    oidc_debug(r, "storing access token expires_in in the session: %d", expires_in);
867
0
    oidc_session_set_timestamp(r, z, OIDC_SESSION_KEY_ACCESSTOKEN_EXPIRES,
868
0
             apr_time_now() + apr_time_from_sec(expires_in));
869
0
  }
870
0
}
871
872
0
apr_time_t oidc_session_get_access_token_expires(request_rec *r, const oidc_session_t *z) {
873
0
  return oidc_session_get_key2timestamp(r, z, OIDC_SESSION_KEY_ACCESSTOKEN_EXPIRES);
874
0
}
875
876
0
const char *oidc_session_get_access_token_expires2str(request_rec *r, const oidc_session_t *z) {
877
0
  apr_time_t expires = oidc_session_get_access_token_expires(r, z);
878
0
  return (expires > -1) ? apr_psprintf(r->pool, "%" APR_TIME_T_FMT, apr_time_sec(expires)) : NULL;
879
0
}
880
881
/*
882
 * refresh token
883
 */
884
0
void oidc_session_set_refresh_token(request_rec *r, oidc_session_t *z, const char *refresh_token) {
885
0
  oidc_session_set(r, z, OIDC_SESSION_KEY_REFRESH_TOKEN, refresh_token);
886
0
}
887
888
0
const char *oidc_session_get_refresh_token(request_rec *r, const oidc_session_t *z) {
889
0
  return oidc_session_get_key2string(r, z, OIDC_SESSION_KEY_REFRESH_TOKEN);
890
0
}
891
892
/*
893
 * session expires
894
 */
895
0
void oidc_session_set_session_expires(request_rec *r, oidc_session_t *z, const apr_time_t expires) {
896
0
  oidc_session_set_timestamp(r, z, OIDC_SESSION_KEY_SESSION_EXPIRES, expires);
897
0
}
898
899
0
apr_time_t oidc_session_get_session_expires(request_rec *r, const oidc_session_t *z) {
900
0
  return oidc_session_get_key2timestamp(r, z, OIDC_SESSION_KEY_SESSION_EXPIRES);
901
0
}
902
903
/*
904
 * cookie domain
905
 */
906
0
void oidc_session_set_cookie_domain(request_rec *r, oidc_session_t *z, const char *cookie_domain) {
907
0
  oidc_session_set(r, z, OIDC_SESSION_KEY_COOKIE_DOMAIN, cookie_domain);
908
0
}
909
910
0
const char *oidc_session_get_cookie_domain(request_rec *r, const oidc_session_t *z) {
911
0
  return oidc_session_get_key2string(r, z, OIDC_SESSION_KEY_COOKIE_DOMAIN);
912
0
}
913
914
/*
915
 * userinfo last refresh
916
 */
917
918
0
void oidc_session_set_userinfo_refresh_interval(request_rec *r, oidc_session_t *z, const int interval) {
919
0
  oidc_session_set_int(r, z, OIDC_SESSION_KEY_USERINFO_REFRESH_INTERVAL, interval);
920
0
}
921
922
0
int oidc_session_get_userinfo_refresh_interval(request_rec *r, const oidc_session_t *z) {
923
0
  return oidc_session_get_int(r, z, OIDC_SESSION_KEY_USERINFO_REFRESH_INTERVAL, -1);
924
0
}
925
926
0
void oidc_session_reset_userinfo_last_refresh(request_rec *r, oidc_session_t *z) {
927
0
  oidc_session_set_timestamp(r, z, OIDC_SESSION_KEY_USERINFO_LAST_REFRESH, apr_time_now());
928
0
}
929
930
0
apr_time_t oidc_session_get_userinfo_last_refresh(request_rec *r, const oidc_session_t *z) {
931
0
  return oidc_session_get_key2timestamp(r, z, OIDC_SESSION_KEY_USERINFO_LAST_REFRESH);
932
0
}
933
934
/*
935
 * access_token last refresh
936
 */
937
0
void oidc_session_set_access_token_last_refresh(request_rec *r, oidc_session_t *z, apr_time_t ts) {
938
0
  oidc_session_set_timestamp(r, z, OIDC_SESSION_KEY_ACCESS_TOKEN_LAST_REFRESH, ts);
939
0
}
940
941
0
apr_time_t oidc_session_get_access_token_last_refresh(request_rec *r, const oidc_session_t *z) {
942
0
  return oidc_session_get_key2timestamp(r, z, OIDC_SESSION_KEY_ACCESS_TOKEN_LAST_REFRESH);
943
0
}
944
945
/*
946
 * request state
947
 */
948
0
void oidc_session_set_request_state(request_rec *r, oidc_session_t *z, const char *request_state) {
949
0
  oidc_session_set(r, z, OIDC_SESSION_KEY_REQUEST_STATE, request_state);
950
0
}
951
952
0
const char *oidc_session_get_request_state(request_rec *r, const oidc_session_t *z) {
953
0
  return oidc_session_get_key2string(r, z, OIDC_SESSION_KEY_REQUEST_STATE);
954
0
}
955
956
/*
957
 * original url
958
 */
959
0
void oidc_session_set_original_url(request_rec *r, oidc_session_t *z, const char *original_url) {
960
0
  oidc_session_set(r, z, OIDC_SESSION_KEY_ORIGINAL_URL, original_url);
961
0
}
962
963
0
const char *oidc_session_get_original_url(request_rec *r, const oidc_session_t *z) {
964
0
  return oidc_session_get_key2string(r, z, OIDC_SESSION_KEY_ORIGINAL_URL);
965
0
}
966
967
/*
968
 * session state
969
 */
970
0
void oidc_session_set_session_state(request_rec *r, oidc_session_t *z, const char *session_state) {
971
0
  oidc_session_set(r, z, OIDC_SESSION_KEY_SESSION_STATE, session_state);
972
0
}
973
974
0
const char *oidc_session_get_session_state(request_rec *r, const oidc_session_t *z) {
975
0
  return oidc_session_get_key2string(r, z, OIDC_SESSION_KEY_SESSION_STATE);
976
0
}
977
978
/*
979
 * issuer
980
 */
981
0
void oidc_session_set_issuer(request_rec *r, oidc_session_t *z, const char *issuer) {
982
0
  oidc_session_set(r, z, OIDC_SESSION_KEY_ISSUER, issuer);
983
0
}
984
985
0
const char *oidc_session_get_issuer(request_rec *r, const oidc_session_t *z) {
986
0
  return oidc_session_get_key2string(r, z, OIDC_SESSION_KEY_ISSUER);
987
0
}
988
989
/*
990
 * new session
991
 */
992
0
void oidc_session_set_session_new(request_rec *r, oidc_session_t *z, const int is_new) {
993
0
  if (z->state == NULL)
994
0
    z->state = oidc_json_object();
995
0
  if (is_new)
996
0
    oidc_json_object_set_new(z->state, OIDC_SESSION_KEY_SESSION_IS_NEW, oidc_json_integer(1));
997
0
  else
998
0
    oidc_json_object_del(z->state, OIDC_SESSION_KEY_SESSION_IS_NEW);
999
0
}
1000
1001
0
int oidc_session_get_session_new(request_rec *r, const oidc_session_t *z) {
1002
0
  return oidc_session_get_int(r, z, OIDC_SESSION_KEY_SESSION_IS_NEW, 0);
1003
0
}
1004
1005
/*
1006
 * scope
1007
 */
1008
0
void oidc_session_set_scope(request_rec *r, oidc_session_t *z, const char *scope) {
1009
0
  oidc_session_set(r, z, OIDC_SESSION_KEY_SCOPE, scope);
1010
0
}
1011
1012
0
const char *oidc_session_get_scope(request_rec *r, const oidc_session_t *z) {
1013
0
  return oidc_session_get_key2string(r, z, OIDC_SESSION_KEY_SCOPE);
1014
0
}
1015
1016
0
void oidc_session_set_path_auth_request_params(request_rec *r, oidc_session_t *z, const char *auth_request_params) {
1017
0
  oidc_session_set(r, z, OIDC_SESSION_KEY_PATH_AUTH_REQUEST_PARAMS, auth_request_params);
1018
0
}
1019
1020
0
const char *oidc_session_get_path_auth_request_params(request_rec *r, const oidc_session_t *z) {
1021
0
  return oidc_session_get_key2string(r, z, OIDC_SESSION_KEY_PATH_AUTH_REQUEST_PARAMS);
1022
0
}
1023
1024
0
void oidc_session_set_path_scope(request_rec *r, oidc_session_t *z, const char *path_scope) {
1025
0
  oidc_session_set(r, z, OIDC_SESSION_KEY_PATH_SCOPE, path_scope);
1026
0
}
1027
1028
0
const char *oidc_session_get_path_scope(request_rec *r, const oidc_session_t *z) {
1029
0
  return oidc_session_get_key2string(r, z, OIDC_SESSION_KEY_PATH_SCOPE);
1030
0
}