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