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-2021 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@zmartzone.eu
42 */
43
44 #include <httpd.h>
45 #include <http_config.h>
46 #include <http_log.h>
47 #include <http_request.h>
48
49 #include "mod_auth_openidc.h"
50 #include "parse.h"
51
52 #include <openssl/opensslconf.h>
53 #include <openssl/opensslv.h>
54
55 #ifdef USE_URANDOM
56
57 #include <sys/stat.h>
58 #include <fcntl.h>
59 #include <unistd.h>
60
61 #define DEV_RANDOM "/dev/urandom"
62
63 #endif
64
65 extern module AP_MODULE_DECLARE_DATA auth_openidc_module;
66
oidc_proto_generate_random_bytes(request_rec * r,unsigned char * buf,apr_size_t length)67 apr_status_t oidc_proto_generate_random_bytes(request_rec *r,
68 unsigned char *buf, apr_size_t length) {
69 apr_status_t rv;
70
71 #ifndef USE_URANDOM
72
73 oidc_debug(r,
74 "apr_generate_random_bytes call for %" APR_SIZE_T_FMT " bytes",
75 length);
76 rv = apr_generate_random_bytes(buf, length);
77 oidc_debug(r, "apr_generate_random_bytes returned");
78
79 #else
80
81 int fd = -1;
82
83 do {
84 apr_ssize_t rc;
85
86 if (fd == -1) {
87 fd = open(DEV_RANDOM, O_RDONLY);
88 if (fd == -1)
89 return errno;
90 }
91
92 do {
93 oidc_debug(r, "read %s for %" APR_SIZE_T_FMT " bytes", DEV_RANDOM,
94 length);
95 rc = read(fd, buf, length);
96 oidc_debug(r, "read %s returned: %"APR_SIZE_T_FMT, DEV_RANDOM " bytes", rc);
97 } while (rc == -1 && errno == EINTR);
98
99 if (rc < 0) {
100 int errnum = errno;
101 close(fd);
102 return errnum;
103 } else if (rc == 0) {
104 close(fd);
105 fd = -1; /* force open() again */
106 } else {
107 buf += rc;
108 length -= rc;
109 }
110 } while (length > 0);
111
112 close(fd);
113
114 rv = APR_SUCCESS;
115
116 #endif
117
118 return rv;
119 }
120
121 /*
122 * generate a random string value value of a specified length
123 */
oidc_proto_generate_random_string(request_rec * r,char ** output,int len)124 static apr_byte_t oidc_proto_generate_random_string(request_rec *r,
125 char **output, int len) {
126 unsigned char *bytes = apr_pcalloc(r->pool, len);
127 if (oidc_proto_generate_random_bytes(r, bytes, len) != APR_SUCCESS) {
128 oidc_error(r, "oidc_proto_generate_random_bytes returned an error");
129 return FALSE;
130 }
131 if (oidc_base64url_encode(r, output, (const char*) bytes, len, TRUE) <= 0) {
132 oidc_error(r, "oidc_base64url_encode returned an error");
133 return FALSE;
134 }
135 return TRUE;
136 }
137
138 #define OIDC_REQUEST_OJBECT_COPY_FROM_REQUEST "copy_from_request"
139 #define OIDC_REQUEST_OJBECT_COPY_AND_REMOVE_FROM_REQUEST "copy_and_remove_from_request"
140
141 /*
142 * indicates wether a request parameter from the authorization request needs to be
143 * copied and/or deleted to/from the protected request object based on the settings specified
144 * in the "copy_from_request"/"copy_and_remove_from_request" JSON array in the request object
145 */
oidc_proto_param_needs_action(json_t * request_object_config,const char * parameter_name,const char * action)146 static apr_byte_t oidc_proto_param_needs_action(json_t *request_object_config,
147 const char *parameter_name, const char *action) {
148 json_t *copy_from_request = json_object_get(request_object_config, action);
149 size_t index = 0;
150 while (index < json_array_size(copy_from_request)) {
151 json_t *value = json_array_get(copy_from_request, index);
152 if ((json_is_string(value))
153 && (apr_strnatcmp(json_string_value(value), parameter_name) == 0)) {
154 return TRUE;
155 }
156 index++;
157 }
158 return FALSE;
159 }
160
161 /* context structure for copying request parameters */
162 typedef struct oidc_proto_copy_req_ctx_t {
163 request_rec *r;
164 json_t *request_object_config;
165 oidc_jwt_t *request_object;
166 apr_table_t *params2;
167 } oidc_proto_copy_req_ctx_t;
168
169 /*
170 * copy a parameter key/value from the authorizion request to the
171 * request object if the configuration setting says to include it
172 */
oidc_proto_copy_from_request(void * rec,const char * name,const char * value)173 static int oidc_proto_copy_from_request(void *rec, const char *name,
174 const char *value) {
175 oidc_proto_copy_req_ctx_t *ctx = (oidc_proto_copy_req_ctx_t*) rec;
176
177 oidc_debug(ctx->r, "processing name: %s, value: %s", name, value);
178
179 if (oidc_proto_param_needs_action(ctx->request_object_config, name,
180 OIDC_REQUEST_OJBECT_COPY_FROM_REQUEST)
181 || oidc_proto_param_needs_action(ctx->request_object_config, name,
182 OIDC_REQUEST_OJBECT_COPY_AND_REMOVE_FROM_REQUEST)) {
183 json_t *result = NULL;
184 json_error_t json_error;
185 result = json_loads(value, JSON_DECODE_ANY, &json_error);
186 if (result == NULL)
187 /* assume string */
188 result = json_string(value);
189 if (result) {
190 json_object_set_new(ctx->request_object->payload.value.json, name,
191 json_deep_copy(result));
192 json_decref(result);
193 }
194
195 if (oidc_proto_param_needs_action(ctx->request_object_config, name,
196 OIDC_REQUEST_OJBECT_COPY_AND_REMOVE_FROM_REQUEST)) {
197 apr_table_set(ctx->params2, name, name);
198 }
199
200 }
201
202 return 1;
203 }
204
205 /*
206 * delete a parameter key/value from the authorizion request if the configuration setting says to remove it
207 */
oidc_proto_delete_from_request(void * rec,const char * name,const char * value)208 static int oidc_proto_delete_from_request(void *rec, const char *name,
209 const char *value) {
210 oidc_proto_copy_req_ctx_t *ctx = (oidc_proto_copy_req_ctx_t*) rec;
211
212 oidc_debug(ctx->r, "deleting from query parameters: name: %s, value: %s",
213 name, value);
214
215 if (oidc_proto_param_needs_action(ctx->request_object_config, name,
216 OIDC_REQUEST_OJBECT_COPY_AND_REMOVE_FROM_REQUEST)) {
217 apr_table_unset(ctx->params2, name);
218 }
219
220 return 1;
221 }
222
223 /*
224 * obtain the public key for a provider to encrypt the request object with
225 */
oidc_proto_get_encryption_jwk_by_type(request_rec * r,oidc_cfg * cfg,struct oidc_provider_t * provider,int key_type,oidc_jwk_t ** jwk)226 apr_byte_t oidc_proto_get_encryption_jwk_by_type(request_rec *r, oidc_cfg *cfg,
227 struct oidc_provider_t *provider, int key_type, oidc_jwk_t **jwk) {
228
229 oidc_jwks_uri_t jwks_uri = { provider->jwks_uri,
230 provider->jwks_refresh_interval, provider->ssl_validate_server };
231
232 oidc_jose_error_t err;
233 json_t *j_jwks = NULL;
234 apr_byte_t force_refresh = TRUE;
235 oidc_jwk_t *key = NULL;
236 char *jwk_json = NULL;
237
238 /* TODO: forcefully refresh now; we may want to relax that */
239 oidc_metadata_jwks_get(r, cfg, &jwks_uri, &j_jwks, &force_refresh);
240
241 if (j_jwks == NULL) {
242 oidc_error(r, "could not retrieve JSON Web Keys");
243 return FALSE;
244 }
245
246 json_t *keys = json_object_get(j_jwks, OIDC_JWK_KEYS);
247 if ((keys == NULL) || !(json_is_array(keys))) {
248 oidc_error(r, "\"%s\" array element is not a JSON array",
249 OIDC_JWK_KEYS);
250 return FALSE;
251 }
252
253 int i;
254 /* walk the set of published keys to find the first that has a matching type */
255 for (i = 0; i < json_array_size(keys); i++) {
256
257 json_t *elem = json_array_get(keys, i);
258
259 const char *use = json_string_value(
260 json_object_get(elem, OIDC_JWK_USE));
261 if ((use != NULL) && (strcmp(use, OIDC_JWK_ENC) != 0)) {
262 oidc_debug(r, "skipping key because of non-matching \"%s\": \"%s\"",
263 OIDC_JWK_USE, use);
264 continue;
265 }
266
267 if (oidc_jwk_parse_json(r->pool, elem, &key, &err) == FALSE) {
268 oidc_warn(r, "oidc_jwk_parse_json failed: %s",
269 oidc_jose_e2s(r->pool, err));
270 continue;
271 }
272
273 if (key_type == key->kty) {
274 oidc_jwk_to_json(r->pool, key, &jwk_json, &err);
275 oidc_debug(r, "found matching encryption key type for key: %s",
276 jwk_json);
277 *jwk = key;
278 break;
279 }
280
281 oidc_jwk_destroy(key);
282 }
283
284 /* no need anymore for the parsed json_t contents, release the it */
285 json_decref(j_jwks);
286
287 return (*jwk != NULL);
288 }
289
oidc_key_list_first(const apr_array_header_t * key_list)290 static oidc_jwk_t* oidc_key_list_first(const apr_array_header_t *key_list) {
291 oidc_jwk_t *rv = NULL;
292 if ((key_list) && (key_list->nelts > 0))
293 rv = ((oidc_jwk_t**) key_list->elts)[0];
294 return rv;
295 }
296
297 /*
298 * generate a request object
299 */
oidc_proto_create_request_object(request_rec * r,struct oidc_provider_t * provider,json_t * request_object_config,apr_table_t * params)300 char* oidc_proto_create_request_object(request_rec *r,
301 struct oidc_provider_t *provider, json_t *request_object_config,
302 apr_table_t *params) {
303
304 oidc_jwk_t *sjwk = NULL;
305 int jwk_needs_destroy = 0;
306
307 oidc_debug(r, "enter");
308
309 oidc_cfg *cfg = ap_get_module_config(r->server->module_config,
310 &auth_openidc_module);
311
312 /* create the request object value */
313 oidc_jwt_t *request_object = oidc_jwt_new(r->pool, TRUE, TRUE);
314
315 /* set basic values: iss and aud */
316 json_object_set_new(request_object->payload.value.json, OIDC_CLAIM_ISS,
317 json_string(provider->client_id));
318 json_object_set_new(request_object->payload.value.json, OIDC_CLAIM_AUD,
319 json_string(provider->issuer));
320
321 /* add static values to the request object as configured in the .conf file; may override iss/aud */
322 oidc_util_json_merge(r, json_object_get(request_object_config, "static"),
323 request_object->payload.value.json);
324
325 /* copy parameters from the authorization request as configured in the .conf file */
326 apr_table_t *delete_from_query_params = apr_table_make(r->pool, 0);
327 oidc_proto_copy_req_ctx_t data = { r, request_object_config, request_object,
328 delete_from_query_params };
329 apr_table_do(oidc_proto_copy_from_request, &data, params, NULL);
330
331 /* delete parameters from the query parameters of the authorization request as configured in the .conf file */
332 data.params2 = params;
333 apr_table_do(oidc_proto_delete_from_request, &data,
334 delete_from_query_params, NULL);
335
336 /* debug logging */
337 oidc_debug(r, "request object: %s",
338 oidc_util_encode_json_object(r, request_object->payload.value.json, JSON_COMPACT));
339
340 char *serialized_request_object = NULL;
341 oidc_jose_error_t err;
342
343 /* get the crypto settings from the configuration */
344 json_t *crypto = json_object_get(request_object_config, "crypto");
345 oidc_json_object_get_string(r->pool, crypto, "sign_alg",
346 &request_object->header.alg, "none");
347
348 /* see if we need to sign the request object */
349 if (strcmp(request_object->header.alg, "none") != 0) {
350
351 sjwk = NULL;
352 jwk_needs_destroy = 0;
353
354 switch (oidc_jwt_alg2kty(request_object)) {
355 case CJOSE_JWK_KTY_RSA:
356 if ((provider->client_signing_keys != NULL)
357 || (cfg->private_keys != NULL)) {
358 sjwk = provider->client_signing_keys ?
359 oidc_key_list_first(provider->client_signing_keys) :
360 oidc_key_list_first(cfg->private_keys);
361 request_object->header.kid = apr_pstrdup(r->pool, sjwk->kid);
362 } else {
363 oidc_error(r,
364 "no global or per-provider private keys have been configured to use for request object signing");
365 }
366 break;
367 case CJOSE_JWK_KTY_OCT:
368 oidc_util_create_symmetric_key(r, provider->client_secret, 0, NULL,
369 FALSE, &sjwk);
370 jwk_needs_destroy = 1;
371 break;
372 default:
373 oidc_error(r,
374 "unsupported signing algorithm, no key type for algorithm: %s",
375 request_object->header.alg);
376 break;
377 }
378
379 if (sjwk == NULL) {
380 oidc_jwt_destroy(request_object);
381 json_decref(request_object_config);
382 return FALSE;
383 }
384
385 if (oidc_jwt_sign(r->pool, request_object, sjwk, &err) == FALSE) {
386 oidc_error(r, "signing Request Object failed: %s",
387 oidc_jose_e2s(r->pool, err));
388 if (jwk_needs_destroy)
389 oidc_jwk_destroy(sjwk);
390 oidc_jwt_destroy(request_object);
391 json_decref(request_object_config);
392 return FALSE;
393 }
394
395 if (jwk_needs_destroy)
396 oidc_jwk_destroy(sjwk);
397 }
398
399 oidc_jwt_t *jwe = oidc_jwt_new(r->pool, TRUE, FALSE);
400 if (jwe == NULL) {
401 oidc_error(r, "creating JWE failed");
402 oidc_jwt_destroy(request_object);
403 json_decref(request_object_config);
404 return FALSE;
405 }
406
407 oidc_json_object_get_string(r->pool, crypto, "crypt_alg", &jwe->header.alg,
408 NULL);
409 oidc_json_object_get_string(r->pool, crypto, "crypt_enc", &jwe->header.enc,
410 NULL);
411
412 char *cser = oidc_jwt_serialize(r->pool, request_object, &err);
413
414 /* see if we need to encrypt the request object */
415 if (jwe->header.alg != NULL) {
416
417 oidc_jwk_t *ejwk = NULL;
418
419 switch (oidc_jwt_alg2kty(jwe)) {
420 case CJOSE_JWK_KTY_RSA:
421 oidc_proto_get_encryption_jwk_by_type(r, cfg, provider,
422 CJOSE_JWK_KTY_RSA, &ejwk);
423 break;
424 case CJOSE_JWK_KTY_OCT:
425 oidc_util_create_symmetric_key(r, provider->client_secret,
426 oidc_alg2keysize(jwe->header.alg), OIDC_JOSE_ALG_SHA256,
427 FALSE, &ejwk);
428 break;
429 default:
430 oidc_error(r,
431 "unsupported encryption algorithm, no key type for algorithm: %s",
432 request_object->header.alg);
433 break;
434 }
435
436 if (ejwk == NULL) {
437 oidc_jwt_destroy(jwe);
438 oidc_jwt_destroy(request_object);
439 json_decref(request_object_config);
440 return FALSE;
441 }
442
443 if (jwe->header.enc == NULL)
444 jwe->header.enc = apr_pstrdup(r->pool, CJOSE_HDR_ENC_A128CBC_HS256);
445
446 if (ejwk->kid != NULL)
447 jwe->header.kid = ejwk->kid;
448
449 if (oidc_jwt_encrypt(r->pool, jwe, ejwk, cser,
450 &serialized_request_object, &err) == FALSE) {
451 oidc_error(r, "encrypting JWT failed: %s",
452 oidc_jose_e2s(r->pool, err));
453 oidc_jwk_destroy(ejwk);
454 oidc_jwt_destroy(jwe);
455 oidc_jwt_destroy(request_object);
456 json_decref(request_object_config);
457 return FALSE;
458 }
459
460 oidc_jwk_destroy(ejwk);
461
462 } else {
463
464 /* should be sign only or "none" */
465 serialized_request_object = cser;
466 }
467
468 oidc_jwt_destroy(request_object);
469 oidc_jwt_destroy(jwe);
470 json_decref(request_object_config);
471
472 oidc_debug(r, "serialized request object JWT header = \"%s\"",
473 oidc_proto_peek_jwt_header(r, serialized_request_object, NULL));
474 oidc_debug(r, "serialized request object = \"%s\"",
475 serialized_request_object);
476
477 return serialized_request_object;
478 }
479
480 /*
481 * generate a request object and pass it by reference in the authorization request
482 */
oidc_proto_create_request_uri(request_rec * r,struct oidc_provider_t * provider,json_t * request_object_config,const char * redirect_uri,apr_table_t * params)483 static char* oidc_proto_create_request_uri(request_rec *r,
484 struct oidc_provider_t *provider, json_t *request_object_config,
485 const char *redirect_uri, apr_table_t *params) {
486
487 oidc_debug(r, "enter");
488
489 /* see if we need to override the resolver URL, mostly for test purposes */
490 char *resolver_url = NULL;
491 if (json_object_get(request_object_config, "url") != NULL)
492 resolver_url = apr_pstrdup(r->pool,
493 json_string_value(
494 json_object_get(request_object_config, "url")));
495 else
496 resolver_url = apr_pstrdup(r->pool, redirect_uri);
497
498 char *serialized_request_object = oidc_proto_create_request_object(r,
499 provider, request_object_config, params);
500
501 /* generate a temporary reference, store the request object in the cache and generate a Request URI that references it */
502 char *request_uri = NULL;
503 if (serialized_request_object != NULL) {
504 char *request_ref = NULL;
505 if (oidc_proto_generate_random_string(r, &request_ref, 16) == TRUE) {
506 oidc_cache_set_request_uri(r, request_ref,
507 serialized_request_object,
508 apr_time_now() + apr_time_from_sec(OIDC_REQUEST_URI_CACHE_DURATION));
509 request_uri = apr_psprintf(r->pool, "%s?%s=%s", resolver_url,
510 OIDC_PROTO_REQUEST_URI, oidc_util_escape_string(r, request_ref));
511 }
512 }
513
514 return request_uri;
515 }
516
517 /*
518 * Generic function to generate request/request_object parameter with value
519 */
oidc_proto_add_request_param(request_rec * r,struct oidc_provider_t * provider,const char * redirect_uri,apr_table_t * params)520 static void oidc_proto_add_request_param(request_rec *r,
521 struct oidc_provider_t *provider, const char *redirect_uri,
522 apr_table_t *params) {
523
524 /* parse the request object configuration from a string in to a JSON structure */
525 json_t *request_object_config = NULL;
526 if (oidc_util_decode_json_object(r, provider->request_object,
527 &request_object_config) == FALSE)
528 return;
529
530 /* request_uri is used as default parameter for sending Request Object */
531 char *parameter = OIDC_PROTO_REQUEST_URI;
532
533 /* get request_object_type parameter from config */
534 json_t *request_object_type = json_object_get(request_object_config,
535 "request_object_type");
536 if (request_object_type != NULL) {
537 const char *request_object_type_str = json_string_value(
538 request_object_type);
539 if (request_object_type_str == NULL) {
540 oidc_error(r,
541 "Value of request_object_type in request_object config is not a string");
542 return;
543 }
544
545 /* ensure parameter variable to have a valid value */
546 if (strcmp(request_object_type_str, OIDC_PROTO_REQUEST_OBJECT) == 0) {
547 parameter = OIDC_PROTO_REQUEST_OBJECT;
548 } else if (strcmp(request_object_type_str, OIDC_PROTO_REQUEST_URI)
549 != 0) {
550 oidc_error(r, "Bad request_object_type in config: %s",
551 request_object_type_str);
552 return;
553 }
554 }
555
556 /* create request value */
557 char *value = NULL;
558 if (strcmp(parameter, OIDC_PROTO_REQUEST_URI) == 0) {
559 /* parameter is "request_uri" */
560 value = oidc_proto_create_request_uri(r, provider,
561 request_object_config, redirect_uri, params);
562 apr_table_set(params, OIDC_PROTO_REQUEST_URI, value);
563 } else {
564 /* parameter is "request" */
565 value = oidc_proto_create_request_object(r, provider,
566 request_object_config, params);
567 apr_table_set(params, OIDC_PROTO_REQUEST_OBJECT, value);
568 }
569 }
570
571 /* context structure for encoding parameters */
572 typedef struct oidc_proto_form_post_ctx_t {
573 request_rec *r;
574 const char *html_body;
575 } oidc_proto_form_post_ctx_t;
576
577 /*
578 * add a key/value pair post parameter
579 */
oidc_proto_add_form_post_param(void * rec,const char * key,const char * value)580 static int oidc_proto_add_form_post_param(void *rec, const char *key,
581 const char *value) {
582 oidc_proto_form_post_ctx_t *ctx = (oidc_proto_form_post_ctx_t*) rec;
583 oidc_debug(ctx->r, "processing: %s=%s", key, value);
584 ctx->html_body = apr_psprintf(ctx->r->pool,
585 "%s <input type=\"hidden\" name=\"%s\" value=\"%s\">\n",
586 ctx->html_body, oidc_util_html_escape(ctx->r->pool, key),
587 oidc_util_html_escape(ctx->r->pool, value));
588 return 1;
589 }
590
591 /*
592 * make the browser POST parameters through Javascript auto-submit
593 */
oidc_proto_html_post(request_rec * r,const char * url,apr_table_t * params)594 static int oidc_proto_html_post(request_rec *r, const char *url,
595 apr_table_t *params) {
596
597 oidc_debug(r, "enter");
598
599 const char *html_body = apr_psprintf(r->pool,
600 " <p>Submitting Authentication Request...</p>\n"
601 " <form method=\"post\" action=\"%s\">\n"
602 " <p>\n", url);
603
604 oidc_proto_form_post_ctx_t data = { r, html_body };
605 apr_table_do(oidc_proto_add_form_post_param, &data, params, NULL);
606
607 html_body = apr_psprintf(r->pool, "%s%s", data.html_body, " </p>\n"
608 " </form>\n");
609
610 return oidc_util_html_send(r, "Submitting...", NULL,
611 "document.forms[0].submit", html_body, OK);
612 }
613
add_auth_request_params(request_rec * r,apr_table_t * params,const char * auth_request_params)614 void add_auth_request_params(request_rec *r, apr_table_t *params,
615 const char *auth_request_params) {
616 char *key = NULL;
617 char *val = NULL;
618
619 if (auth_request_params == NULL)
620 return;
621
622 while (*auth_request_params
623 && (val = ap_getword(r->pool, &auth_request_params, OIDC_CHAR_AMP))) {
624 key = ap_getword(r->pool, (const char**) &val, OIDC_CHAR_EQUAL);
625 ap_unescape_url(key);
626 ap_unescape_url(val);
627 if (apr_strnatcmp(val, OIDC_STR_HASH) != 0) {
628 apr_table_add(params, key, val);
629 continue;
630 }
631 if (oidc_util_request_has_parameter(r, key) == TRUE) {
632 oidc_util_get_request_parameter(r, key, &val);
633 apr_table_add(params, key, val);
634 }
635 }
636 }
637
638 /*
639 * send an OpenID Connect authorization request to the specified provider
640 */
oidc_proto_authorization_request(request_rec * r,struct oidc_provider_t * provider,const char * login_hint,const char * redirect_uri,const char * state,oidc_proto_state_t * proto_state,const char * id_token_hint,const char * code_challenge,const char * auth_request_params,const char * path_scope)641 int oidc_proto_authorization_request(request_rec *r,
642 struct oidc_provider_t *provider, const char *login_hint,
643 const char *redirect_uri, const char *state,
644 oidc_proto_state_t *proto_state, const char *id_token_hint,
645 const char *code_challenge, const char *auth_request_params,
646 const char *path_scope) {
647
648 /* log some stuff */
649 oidc_debug(r,
650 "enter, issuer=%s, redirect_uri=%s, state=%s, proto_state=%s, code_challenge=%s, auth_request_params=%s, path_scope=%s",
651 provider->issuer, redirect_uri, state,
652 oidc_proto_state_to_string(r, proto_state), code_challenge,
653 auth_request_params, path_scope);
654
655 int rv = OK;
656 char *authorization_request = NULL;
657
658 /* assemble parameters to call the token endpoint for validation */
659 apr_table_t *params = apr_table_make(r->pool, 4);
660
661 /* add the response type */
662 apr_table_setn(params, OIDC_PROTO_RESPONSE_TYPE,
663 oidc_proto_state_get_response_type(proto_state));
664
665 /* concat the per-path scopes with the per-provider scopes */
666 const char *scope = provider->scope;
667 if (path_scope != NULL)
668 scope = ((scope != NULL) && (apr_strnatcmp(scope, "") != 0)) ?
669 apr_pstrcat(r->pool, scope, OIDC_STR_SPACE, path_scope,
670 NULL) :
671 path_scope;
672
673 if (scope != NULL) {
674 if (!oidc_util_spaced_string_contains(r->pool, scope,
675 OIDC_PROTO_SCOPE_OPENID)) {
676 oidc_warn(r,
677 "the configuration for the \"%s\" parameter does not include the \"%s\" scope, your provider may not return an \"id_token\": %s",
678 OIDC_PROTO_SCOPE, OIDC_PROTO_SCOPE_OPENID, provider->scope);
679 }
680 apr_table_setn(params, OIDC_PROTO_SCOPE, scope);
681 }
682
683 if (provider->client_id == NULL) {
684 oidc_error(r,
685 "no Client ID set for the provider: perhaps you are accessing an endpoint protected with \"AuthType openid-connect\" instead of \"AuthType oauth20\"?)");
686 return HTTP_INTERNAL_SERVER_ERROR;
687 }
688
689 /* add the client ID */
690 apr_table_setn(params, OIDC_PROTO_CLIENT_ID, provider->client_id);
691
692 /* add the state */
693 apr_table_setn(params, OIDC_PROTO_STATE, state);
694
695 /* add the redirect uri */
696 apr_table_setn(params, OIDC_PROTO_REDIRECT_URI, redirect_uri);
697
698 /* add the nonce if set */
699 const char *nonce = oidc_proto_state_get_nonce(proto_state);
700 if (nonce != NULL)
701 apr_table_setn(params, OIDC_PROTO_NONCE, nonce);
702
703 /* add PKCE code challenge if set */
704 if (code_challenge != NULL) {
705 apr_table_setn(params, OIDC_PROTO_CODE_CHALLENGE, code_challenge);
706 apr_table_setn(params, OIDC_PROTO_CODE_CHALLENGE_METHOD,
707 provider->pkce->method);
708 }
709
710 /* add the response_mode if explicitly set */
711 const char *response_mode = oidc_proto_state_get_response_mode(proto_state);
712 if (response_mode != NULL)
713 apr_table_setn(params, OIDC_PROTO_RESPONSE_MODE, response_mode);
714
715 /* add the login_hint if provided */
716 if (login_hint != NULL)
717 apr_table_setn(params, OIDC_PROTO_LOGIN_HINT, login_hint);
718
719 /* add the id_token_hint if provided */
720 if (id_token_hint != NULL)
721 apr_table_setn(params, OIDC_PROTO_ID_TOKEN_HINT, id_token_hint);
722
723 /* add the prompt setting if provided (e.g. "none" for no-GUI checks) */
724 const char *prompt = oidc_proto_state_get_prompt(proto_state);
725 if (prompt != NULL)
726 apr_table_setn(params, OIDC_PROTO_PROMPT, prompt);
727
728 /* add any statically configured custom authorization request parameters */
729 add_auth_request_params(r, params, provider->auth_request_params);
730
731 /* add any dynamically configured custom authorization request parameters */
732 add_auth_request_params(r, params, auth_request_params);
733
734 /* add request parameter (request or request_uri) if set */
735 if (provider->request_object != NULL)
736 oidc_proto_add_request_param(r, provider, redirect_uri, params);
737
738 /* send the full authentication request via POST or GET */
739 if (provider->auth_request_method == OIDC_AUTH_REQUEST_METHOD_POST) {
740
741 /* construct a HTML POST auto-submit page with the authorization request parameters */
742 rv = oidc_proto_html_post(r, provider->authorization_endpoint_url,
743 params);
744
745 } else if (provider->auth_request_method == OIDC_AUTH_REQUEST_METHOD_GET) {
746
747 /* construct the full authorization request URL */
748 authorization_request = oidc_util_http_query_encoded_url(r,
749 provider->authorization_endpoint_url, params);
750
751 // TODO: should also enable this when using the POST binding for the auth request
752 /* see if we need to preserve POST parameters through Javascript/HTML5 storage */
753 if (oidc_post_preserve_javascript(r, authorization_request, NULL,
754 NULL) == FALSE) {
755
756 /* add the redirect location header */
757 oidc_util_hdr_out_location_set(r, authorization_request);
758
759 /* and tell Apache to return an HTTP Redirect (302) message */
760 rv = HTTP_MOVED_TEMPORARILY;
761 }
762 } else {
763 oidc_error(r, "provider->auth_request_method set to wrong value: %d",
764 provider->auth_request_method);
765 return HTTP_INTERNAL_SERVER_ERROR;
766 }
767
768 /* add a referred token binding request for the provider if enabled */
769 if ((provider->token_binding_policy > OIDC_TOKEN_BINDING_POLICY_DISABLED)
770 && (oidc_util_get_provided_token_binding_id(r) != NULL))
771 oidc_util_hdr_err_out_add(r,
772 OIDC_HTTP_HDR_INCLUDE_REFERRED_TOKEN_BINDING_ID, "true");
773
774 /* cleanup */
775 oidc_proto_state_destroy(proto_state);
776
777 /* no cache */
778 oidc_util_hdr_err_out_add(r, OIDC_HTTP_HDR_CACHE_CONTROL,
779 "no-cache, no-store, max-age=0");
780
781 /* log our exit code */
782 oidc_debug(r, "return: %d", rv);
783
784 return rv;
785 }
786
787 /*
788 * indicate whether the incoming HTTP POST request is an OpenID Connect Authorization Response
789 */
oidc_proto_is_post_authorization_response(request_rec * r,oidc_cfg * cfg)790 apr_byte_t oidc_proto_is_post_authorization_response(request_rec *r,
791 oidc_cfg *cfg) {
792
793 /* prereq: this is a call to the configured redirect_uri; see if it is a POST */
794 return (r->method_number == M_POST);
795 }
796
797 /*
798 * indicate whether the incoming HTTP GET request is an OpenID Connect Authorization Response
799 */
oidc_proto_is_redirect_authorization_response(request_rec * r,oidc_cfg * cfg)800 apr_byte_t oidc_proto_is_redirect_authorization_response(request_rec *r,
801 oidc_cfg *cfg) {
802
803 /* prereq: this is a call to the configured redirect_uri; see if it is a GET with state and id_token or code parameters */
804 return ((r->method_number == M_GET)
805 && oidc_util_request_has_parameter(r, OIDC_PROTO_STATE)
806 && (oidc_util_request_has_parameter(r, OIDC_PROTO_ID_TOKEN)
807 || oidc_util_request_has_parameter(r, OIDC_PROTO_CODE)));
808 }
809
810 /*
811 * generate a random value (nonce) to correlate request/response through browser state
812 */
oidc_proto_generate_nonce(request_rec * r,char ** nonce,int len)813 apr_byte_t oidc_proto_generate_nonce(request_rec *r, char **nonce, int len) {
814 return oidc_proto_generate_random_string(r, nonce, len);
815 }
816
817 /*
818 * PCKE "plain" proto state
819 */
oidc_proto_pkce_state_plain(request_rec * r,char ** state)820 static apr_byte_t oidc_proto_pkce_state_plain(request_rec *r, char **state) {
821 return oidc_proto_generate_random_string(r, state,
822 OIDC_PROTO_CODE_VERIFIER_LENGTH);
823 }
824
825 /*
826 * PCKE "plain" code_challenge
827 */
oidc_proto_pkce_challenge_plain(request_rec * r,const char * state,char ** code_challenge)828 static apr_byte_t oidc_proto_pkce_challenge_plain(request_rec *r,
829 const char *state, char **code_challenge) {
830 *code_challenge = apr_pstrdup(r->pool, state);
831 return TRUE;
832 }
833
834 /*
835 * PCKE "plain" code_verifier
836 */
oidc_proto_pkce_verifier_plain(request_rec * r,const char * state,char ** code_verifier)837 static apr_byte_t oidc_proto_pkce_verifier_plain(request_rec *r,
838 const char *state, char **code_verifier) {
839 *code_verifier = apr_pstrdup(r->pool, state);
840 return TRUE;
841 }
842
843 /*
844 * PCKE "s256" proto state
845 */
oidc_proto_pkce_state_s256(request_rec * r,char ** state)846 static apr_byte_t oidc_proto_pkce_state_s256(request_rec *r, char **state) {
847 return oidc_proto_generate_random_string(r, state,
848 OIDC_PROTO_CODE_VERIFIER_LENGTH);
849 }
850
851 /*
852 * PCKE "s256" code_challenge
853 */
oidc_proto_pkce_challenge_s256(request_rec * r,const char * state,char ** code_challenge)854 static apr_byte_t oidc_proto_pkce_challenge_s256(request_rec *r,
855 const char *state, char **code_challenge) {
856 if (oidc_util_hash_string_and_base64url_encode(r, OIDC_JOSE_ALG_SHA256,
857 state, code_challenge) == FALSE) {
858 oidc_error(r,
859 "oidc_util_hash_string_and_base64url_encode returned an error for the code verifier");
860 return FALSE;
861 }
862 return TRUE;
863 }
864
865 /*
866 * PCKE "s256" code_verifier
867 */
oidc_proto_pkce_verifier_s256(request_rec * r,const char * state,char ** code_verifier)868 static apr_byte_t oidc_proto_pkce_verifier_s256(request_rec *r,
869 const char *state, char **code_verifier) {
870 *code_verifier = apr_pstrdup(r->pool, state);
871 return TRUE;
872 }
873
874 /*
875 * PCKE "referred_tb" proto state
876 */
oidc_proto_pkce_state_referred_tb(request_rec * r,char ** state)877 static apr_byte_t oidc_proto_pkce_state_referred_tb(request_rec *r,
878 char **state) {
879 *state = NULL;
880 return TRUE;
881 }
882
883 /*
884 * PCKE "referred_tb" code_challenge
885 */
oidc_proto_pkce_challenge_referred_tb(request_rec * r,const char * state,char ** code_challenge)886 static apr_byte_t oidc_proto_pkce_challenge_referred_tb(request_rec *r,
887 const char *state, char **code_challenge) {
888 // state should be NULL
889 *code_challenge = OIDC_PKCE_METHOD_REFERRED_TB;
890 return TRUE;
891 }
892
893 /*
894 * PCKE "referred_tb" code_verifier
895 */
oidc_proto_pkce_verifier_referred_tb(request_rec * r,const char * state,char ** code_verifier)896 static apr_byte_t oidc_proto_pkce_verifier_referred_tb(request_rec *r,
897 const char *state, char **code_verifier) {
898 const char *tb_id = oidc_util_get_provided_token_binding_id(r);
899 *code_verifier = tb_id ? apr_pstrdup(r->pool, tb_id) : NULL;
900 return TRUE;
901 }
902
903 /*
904 * PKCE plain
905 */
906 oidc_proto_pkce_t oidc_pkce_plain = {
907 OIDC_PKCE_METHOD_PLAIN,
908 oidc_proto_pkce_state_plain,
909 oidc_proto_pkce_verifier_plain,
910 oidc_proto_pkce_challenge_plain
911 };
912
913 /*
914 * PKCE s256
915 */
916 oidc_proto_pkce_t oidc_pkce_s256 = {
917 OIDC_PKCE_METHOD_S256,
918 oidc_proto_pkce_state_s256,
919 oidc_proto_pkce_verifier_s256,
920 oidc_proto_pkce_challenge_s256
921 };
922
923 /*
924 * PKCE referred_tb
925 */
926 oidc_proto_pkce_t oidc_pkce_referred_tb = {
927 OIDC_PKCE_METHOD_REFERRED_TB,
928 oidc_proto_pkce_state_referred_tb,
929 oidc_proto_pkce_verifier_referred_tb,
930 oidc_proto_pkce_challenge_referred_tb };
931
932 #define OIDC_PROTO_STATE_ISSUER "i"
933 #define OIDC_PROTO_STATE_ORIGINAL_URL "ou"
934 #define OIDC_PROTO_STATE_ORIGINAL_METHOD "om"
935 #define OIDC_PROTO_STATE_RESPONSE_MODE "rm"
936 #define OIDC_PROTO_STATE_RESPONSE_TYPE "rt"
937 #define OIDC_PROTO_STATE_NONCE "n"
938 #define OIDC_PROTO_STATE_TIMESTAMP "t"
939 #define OIDC_PROTO_STATE_PROMPT "pr"
940 #define OIDC_PROTO_STATE_PKCE_STATE "ps"
941 #define OIDC_PROTO_STATE_STATE "s"
942
oidc_proto_state_get_string_value(oidc_proto_state_t * proto_state,const char * name)943 static const char* oidc_proto_state_get_string_value(
944 oidc_proto_state_t *proto_state, const char *name) {
945 json_t *v = json_object_get(proto_state, name);
946 return v ? json_string_value(v) : NULL;
947 }
948
oidc_proto_state_set_string_value(oidc_proto_state_t * proto_state,const char * name,const char * value)949 static void oidc_proto_state_set_string_value(oidc_proto_state_t *proto_state,
950 const char *name, const char *value) {
951 json_object_set_new(proto_state, name, json_string(value));
952 }
953
oidc_proto_state_new()954 oidc_proto_state_t* oidc_proto_state_new() {
955 return json_object();
956 }
957
oidc_proto_state_destroy(oidc_proto_state_t * proto_state)958 void oidc_proto_state_destroy(oidc_proto_state_t *proto_state) {
959 json_decref(proto_state);
960 }
961
oidc_proto_state_from_cookie(request_rec * r,oidc_cfg * c,const char * cookieValue)962 oidc_proto_state_t* oidc_proto_state_from_cookie(request_rec *r, oidc_cfg *c,
963 const char *cookieValue) {
964 json_t *result = NULL;
965 oidc_util_jwt_verify(r, c->crypto_passphrase, cookieValue, &result);
966 return result;
967 }
968
oidc_proto_state_to_cookie(request_rec * r,oidc_cfg * c,oidc_proto_state_t * proto_state)969 char* oidc_proto_state_to_cookie(request_rec *r, oidc_cfg *c,
970 oidc_proto_state_t *proto_state) {
971 char *cookieValue = NULL;
972 oidc_util_jwt_create(r, c->crypto_passphrase, proto_state, &cookieValue);
973 return cookieValue;
974 }
oidc_proto_state_to_string(request_rec * r,oidc_proto_state_t * proto_state)975 char* oidc_proto_state_to_string(request_rec *r,
976 oidc_proto_state_t *proto_state) {
977 return oidc_util_encode_json_object(r, proto_state, JSON_COMPACT);
978 }
979
oidc_proto_state_get_issuer(oidc_proto_state_t * proto_state)980 const char* oidc_proto_state_get_issuer(oidc_proto_state_t *proto_state) {
981 return oidc_proto_state_get_string_value(proto_state,
982 OIDC_PROTO_STATE_ISSUER);
983 }
984
oidc_proto_state_get_nonce(oidc_proto_state_t * proto_state)985 const char* oidc_proto_state_get_nonce(oidc_proto_state_t *proto_state) {
986 return oidc_proto_state_get_string_value(proto_state,
987 OIDC_PROTO_STATE_NONCE);
988 }
989
oidc_proto_state_get_timestamp(oidc_proto_state_t * proto_state)990 apr_time_t oidc_proto_state_get_timestamp(oidc_proto_state_t *proto_state) {
991 json_t *v = json_object_get(proto_state, OIDC_PROTO_STATE_TIMESTAMP);
992 return v ? apr_time_from_sec(json_integer_value(v)) : -1;
993 }
994
oidc_proto_state_get_prompt(oidc_proto_state_t * proto_state)995 const char* oidc_proto_state_get_prompt(oidc_proto_state_t *proto_state) {
996 return oidc_proto_state_get_string_value(proto_state,
997 OIDC_PROTO_STATE_PROMPT);
998 }
999
oidc_proto_state_get_response_type(oidc_proto_state_t * proto_state)1000 const char* oidc_proto_state_get_response_type(oidc_proto_state_t *proto_state) {
1001 return oidc_proto_state_get_string_value(proto_state,
1002 OIDC_PROTO_STATE_RESPONSE_TYPE);
1003 }
1004
oidc_proto_state_get_response_mode(oidc_proto_state_t * proto_state)1005 const char* oidc_proto_state_get_response_mode(oidc_proto_state_t *proto_state) {
1006 return oidc_proto_state_get_string_value(proto_state,
1007 OIDC_PROTO_STATE_RESPONSE_MODE);
1008 }
1009
oidc_proto_state_get_original_url(oidc_proto_state_t * proto_state)1010 const char* oidc_proto_state_get_original_url(oidc_proto_state_t *proto_state) {
1011 return oidc_proto_state_get_string_value(proto_state,
1012 OIDC_PROTO_STATE_ORIGINAL_URL);
1013 }
1014
oidc_proto_state_get_original_method(oidc_proto_state_t * proto_state)1015 const char* oidc_proto_state_get_original_method(
1016 oidc_proto_state_t *proto_state) {
1017 return oidc_proto_state_get_string_value(proto_state,
1018 OIDC_PROTO_STATE_ORIGINAL_METHOD);
1019 }
1020
oidc_proto_state_get_state(oidc_proto_state_t * proto_state)1021 const char* oidc_proto_state_get_state(oidc_proto_state_t *proto_state) {
1022 return oidc_proto_state_get_string_value(proto_state,
1023 OIDC_PROTO_STATE_STATE);
1024 }
1025
oidc_proto_state_get_pkce_state(oidc_proto_state_t * proto_state)1026 const char* oidc_proto_state_get_pkce_state(oidc_proto_state_t *proto_state) {
1027 return oidc_proto_state_get_string_value(proto_state,
1028 OIDC_PROTO_STATE_PKCE_STATE);
1029 }
1030
oidc_proto_state_set_state(oidc_proto_state_t * proto_state,const char * state)1031 void oidc_proto_state_set_state(oidc_proto_state_t *proto_state,
1032 const char *state) {
1033 oidc_proto_state_set_string_value(proto_state, OIDC_PROTO_STATE_STATE,
1034 state);
1035 }
1036
oidc_proto_state_set_issuer(oidc_proto_state_t * proto_state,const char * issuer)1037 void oidc_proto_state_set_issuer(oidc_proto_state_t *proto_state,
1038 const char *issuer) {
1039 oidc_proto_state_set_string_value(proto_state, OIDC_PROTO_STATE_ISSUER,
1040 issuer);
1041 }
1042
oidc_proto_state_set_original_url(oidc_proto_state_t * proto_state,const char * original_url)1043 void oidc_proto_state_set_original_url(oidc_proto_state_t *proto_state,
1044 const char *original_url) {
1045 oidc_proto_state_set_string_value(proto_state,
1046 OIDC_PROTO_STATE_ORIGINAL_URL, original_url);
1047 }
1048
oidc_proto_state_set_original_method(oidc_proto_state_t * proto_state,const char * original_method)1049 void oidc_proto_state_set_original_method(oidc_proto_state_t *proto_state,
1050 const char *original_method) {
1051 oidc_proto_state_set_string_value(proto_state,
1052 OIDC_PROTO_STATE_ORIGINAL_METHOD, original_method);
1053 }
1054
oidc_proto_state_set_response_mode(oidc_proto_state_t * proto_state,const char * response_mode)1055 void oidc_proto_state_set_response_mode(oidc_proto_state_t *proto_state,
1056 const char *response_mode) {
1057 oidc_proto_state_set_string_value(proto_state,
1058 OIDC_PROTO_STATE_RESPONSE_MODE, response_mode);
1059 }
1060
oidc_proto_state_set_response_type(oidc_proto_state_t * proto_state,const char * response_type)1061 void oidc_proto_state_set_response_type(oidc_proto_state_t *proto_state,
1062 const char *response_type) {
1063 oidc_proto_state_set_string_value(proto_state,
1064 OIDC_PROTO_STATE_RESPONSE_TYPE, response_type);
1065 }
1066
oidc_proto_state_set_nonce(oidc_proto_state_t * proto_state,const char * nonce)1067 void oidc_proto_state_set_nonce(oidc_proto_state_t *proto_state,
1068 const char *nonce) {
1069 oidc_proto_state_set_string_value(proto_state, OIDC_PROTO_STATE_NONCE,
1070 nonce);
1071 }
1072
oidc_proto_state_set_prompt(oidc_proto_state_t * proto_state,const char * prompt)1073 void oidc_proto_state_set_prompt(oidc_proto_state_t *proto_state,
1074 const char *prompt) {
1075 oidc_proto_state_set_string_value(proto_state, OIDC_PROTO_STATE_PROMPT,
1076 prompt);
1077 }
1078
oidc_proto_state_set_pkce_state(oidc_proto_state_t * proto_state,const char * pkce_state)1079 void oidc_proto_state_set_pkce_state(oidc_proto_state_t *proto_state,
1080 const char *pkce_state) {
1081 oidc_proto_state_set_string_value(proto_state,
1082 OIDC_PROTO_STATE_PKCE_STATE, pkce_state);
1083 }
1084
oidc_proto_state_set_timestamp_now(oidc_proto_state_t * proto_state)1085 void oidc_proto_state_set_timestamp_now(oidc_proto_state_t *proto_state) {
1086 json_object_set_new(proto_state, OIDC_PROTO_STATE_TIMESTAMP,
1087 json_integer(apr_time_sec(apr_time_now())));
1088 }
1089
1090 /*
1091 * if a nonce was passed in the authorization request (and stored in the browser state),
1092 * check that it matches the nonce value in the id_token payload
1093 */
1094 // non-static for test.c
oidc_proto_validate_nonce(request_rec * r,oidc_cfg * cfg,oidc_provider_t * provider,const char * nonce,oidc_jwt_t * jwt)1095 apr_byte_t oidc_proto_validate_nonce(request_rec *r, oidc_cfg *cfg,
1096 oidc_provider_t *provider, const char *nonce, oidc_jwt_t *jwt) {
1097
1098 oidc_jose_error_t err;
1099
1100 /* see if we have this nonce cached already */
1101 char *replay = NULL;
1102 oidc_cache_get_nonce(r, nonce, &replay);
1103 if (replay != NULL) {
1104 oidc_error(r,
1105 "the nonce value (%s) passed in the browser state was found in the cache already; possible replay attack!?",
1106 nonce);
1107 return FALSE;
1108 }
1109
1110 /* get the "nonce" value in the id_token payload */
1111 char *j_nonce = NULL;
1112 if (oidc_jose_get_string(r->pool, jwt->payload.value.json,
1113 OIDC_CLAIM_NONCE,
1114 TRUE, &j_nonce, &err) == FALSE) {
1115 oidc_error(r,
1116 "id_token JSON payload did not contain a \"%s\" string: %s",
1117 OIDC_CLAIM_NONCE, oidc_jose_e2s(r->pool, err));
1118 return FALSE;
1119 }
1120
1121 /* see if the nonce in the id_token matches the one that we sent in the authorization request */
1122 if (apr_strnatcmp(nonce, j_nonce) != 0) {
1123 oidc_error(r,
1124 "the nonce value (%s) in the id_token did not match the one stored in the browser session (%s)",
1125 j_nonce, nonce);
1126 return FALSE;
1127 }
1128
1129 /*
1130 * nonce cache duration (replay prevention window) is the 2x the configured
1131 * slack on the timestamp (+-) for token issuance plus 10 seconds for safety
1132 */
1133 apr_time_t nonce_cache_duration = apr_time_from_sec(
1134 provider->idtoken_iat_slack * 2 + 10);
1135
1136 /* store it in the cache for the calculated duration */
1137 oidc_cache_set_nonce(r, nonce, nonce,
1138 apr_time_now() + nonce_cache_duration);
1139
1140 oidc_debug(r,
1141 "nonce \"%s\" validated successfully and is now cached for %" APR_TIME_T_FMT " seconds",
1142 nonce, apr_time_sec(nonce_cache_duration));
1143
1144 return TRUE;
1145 }
1146
1147 /*
1148 * validate the "aud" and "azp" claims in the id_token payload
1149 */
oidc_proto_validate_aud_and_azp(request_rec * r,oidc_cfg * cfg,oidc_provider_t * provider,oidc_jwt_payload_t * id_token_payload)1150 apr_byte_t oidc_proto_validate_aud_and_azp(request_rec *r, oidc_cfg *cfg,
1151 oidc_provider_t *provider, oidc_jwt_payload_t *id_token_payload) {
1152
1153 char *azp = NULL;
1154 oidc_jose_get_string(r->pool, id_token_payload->value.json,
1155 OIDC_CLAIM_AZP,
1156 FALSE, &azp,
1157 NULL);
1158
1159 /*
1160 * the "azp" claim is only needed when the id_token has a single audience value and that audience
1161 * is different than the authorized party; it MAY be included even when the authorized party is
1162 * the same as the sole audience.
1163 */
1164 if ((azp != NULL) && (apr_strnatcmp(azp, provider->client_id) != 0)) {
1165 oidc_error(r,
1166 "the \"%s\" claim (%s) is present in the id_token, but is not equal to the configured client_id (%s)",
1167 OIDC_CLAIM_AZP, azp, provider->client_id);
1168 return FALSE;
1169 }
1170
1171 /* get the "aud" value from the JSON payload */
1172 json_t *aud = json_object_get(id_token_payload->value.json,
1173 OIDC_CLAIM_AUD);
1174 if (aud != NULL) {
1175
1176 /* check if it is a single-value */
1177 if (json_is_string(aud)) {
1178
1179 /* a single-valued audience must be equal to our client_id */
1180 if (apr_strnatcmp(json_string_value(aud), provider->client_id)
1181 != 0) {
1182 oidc_error(r,
1183 "the configured client_id (%s) did not match the \"%s\" claim value (%s) in the id_token",
1184 provider->client_id, OIDC_CLAIM_AUD,
1185 json_string_value(aud));
1186 return FALSE;
1187 }
1188
1189 /* check if this is a multi-valued audience */
1190 } else if (json_is_array(aud)) {
1191
1192 if ((json_array_size(aud) > 1) && (azp == NULL)) {
1193 oidc_debug(r,
1194 "the \"%s\" claim value in the id_token is an array with more than 1 element, but \"%s\" claim is not present (a SHOULD in the spec...)",
1195 OIDC_CLAIM_AUD, OIDC_CLAIM_AZP);
1196 }
1197
1198 if (oidc_util_json_array_has_value(r, aud,
1199 provider->client_id) == FALSE) {
1200 oidc_error(r,
1201 "our configured client_id (%s) could not be found in the array of values for \"%s\" claim",
1202 provider->client_id, OIDC_CLAIM_AUD);
1203 return FALSE;
1204 }
1205 } else {
1206 oidc_error(r,
1207 "id_token JSON payload \"%s\" claim is not a string nor an array",
1208 OIDC_CLAIM_AUD);
1209 return FALSE;
1210 }
1211
1212 } else {
1213 oidc_error(r, "id_token JSON payload did not contain an \"%s\" claim",
1214 OIDC_CLAIM_AUD);
1215 return FALSE;
1216 }
1217
1218 return TRUE;
1219 }
1220
1221 /*
1222 * validate "iat" claim in JWT
1223 */
oidc_proto_validate_iat(request_rec * r,oidc_jwt_t * jwt,apr_byte_t is_mandatory,int slack)1224 static apr_byte_t oidc_proto_validate_iat(request_rec *r, oidc_jwt_t *jwt,
1225 apr_byte_t is_mandatory, int slack) {
1226
1227 /* get the current time */
1228 apr_time_t now = apr_time_sec(apr_time_now());
1229
1230 /* sanity check for iat being set */
1231 if (jwt->payload.iat == OIDC_JWT_CLAIM_TIME_EMPTY) {
1232 if (is_mandatory) {
1233 oidc_error(r, "JWT did not contain an \"%s\" number value",
1234 OIDC_CLAIM_IAT);
1235 return FALSE;
1236 }
1237 return TRUE;
1238 }
1239
1240 /* see if we are asked to enforce a time window at all */
1241 if (slack < 0) {
1242 oidc_debug(r, "slack for JWT set < 0, do not enforce boundary check");
1243 return TRUE;
1244 }
1245
1246 /* check if this id_token has been issued just now +- slack (default 10 minutes) */
1247 if ((now - slack) > jwt->payload.iat) {
1248 oidc_error(r,
1249 "\"iat\" validation failure (%ld): JWT was issued more than %d seconds ago",
1250 (long )jwt->payload.iat, slack);
1251 return FALSE;
1252 }
1253 if ((now + slack) < jwt->payload.iat) {
1254 oidc_error(r,
1255 "\"iat\" validation failure (%ld): JWT was issued more than %d seconds in the future",
1256 (long )jwt->payload.iat, slack);
1257 return FALSE;
1258 }
1259
1260 return TRUE;
1261 }
1262
1263 /*
1264 * validate "exp" claim in JWT
1265 */
oidc_proto_validate_exp(request_rec * r,oidc_jwt_t * jwt,apr_byte_t is_mandatory)1266 static apr_byte_t oidc_proto_validate_exp(request_rec *r, oidc_jwt_t *jwt,
1267 apr_byte_t is_mandatory) {
1268
1269 /* get the current time */
1270 apr_time_t now = apr_time_sec(apr_time_now());
1271
1272 /* sanity check for exp being set */
1273 if (jwt->payload.exp == OIDC_JWT_CLAIM_TIME_EMPTY) {
1274 if (is_mandatory) {
1275 oidc_error(r, "JWT did not contain an \"%s\" number value",
1276 OIDC_CLAIM_EXP);
1277 return FALSE;
1278 }
1279 return TRUE;
1280 }
1281
1282 /* see if now is beyond the JWT expiry timestamp */
1283 apr_time_t expires = jwt->payload.exp;
1284 if (now > expires) {
1285 oidc_error(r,
1286 "\"exp\" validation failure (%ld): JWT expired %ld seconds ago",
1287 (long )expires, (long )(now - expires));
1288 return FALSE;
1289 }
1290
1291 return TRUE;
1292 }
1293
1294 /*
1295 * validate a JSON Web token
1296 */
oidc_proto_validate_jwt(request_rec * r,oidc_jwt_t * jwt,const char * iss,apr_byte_t exp_is_mandatory,apr_byte_t iat_is_mandatory,int iat_slack,int token_binding_policy)1297 apr_byte_t oidc_proto_validate_jwt(request_rec *r, oidc_jwt_t *jwt,
1298 const char *iss, apr_byte_t exp_is_mandatory,
1299 apr_byte_t iat_is_mandatory, int iat_slack, int token_binding_policy) {
1300
1301 if (iss != NULL) {
1302
1303 /* issuer is set and must match */
1304 if (jwt->payload.iss == NULL) {
1305 oidc_error(r,
1306 "JWT did not contain an \"%s\" string (requested value: %s)",
1307 OIDC_CLAIM_ISS, iss);
1308 return FALSE;
1309 }
1310
1311 /* check if the issuer matches the requested value */
1312 if (oidc_util_issuer_match(iss, jwt->payload.iss) == FALSE) {
1313 oidc_error(r,
1314 "requested issuer (%s) does not match received \"%s\" value in id_token (%s)",
1315 iss, OIDC_CLAIM_ISS, jwt->payload.iss);
1316 return FALSE;
1317 }
1318 }
1319
1320 /* check exp */
1321 if (oidc_proto_validate_exp(r, jwt, exp_is_mandatory) == FALSE)
1322 return FALSE;
1323
1324 /* check iat */
1325 if (oidc_proto_validate_iat(r, jwt, iat_is_mandatory, iat_slack) == FALSE)
1326 return FALSE;
1327
1328 /* check the token binding ID in the JWT */
1329 if (oidc_util_json_validate_cnf(r, jwt->payload.value.json,
1330 token_binding_policy) == FALSE)
1331 return FALSE;
1332
1333 return TRUE;
1334 }
1335
1336 /*
1337 * check whether the provided JWT is a valid id_token for the specified "provider"
1338 */
oidc_proto_validate_idtoken(request_rec * r,oidc_provider_t * provider,oidc_jwt_t * jwt,const char * nonce)1339 static apr_byte_t oidc_proto_validate_idtoken(request_rec *r,
1340 oidc_provider_t *provider, oidc_jwt_t *jwt, const char *nonce) {
1341
1342 oidc_cfg *cfg = ap_get_module_config(r->server->module_config,
1343 &auth_openidc_module);
1344
1345 oidc_debug(r, "enter, jwt.header=\"%s\", jwt.payload=\"%s\", nonce=\"%s\"",
1346 jwt->header.value.str, jwt->payload.value.str, nonce);
1347
1348 /* if a nonce is not passed, we're doing a ("code") flow where the nonce is optional */
1349 if (nonce != NULL) {
1350 /* if present, verify the nonce */
1351 if (oidc_proto_validate_nonce(r, cfg, provider, nonce, jwt) == FALSE)
1352 return FALSE;
1353 }
1354
1355 /* validate the ID Token JWT, requiring iss match, and valid exp + iat */
1356 if (oidc_proto_validate_jwt(r, jwt,
1357 provider->validate_issuer ? provider->issuer : NULL, TRUE, TRUE,
1358 provider->idtoken_iat_slack,
1359 provider->token_binding_policy) == FALSE)
1360 return FALSE;
1361
1362 /* check if the required-by-spec "sub" claim is present */
1363 if (jwt->payload.sub == NULL) {
1364 oidc_error(r,
1365 "id_token JSON payload did not contain the required-by-spec \"%s\" string value",
1366 OIDC_CLAIM_SUB);
1367 return FALSE;
1368 }
1369
1370 /* verify the "aud" and "azp" values */
1371 if (oidc_proto_validate_aud_and_azp(r, cfg, provider,
1372 &jwt->payload) == FALSE)
1373 return FALSE;
1374
1375 return TRUE;
1376 }
1377
1378 /*
1379 * get the key from the JWKs that corresponds with the key specified in the header
1380 */
oidc_proto_get_key_from_jwks(request_rec * r,oidc_jwt_t * jwt,json_t * j_jwks,apr_hash_t * result)1381 static apr_byte_t oidc_proto_get_key_from_jwks(request_rec *r, oidc_jwt_t *jwt,
1382 json_t *j_jwks, apr_hash_t *result) {
1383
1384 apr_byte_t rc = TRUE;
1385 oidc_jwk_t *jwk = NULL;
1386 oidc_jose_error_t err;
1387 char *jwk_json = NULL;
1388
1389 /* get the (optional) thumbprint for comparison */
1390 const char *x5t = oidc_jwt_hdr_get(jwt, OIDC_JWK_X5T);
1391 oidc_debug(r, "search for kid \"%s\" or thumbprint x5t \"%s\"",
1392 jwt->header.kid, x5t);
1393
1394 /* get the "keys" JSON array from the JWKs object */
1395 json_t *keys = json_object_get(j_jwks, OIDC_JWK_KEYS);
1396 if ((keys == NULL) || !(json_is_array(keys))) {
1397 oidc_error(r, "\"%s\" array element is not a JSON array",
1398 OIDC_JWK_KEYS);
1399 return FALSE;
1400 }
1401
1402 int i;
1403 for (i = 0; i < json_array_size(keys); i++) {
1404
1405 /* get the next element in the array */
1406 json_t *elem = json_array_get(keys, i);
1407
1408 if (oidc_jwk_parse_json(r->pool, elem, &jwk, &err) == FALSE) {
1409 oidc_warn(r, "oidc_jwk_parse_json failed: %s",
1410 oidc_jose_e2s(r->pool, err));
1411 continue;
1412 }
1413
1414 /* get the key type and see if it is the type that we are looking for */
1415 if (oidc_jwt_alg2kty(jwt) != jwk->kty) {
1416 oidc_debug(r,
1417 "skipping non matching kty=%d for kid=%s because it doesn't match requested kty=%d, kid=%s",
1418 jwk->kty, jwk->kid, oidc_jwt_alg2kty(jwt), jwt->header.kid);
1419 oidc_jwk_destroy(jwk);
1420 continue;
1421 }
1422
1423 /* see if we were looking for a specific kid, if not we'll include any key that matches the type */
1424 if ((jwt->header.kid == NULL) && (x5t == NULL)) {
1425 const char *use = json_string_value(
1426 json_object_get(elem, OIDC_JWK_USE));
1427 if ((use != NULL) && (strcmp(use, OIDC_JWK_SIG) != 0)) {
1428 oidc_debug(r,
1429 "skipping key because of non-matching \"%s\": \"%s\"",
1430 OIDC_JWK_USE, use);
1431 oidc_jwk_destroy(jwk);
1432 } else {
1433 oidc_jwk_to_json(r->pool, jwk, &jwk_json, &err);
1434 oidc_debug(r,
1435 "no kid/x5t to match, include matching key type: %s",
1436 jwk_json);
1437 if (jwk->kid != NULL)
1438 apr_hash_set(result, jwk->kid, APR_HASH_KEY_STRING, jwk);
1439 else
1440 // can do this because we never remove anything from the list
1441 apr_hash_set(result,
1442 apr_psprintf(r->pool, "%d", apr_hash_count(result)),
1443 APR_HASH_KEY_STRING, jwk);
1444 }
1445 continue;
1446 }
1447
1448 /* we are looking for a specific kid, get the kid from the current element */
1449 /* compare the requested kid against the current element */
1450 if ((jwt->header.kid != NULL) && (jwk->kid != NULL)
1451 && (apr_strnatcmp(jwt->header.kid, jwk->kid) == 0)) {
1452 oidc_jwk_to_json(r->pool, jwk, &jwk_json, &err);
1453 oidc_debug(r, "found matching kid: \"%s\" for jwk: %s",
1454 jwt->header.kid, jwk_json);
1455 apr_hash_set(result, jwt->header.kid, APR_HASH_KEY_STRING, jwk);
1456 break;
1457 }
1458
1459 /* we are looking for a specific x5t, get the x5t from the current element */
1460 char *s_x5t = NULL;
1461 oidc_json_object_get_string(r->pool, elem, OIDC_JWK_X5T, &s_x5t,
1462 NULL);
1463 /* compare the requested thumbprint against the current element */
1464 if ((s_x5t != NULL) && (x5t != NULL)
1465 && (apr_strnatcmp(x5t, s_x5t) == 0)) {
1466 oidc_jwk_to_json(r->pool, jwk, &jwk_json, &err);
1467 oidc_debug(r, "found matching %s: \"%s\" for jwk: %s", OIDC_JWK_X5T,
1468 x5t, jwk_json);
1469 apr_hash_set(result, x5t, APR_HASH_KEY_STRING, jwk);
1470 break;
1471 }
1472
1473 /* the right key type but no matching kid/x5t */
1474 oidc_jwk_destroy(jwk);
1475 }
1476
1477 return rc;
1478 }
1479
1480 /*
1481 * get the keys from the (possibly cached) set of JWKs on the jwk_uri that corresponds with the key specified in the header
1482 */
oidc_proto_get_keys_from_jwks_uri(request_rec * r,oidc_cfg * cfg,oidc_jwt_t * jwt,const oidc_jwks_uri_t * jwks_uri,apr_hash_t * keys,apr_byte_t * force_refresh)1483 apr_byte_t oidc_proto_get_keys_from_jwks_uri(request_rec *r, oidc_cfg *cfg,
1484 oidc_jwt_t *jwt, const oidc_jwks_uri_t *jwks_uri, apr_hash_t *keys,
1485 apr_byte_t *force_refresh) {
1486
1487 json_t *j_jwks = NULL;
1488
1489 /* get the set of JSON Web Keys for this provider (possibly by downloading them from the specified provider->jwk_uri) */
1490 oidc_metadata_jwks_get(r, cfg, jwks_uri, &j_jwks, force_refresh);
1491 if (j_jwks == NULL) {
1492 oidc_error(r, "could not %s JSON Web Keys",
1493 *force_refresh ? "refresh" : "get");
1494 return FALSE;
1495 }
1496
1497 /*
1498 * get the key corresponding to the kid from the header, referencing the key that
1499 * was used to sign this message (or get all keys in case no kid was set)
1500 *
1501 * we don't check the error return value because we'll treat "error" in the same
1502 * way as "key not found" i.e. by refreshing the keys from the JWKs URI if not
1503 * already done
1504 */
1505 oidc_proto_get_key_from_jwks(r, jwt, j_jwks, keys);
1506
1507 /* no need anymore for the parsed json_t contents, release the it */
1508 json_decref(j_jwks);
1509
1510 /* if we've got no keys and we did not do a fresh download, then the cache may be stale */
1511 if ((apr_hash_count(keys) < 1) && (*force_refresh == FALSE)) {
1512
1513 /* we did not get a key, but we have not refreshed the JWKs from the jwks_uri yet */
1514 oidc_warn(r,
1515 "could not find a key in the cached JSON Web Keys, doing a forced refresh in case keys were rolled over");
1516 /* get the set of JSON Web Keys forcing a fresh download from the specified JWKs URI */
1517 *force_refresh = TRUE;
1518 return oidc_proto_get_keys_from_jwks_uri(r, cfg, jwt, jwks_uri, keys,
1519 force_refresh);
1520 }
1521
1522 oidc_debug(r,
1523 "returning %d key(s) obtained from the (possibly cached) JWKs URI",
1524 apr_hash_count(keys));
1525
1526 return TRUE;
1527 }
1528
1529 /*
1530 * verify the signature on a JWT using the dynamically obtained and statically configured keys
1531 */
oidc_proto_jwt_verify(request_rec * r,oidc_cfg * cfg,oidc_jwt_t * jwt,const oidc_jwks_uri_t * jwks_uri,apr_hash_t * static_keys,const char * alg)1532 apr_byte_t oidc_proto_jwt_verify(request_rec *r, oidc_cfg *cfg, oidc_jwt_t *jwt,
1533 const oidc_jwks_uri_t *jwks_uri, apr_hash_t *static_keys,
1534 const char *alg) {
1535
1536 oidc_jose_error_t err;
1537 apr_hash_t *dynamic_keys = NULL;
1538
1539 if (alg != NULL) {
1540 if (apr_strnatcmp(jwt->header.alg, alg) != 0) {
1541 oidc_error(r,
1542 "JWT was not signed with the expected configured algorithm: %s != %s",
1543 jwt->header.alg, alg);
1544 return FALSE;
1545 }
1546 }
1547
1548 dynamic_keys = apr_hash_make(r->pool);
1549
1550 /* see if we've got a JWKs URI set for signature validation with dynamically obtained asymmetric keys */
1551 if (jwks_uri->url == NULL) {
1552 oidc_debug(r,
1553 "\"jwks_uri\" is not set, signature validation will only be performed against statically configured keys");
1554 /* the JWKs URI was provided, but let's see if it makes sense to pull down keys, i.e. if it is an asymmetric signature */
1555 } /*else if (oidc_jose_signature_is_hmac(r->pool, jwt)) {
1556 oidc_debug(r,
1557 "\"jwks_uri\" is set, but the JWT has a symmetric signature so we won't pull/use keys from there");
1558 } */else {
1559 apr_byte_t force_refresh = jwt->header.kid == NULL ? TRUE : FALSE;
1560 /* get the key from the JWKs that corresponds with the key specified in the header */
1561 if (oidc_proto_get_keys_from_jwks_uri(r, cfg, jwt, jwks_uri,
1562 dynamic_keys, &force_refresh) == FALSE) {
1563 oidc_jwk_list_destroy_hash(r->pool, dynamic_keys);
1564 return FALSE;
1565 }
1566 }
1567
1568 /* do the actual JWS verification with the locally and remotely provided key material */
1569 // TODO: now static keys "win" if the same `kid` was used in both local and remote key sets
1570 if (oidc_jwt_verify(r->pool, jwt,
1571 oidc_util_merge_key_sets_hash(r->pool, static_keys, dynamic_keys),
1572 &err) == FALSE) {
1573 oidc_error(r, "JWT signature verification failed: %s",
1574 oidc_jose_e2s(r->pool, err));
1575 oidc_jwk_list_destroy_hash(r->pool, dynamic_keys);
1576 return FALSE;
1577 }
1578
1579 oidc_debug(r,
1580 "JWT signature verification with algorithm \"%s\" was successful",
1581 jwt->header.alg);
1582
1583 oidc_jwk_list_destroy_hash(r->pool, dynamic_keys);
1584 return TRUE;
1585 }
1586
1587 /*
1588 * return the compact-encoded JWT header contents
1589 */
oidc_proto_peek_jwt_header(request_rec * r,const char * compact_encoded_jwt,char ** alg)1590 char* oidc_proto_peek_jwt_header(request_rec *r,
1591 const char *compact_encoded_jwt, char **alg) {
1592 char *input = NULL, *result = NULL;
1593 char *p = strstr(compact_encoded_jwt ? compact_encoded_jwt : "", ".");
1594 if (p == NULL) {
1595 oidc_warn(r,
1596 "could not parse first element separated by \".\" from input");
1597 return NULL;
1598 }
1599 input = apr_pstrmemdup(r->pool, compact_encoded_jwt,
1600 strlen(compact_encoded_jwt) - strlen(p));
1601 if (oidc_base64url_decode(r->pool, &result, input) <= 0) {
1602 oidc_warn(r, "oidc_base64url_decode returned an error");
1603 return NULL;
1604 }
1605 if (alg) {
1606 json_t *json = NULL;
1607 oidc_util_decode_json_object(r, result, &json);
1608 if (json)
1609 *alg = apr_pstrdup(r->pool,
1610 json_string_value(json_object_get(json, CJOSE_HDR_ALG)));
1611 json_decref(json);
1612 }
1613 return result;
1614 }
1615
1616 /*
1617 * check whether the provided string is a valid id_token and return its parsed contents
1618 */
oidc_proto_parse_idtoken(request_rec * r,oidc_cfg * cfg,oidc_provider_t * provider,const char * id_token,const char * nonce,oidc_jwt_t ** jwt,apr_byte_t is_code_flow)1619 apr_byte_t oidc_proto_parse_idtoken(request_rec *r, oidc_cfg *cfg,
1620 oidc_provider_t *provider, const char *id_token, const char *nonce,
1621 oidc_jwt_t **jwt, apr_byte_t is_code_flow) {
1622
1623 char *alg = NULL;
1624 oidc_debug(r, "enter: id_token header=%s",
1625 oidc_proto_peek_jwt_header(r, id_token, &alg));
1626 apr_hash_t *decryption_keys = NULL;
1627
1628 char buf[APR_RFC822_DATE_LEN + 1];
1629 oidc_jose_error_t err;
1630 oidc_jwk_t *jwk = NULL;
1631 if (oidc_util_create_symmetric_key(r, provider->client_secret,
1632 oidc_alg2keysize(alg), OIDC_JOSE_ALG_SHA256,
1633 TRUE, &jwk) == FALSE)
1634 return FALSE;
1635
1636 decryption_keys = oidc_util_merge_symmetric_key(r->pool, cfg->private_keys,
1637 jwk);
1638 if (provider->client_encryption_keys)
1639 decryption_keys = oidc_util_merge_key_sets(r->pool, decryption_keys,
1640 provider->client_encryption_keys);
1641
1642 if (oidc_jwt_parse(r->pool, id_token, jwt, decryption_keys, &err) == FALSE) {
1643 oidc_error(r, "oidc_jwt_parse failed: %s", oidc_jose_e2s(r->pool, err));
1644 oidc_jwt_destroy(*jwt);
1645 *jwt = NULL;
1646 return FALSE;
1647 }
1648
1649 oidc_jwk_destroy(jwk);
1650 oidc_debug(r,
1651 "successfully parsed (and possibly decrypted) JWT with header=%s, and payload=%s",
1652 (*jwt)->header.value.str, (*jwt)->payload.value.str);
1653
1654 // make signature validation exception for 'code' flow and the algorithm NONE
1655 if (is_code_flow == FALSE || strcmp((*jwt)->header.alg, "none") != 0) {
1656
1657 jwk = NULL;
1658 if (oidc_util_create_symmetric_key(r, provider->client_secret, 0,
1659 NULL, TRUE, &jwk) == FALSE)
1660 return FALSE;
1661
1662 oidc_jwks_uri_t jwks_uri = { provider->jwks_uri,
1663 provider->jwks_refresh_interval, provider->ssl_validate_server };
1664 if (oidc_proto_jwt_verify(r, cfg, *jwt, &jwks_uri,
1665 oidc_util_merge_symmetric_key(r->pool, NULL, jwk),
1666 provider->id_token_signed_response_alg) == FALSE) {
1667
1668 oidc_error(r,
1669 "id_token signature could not be validated, aborting");
1670 oidc_jwt_destroy(*jwt);
1671 *jwt = NULL;
1672 oidc_jwk_destroy(jwk);
1673 return FALSE;
1674 }
1675 oidc_jwk_destroy(jwk);
1676 }
1677
1678 /* this is where the meat is */
1679 if (oidc_proto_validate_idtoken(r, provider, *jwt, nonce) == FALSE) {
1680 oidc_error(r, "id_token payload could not be validated, aborting");
1681 oidc_jwt_destroy(*jwt);
1682 *jwt = NULL;
1683 return FALSE;
1684 }
1685
1686 /* log our results */
1687
1688 apr_rfc822_date(buf, apr_time_from_sec((*jwt)->payload.exp));
1689 oidc_debug(r,
1690 "valid id_token for user \"%s\" expires: [%s], in %ld secs from now)",
1691 (*jwt)->payload.sub, buf,
1692 (long)((*jwt)->payload.exp - apr_time_sec(apr_time_now())));
1693
1694 /* since we've made it so far, we may as well say it is a valid id_token */
1695 return TRUE;
1696 }
1697
1698 /*
1699 * check that the access_token type is supported
1700 */
oidc_proto_validate_token_type(request_rec * r,oidc_provider_t * provider,const char * token_type)1701 static apr_byte_t oidc_proto_validate_token_type(request_rec *r,
1702 oidc_provider_t *provider, const char *token_type) {
1703 /* we only support bearer/Bearer */
1704 if ((token_type != NULL)
1705 && (apr_strnatcasecmp(token_type, OIDC_PROTO_BEARER) != 0)
1706 && (provider->userinfo_endpoint_url != NULL)) {
1707 oidc_error(r,
1708 "token_type is \"%s\" and UserInfo endpoint (%s) for issuer \"%s\" is set: can only deal with \"%s\" authentication against a UserInfo endpoint!",
1709 token_type, provider->userinfo_endpoint_url, provider->issuer,
1710 OIDC_PROTO_BEARER);
1711 return FALSE;
1712 }
1713 return TRUE;
1714 }
1715
1716 /*
1717 * setup for an endpoint call without authentication
1718 */
oidc_proto_endpoint_auth_none(request_rec * r,const char * client_id,apr_table_t * params)1719 static apr_byte_t oidc_proto_endpoint_auth_none(request_rec *r,
1720 const char *client_id, apr_table_t *params) {
1721 apr_table_set(params, OIDC_PROTO_CLIENT_ID, client_id);
1722 return TRUE;
1723 }
1724
1725 /*
1726 * setup for an endpoint call with OIDC client_secret_basic authentication
1727 */
oidc_proto_endpoint_client_secret_basic(request_rec * r,const char * client_id,const char * client_secret,char ** basic_auth_str)1728 static apr_byte_t oidc_proto_endpoint_client_secret_basic(request_rec *r,
1729 const char *client_id, const char *client_secret, char **basic_auth_str) {
1730 oidc_debug(r, "enter");
1731 if (client_secret == NULL) {
1732 oidc_error(r, "no client secret is configured");
1733 return FALSE;
1734 }
1735 *basic_auth_str = apr_psprintf(r->pool, "%s:%s",
1736 oidc_util_escape_string(r, client_id),
1737 oidc_util_escape_string(r, client_secret));
1738
1739 return TRUE;
1740 }
1741
1742 /*
1743 * setup for an endpoint call with OIDC client_secret_post authentication
1744 */
oidc_proto_endpoint_client_secret_post(request_rec * r,const char * client_id,const char * client_secret,apr_table_t * params)1745 static apr_byte_t oidc_proto_endpoint_client_secret_post(request_rec *r,
1746 const char *client_id, const char *client_secret, apr_table_t *params) {
1747 oidc_debug(r, "enter");
1748 if (client_secret == NULL) {
1749 oidc_error(r, "no client secret is configured");
1750 return FALSE;
1751 }
1752 apr_table_set(params, OIDC_PROTO_CLIENT_ID, client_id);
1753 apr_table_set(params, OIDC_PROTO_CLIENT_SECRET, client_secret);
1754 return TRUE;
1755 }
1756
1757 #define OIDC_PROTO_ASSERTION_JTI_LEN 16
1758
1759 /*
1760 * helper function to create a JWT assertion for endpoint authentication
1761 */
oidc_proto_jwt_create(request_rec * r,const char * client_id,const char * audience,oidc_jwt_t ** out)1762 static apr_byte_t oidc_proto_jwt_create(request_rec *r, const char *client_id,
1763 const char *audience, oidc_jwt_t **out) {
1764
1765 *out = oidc_jwt_new(r->pool, TRUE, TRUE);
1766 oidc_jwt_t *jwt = *out;
1767
1768 char *jti = NULL;
1769 oidc_proto_generate_random_string(r, &jti,
1770 OIDC_PROTO_ASSERTION_JTI_LEN);
1771
1772 json_object_set_new(jwt->payload.value.json, OIDC_CLAIM_ISS,
1773 json_string(client_id));
1774 json_object_set_new(jwt->payload.value.json, OIDC_CLAIM_SUB,
1775 json_string(client_id));
1776 json_object_set_new(jwt->payload.value.json, OIDC_CLAIM_AUD,
1777 json_string(audience));
1778 json_object_set_new(jwt->payload.value.json, OIDC_CLAIM_JTI,
1779 json_string(jti));
1780 json_object_set_new(jwt->payload.value.json, OIDC_CLAIM_EXP,
1781 json_integer(apr_time_sec(apr_time_now()) + 60));
1782 json_object_set_new(jwt->payload.value.json, OIDC_CLAIM_IAT,
1783 json_integer(apr_time_sec(apr_time_now())));
1784
1785 return TRUE;
1786 }
1787
1788 /*
1789 * helper function to add a JWT assertion to the HTTP request as endpoint authentication
1790 */
oidc_proto_jwt_sign_and_add(request_rec * r,apr_table_t * params,oidc_jwt_t * jwt,oidc_jwk_t * jwk)1791 static apr_byte_t oidc_proto_jwt_sign_and_add(request_rec *r,
1792 apr_table_t *params, oidc_jwt_t *jwt, oidc_jwk_t *jwk) {
1793 oidc_jose_error_t err;
1794
1795 if (oidc_jwt_sign(r->pool, jwt, jwk, &err) == FALSE) {
1796 oidc_error(r, "signing JWT failed: %s", oidc_jose_e2s(r->pool, err));
1797 return FALSE;
1798 }
1799
1800 char *cser = oidc_jwt_serialize(r->pool, jwt, &err);
1801 if (cser == NULL) {
1802 oidc_error(r, "oidc_jwt_serialize failed: %s",
1803 oidc_jose_e2s(r->pool, err));
1804 return FALSE;
1805 }
1806
1807 apr_table_setn(params, OIDC_PROTO_CLIENT_ASSERTION_TYPE,
1808 OIDC_PROTO_CLIENT_ASSERTION_TYPE_JWT_BEARER);
1809 apr_table_set(params, OIDC_PROTO_CLIENT_ASSERTION, cser);
1810
1811 return TRUE;
1812 }
1813
1814 #define OIDC_PROTO_JWT_ASSERTION_SYMMETRIC_ALG CJOSE_HDR_ALG_HS256
1815
oidc_proto_endpoint_auth_client_secret_jwt(request_rec * r,const char * client_id,const char * client_secret,const char * audience,apr_table_t * params)1816 static apr_byte_t oidc_proto_endpoint_auth_client_secret_jwt(request_rec *r,
1817 const char *client_id, const char *client_secret, const char *audience,
1818 apr_table_t *params) {
1819 oidc_jwt_t *jwt = NULL;
1820 oidc_jose_error_t err;
1821
1822 oidc_debug(r, "enter");
1823
1824 if (oidc_proto_jwt_create(r, client_id, audience, &jwt) == FALSE)
1825 return FALSE;
1826
1827 oidc_jwk_t *jwk = oidc_jwk_create_symmetric_key(r->pool, NULL,
1828 (const unsigned char*) client_secret, strlen(client_secret),
1829 FALSE, &err);
1830 if (jwk == NULL) {
1831 oidc_error(r, "parsing of client secret into JWK failed: %s",
1832 oidc_jose_e2s(r->pool, err));
1833 oidc_jwt_destroy(jwt);
1834 return FALSE;
1835 }
1836
1837 jwt->header.alg = apr_pstrdup(r->pool,
1838 OIDC_PROTO_JWT_ASSERTION_SYMMETRIC_ALG);
1839
1840 oidc_proto_jwt_sign_and_add(r, params, jwt, jwk);
1841
1842 oidc_jwt_destroy(jwt);
1843 oidc_jwk_destroy(jwk);
1844
1845 return TRUE;
1846 }
1847
oidc_proto_endpoint_access_token_bearer(request_rec * r,oidc_cfg * cfg,const char * bearer_access_token,char ** bearer_auth_str)1848 static apr_byte_t oidc_proto_endpoint_access_token_bearer(request_rec *r,
1849 oidc_cfg *cfg, const char *bearer_access_token, char **bearer_auth_str) {
1850
1851 apr_byte_t rv = TRUE;
1852
1853 if (bearer_access_token != NULL) {
1854 *bearer_auth_str = apr_psprintf(r->pool, "%s", bearer_access_token);
1855 } else {
1856 oidc_error(r,
1857 "endpoint auth method set to bearer access token but no token is provided");
1858 rv = FALSE;
1859 }
1860
1861 return rv;
1862 }
1863
1864 #define OIDC_PROTO_JWT_ASSERTION_ASYMMETRIC_ALG CJOSE_HDR_ALG_RS256
1865
oidc_proto_endpoint_auth_private_key_jwt(request_rec * r,oidc_cfg * cfg,const char * client_id,const apr_array_header_t * client_signing_keys,const char * audience,apr_table_t * params)1866 static apr_byte_t oidc_proto_endpoint_auth_private_key_jwt(request_rec *r,
1867 oidc_cfg *cfg, const char *client_id,
1868 const apr_array_header_t *client_signing_keys, const char *audience,
1869 apr_table_t *params) {
1870 oidc_jwt_t *jwt = NULL;
1871 oidc_jwk_t *jwk = NULL;
1872 const apr_array_header_t *signing_keys = NULL;
1873
1874 oidc_debug(r, "enter");
1875
1876 if (oidc_proto_jwt_create(r, client_id, audience, &jwt) == FALSE)
1877 return FALSE;
1878
1879 if ((client_signing_keys != NULL) && (client_signing_keys->nelts > 0)) {
1880 signing_keys = client_signing_keys;
1881 } else if ((cfg->private_keys != NULL) && (cfg->private_keys->nelts > 0)) {
1882 signing_keys = cfg->private_keys;
1883 } else {
1884 oidc_error(r,
1885 "no private keys have been configured to use for private_key_jwt client authentication (" OIDCPrivateKeyFiles ")");
1886 oidc_jwt_destroy(jwt);
1887 return FALSE;
1888 }
1889
1890 jwk = ((oidc_jwk_t**) signing_keys->elts)[0];
1891
1892 jwt->header.kid = apr_pstrdup(r->pool, jwk->kid);
1893 jwt->header.alg = apr_pstrdup(r->pool, CJOSE_HDR_ALG_RS256);
1894
1895 oidc_proto_jwt_sign_and_add(r, params, jwt, jwk);
1896
1897 oidc_jwt_destroy(jwt);
1898
1899 return TRUE;
1900 }
1901
oidc_proto_token_endpoint_auth(request_rec * r,oidc_cfg * cfg,const char * token_endpoint_auth,const char * client_id,const char * client_secret,const apr_array_header_t * client_signing_keys,const char * audience,apr_table_t * params,const char * bearer_access_token,char ** basic_auth_str,char ** bearer_auth_str)1902 apr_byte_t oidc_proto_token_endpoint_auth(request_rec *r, oidc_cfg *cfg,
1903 const char *token_endpoint_auth, const char *client_id,
1904 const char *client_secret,
1905 const apr_array_header_t *client_signing_keys, const char *audience,
1906 apr_table_t *params, const char *bearer_access_token,
1907 char **basic_auth_str, char **bearer_auth_str) {
1908
1909 oidc_debug(r, "token_endpoint_auth=%s", token_endpoint_auth);
1910
1911 if (client_id == NULL) {
1912 oidc_debug(r, "no client ID set: assume we don't need to authenticate");
1913 return TRUE;
1914 }
1915
1916 // default is client_secret_basic, but only if a client_secret is set,
1917 // otherwise we are a public client
1918 if ((token_endpoint_auth == NULL) && (client_secret != NULL))
1919 token_endpoint_auth = OIDC_PROTO_CLIENT_SECRET_BASIC;
1920
1921 if ((token_endpoint_auth == NULL) || (apr_strnatcmp(token_endpoint_auth,
1922 OIDC_PROTO_ENDPOINT_AUTH_NONE) == 0)) {
1923 oidc_debug(r,
1924 "no client secret is configured or the token endpoint auth method was set to \"%s\"; calling the token endpoint without client authentication; only public clients are supported",
1925 OIDC_PROTO_ENDPOINT_AUTH_NONE);
1926 return oidc_proto_endpoint_auth_none(r, client_id, params);
1927 }
1928
1929 // if no client_secret is set and we don't authenticate using private_key_jwt,
1930 // we can only be a public client since the other methods require a client_secret
1931 if ((client_secret == NULL) && (apr_strnatcmp(token_endpoint_auth,
1932 OIDC_PROTO_PRIVATE_KEY_JWT) != 0)) {
1933 oidc_debug(r,
1934 "no client secret set and not using private_key_jwt, assume we are a public client");
1935 return oidc_proto_endpoint_auth_none(r, client_id, params);
1936 }
1937
1938 if (apr_strnatcmp(token_endpoint_auth,
1939 OIDC_PROTO_CLIENT_SECRET_BASIC) == 0)
1940 return oidc_proto_endpoint_client_secret_basic(r, client_id,
1941 client_secret, basic_auth_str);
1942
1943 if (apr_strnatcmp(token_endpoint_auth,
1944 OIDC_PROTO_CLIENT_SECRET_POST) == 0)
1945 return oidc_proto_endpoint_client_secret_post(r, client_id,
1946 client_secret, params);
1947
1948 if (apr_strnatcmp(token_endpoint_auth,
1949 OIDC_PROTO_CLIENT_SECRET_JWT) == 0)
1950 return oidc_proto_endpoint_auth_client_secret_jwt(r, client_id,
1951 client_secret, audience, params);
1952
1953 if (apr_strnatcmp(token_endpoint_auth,
1954 OIDC_PROTO_PRIVATE_KEY_JWT) == 0)
1955 return oidc_proto_endpoint_auth_private_key_jwt(r, cfg, client_id,
1956 client_signing_keys, audience, params);
1957
1958 if (apr_strnatcmp(token_endpoint_auth,
1959 OIDC_PROTO_BEARER_ACCESS_TOKEN) == 0) {
1960 return oidc_proto_endpoint_access_token_bearer(r, cfg,
1961 bearer_access_token, bearer_auth_str);
1962 }
1963
1964 oidc_error(r, "uhm, shouldn't be here...");
1965
1966 return FALSE;
1967 }
1968
1969 /*
1970 * send a code/refresh request to the token endpoint and return the parsed contents
1971 */
oidc_proto_token_endpoint_request(request_rec * r,oidc_cfg * cfg,oidc_provider_t * provider,apr_table_t * params,char ** id_token,char ** access_token,char ** token_type,int * expires_in,char ** refresh_token)1972 static apr_byte_t oidc_proto_token_endpoint_request(request_rec *r,
1973 oidc_cfg *cfg, oidc_provider_t *provider, apr_table_t *params,
1974 char **id_token, char **access_token, char **token_type,
1975 int *expires_in, char **refresh_token) {
1976
1977 char *response = NULL;
1978 char *basic_auth = NULL;
1979 char *bearer_auth = NULL;
1980
1981 /* add the token endpoint authentication credentials */
1982 if (oidc_proto_token_endpoint_auth(r, cfg, provider->token_endpoint_auth,
1983 provider->client_id, provider->client_secret,
1984 provider->client_signing_keys, provider->token_endpoint_url, params,
1985 NULL, &basic_auth, &bearer_auth) == FALSE)
1986 return FALSE;
1987
1988 /* add any configured extra static parameters to the token endpoint */
1989 oidc_util_table_add_query_encoded_params(r->pool, params,
1990 provider->token_endpoint_params);
1991
1992 /* send the refresh request to the token endpoint */
1993 if (oidc_util_http_post_form(r, provider->token_endpoint_url, params,
1994 basic_auth, bearer_auth, provider->ssl_validate_server, &response,
1995 cfg->http_timeout_long, cfg->outgoing_proxy,
1996 oidc_dir_cfg_pass_cookies(r),
1997 oidc_util_get_full_path(r->pool,
1998 provider->token_endpoint_tls_client_cert),
1999 oidc_util_get_full_path(r->pool,
2000 provider->token_endpoint_tls_client_key)) == FALSE) {
2001 oidc_warn(r, "error when calling the token endpoint (%s)",
2002 provider->token_endpoint_url);
2003 return FALSE;
2004 }
2005
2006 /* check for errors, the response itself will have been logged already */
2007 json_t *result = NULL;
2008 if (oidc_util_decode_json_and_check_error(r, response, &result) == FALSE)
2009 return FALSE;
2010
2011 /* get the id_token from the parsed response */
2012 oidc_json_object_get_string(r->pool, result, OIDC_PROTO_ID_TOKEN, id_token,
2013 NULL);
2014
2015 /* get the access_token from the parsed response */
2016 oidc_json_object_get_string(r->pool, result, OIDC_PROTO_ACCESS_TOKEN,
2017 access_token,
2018 NULL);
2019
2020 /* get the token type from the parsed response */
2021 oidc_json_object_get_string(r->pool, result, OIDC_PROTO_TOKEN_TYPE,
2022 token_type,
2023 NULL);
2024
2025 /* check the new token type */
2026 if (token_type != NULL) {
2027 if (oidc_proto_validate_token_type(r, provider, *token_type) == FALSE) {
2028 oidc_warn(r, "access token type did not validate, dropping it");
2029 *access_token = NULL;
2030 }
2031 }
2032
2033 /* get the expires_in value */
2034 oidc_json_object_get_int(r->pool, result, OIDC_PROTO_EXPIRES_IN, expires_in,
2035 -1);
2036
2037 /* get the refresh_token from the parsed response */
2038 oidc_json_object_get_string(r->pool, result, OIDC_PROTO_REFRESH_TOKEN,
2039 refresh_token,
2040 NULL);
2041
2042 json_decref(result);
2043
2044 return TRUE;
2045 }
2046
2047 /*
2048 * resolves the code received from the OP in to an id_token, access_token and refresh_token
2049 */
oidc_proto_resolve_code(request_rec * r,oidc_cfg * cfg,oidc_provider_t * provider,const char * code,const char * code_verifier,char ** id_token,char ** access_token,char ** token_type,int * expires_in,char ** refresh_token,const char * state)2050 static apr_byte_t oidc_proto_resolve_code(request_rec *r, oidc_cfg *cfg,
2051 oidc_provider_t *provider, const char *code, const char *code_verifier,
2052 char **id_token, char **access_token, char **token_type,
2053 int *expires_in, char **refresh_token, const char *state) {
2054
2055 oidc_debug(r, "enter");
2056
2057 /* assemble the parameters for a call to the token endpoint */
2058 apr_table_t *params = apr_table_make(r->pool, 5);
2059 apr_table_setn(params, OIDC_PROTO_GRANT_TYPE,
2060 OIDC_PROTO_GRANT_TYPE_AUTHZ_CODE);
2061 apr_table_setn(params, OIDC_PROTO_CODE, code);
2062 apr_table_set(params, OIDC_PROTO_REDIRECT_URI,
2063 oidc_get_redirect_uri_iss(r, cfg, provider));
2064
2065 if (code_verifier)
2066 apr_table_setn(params, OIDC_PROTO_CODE_VERIFIER, code_verifier);
2067
2068 /* add state to mitigate IDP mixup attacks, only useful in a multi-provider setup */
2069 if ((cfg->metadata_dir != NULL) && (state))
2070 apr_table_setn(params, OIDC_PROTO_STATE, state);
2071
2072 return oidc_proto_token_endpoint_request(r, cfg, provider, params, id_token,
2073 access_token, token_type, expires_in, refresh_token);
2074 }
2075
2076 /*
2077 * refreshes the access_token/id_token /refresh_token received from the OP using the refresh_token
2078 */
oidc_proto_refresh_request(request_rec * r,oidc_cfg * cfg,oidc_provider_t * provider,const char * rtoken,char ** id_token,char ** access_token,char ** token_type,int * expires_in,char ** refresh_token)2079 apr_byte_t oidc_proto_refresh_request(request_rec *r, oidc_cfg *cfg,
2080 oidc_provider_t *provider, const char *rtoken, char **id_token,
2081 char **access_token, char **token_type, int *expires_in,
2082 char **refresh_token) {
2083
2084 oidc_debug(r, "enter");
2085
2086 /* assemble the parameters for a call to the token endpoint */
2087 apr_table_t *params = apr_table_make(r->pool, 5);
2088 apr_table_setn(params, OIDC_PROTO_GRANT_TYPE,
2089 OIDC_PROTO_GRANT_TYPE_REFRESH_TOKEN);
2090 apr_table_setn(params, OIDC_PROTO_REFRESH_TOKEN, rtoken);
2091 apr_table_setn(params, OIDC_PROTO_SCOPE, provider->scope);
2092
2093 return oidc_proto_token_endpoint_request(r, cfg, provider, params, id_token,
2094 access_token, token_type, expires_in, refresh_token);
2095 }
2096
oidc_user_info_response_validate(request_rec * r,oidc_cfg * cfg,oidc_provider_t * provider,char ** response,json_t ** claims,char ** userinfo_jwt)2097 static apr_byte_t oidc_user_info_response_validate(request_rec *r,
2098 oidc_cfg *cfg, oidc_provider_t *provider, char **response,
2099 json_t **claims, char **userinfo_jwt) {
2100
2101 oidc_debug(r,
2102 "enter: userinfo_signed_response_alg=%s, userinfo_encrypted_response_alg=%s, userinfo_encrypted_response_enc=%s",
2103 provider->userinfo_signed_response_alg,
2104 provider->userinfo_encrypted_response_alg,
2105 provider->userinfo_encrypted_response_enc);
2106
2107 char *alg = NULL;
2108 if ((provider->userinfo_signed_response_alg != NULL)
2109 || (provider->userinfo_encrypted_response_alg != NULL)
2110 || (provider->userinfo_encrypted_response_enc != NULL)) {
2111 oidc_debug(r, "JWT header=%s",
2112 oidc_proto_peek_jwt_header(r, *response, &alg));
2113 }
2114
2115 oidc_jose_error_t err;
2116 oidc_jwk_t *jwk = NULL;
2117 oidc_jwt_t *jwt = NULL;
2118 char *payload = NULL;
2119
2120 if (oidc_util_create_symmetric_key(r, provider->client_secret,
2121 oidc_alg2keysize(alg), OIDC_JOSE_ALG_SHA256,
2122 TRUE, &jwk) == FALSE)
2123 return FALSE;
2124
2125 if (provider->userinfo_encrypted_response_alg != NULL) {
2126 if (oidc_jwe_decrypt(r->pool, *response,
2127 oidc_util_merge_symmetric_key(r->pool, cfg->private_keys, jwk),
2128 &payload, &err, TRUE) == FALSE) {
2129 oidc_error(r, "oidc_jwe_decrypt failed: %s",
2130 oidc_jose_e2s(r->pool, err));
2131 oidc_jwk_destroy(jwk);
2132 return FALSE;
2133 } else {
2134 oidc_debug(r,
2135 "successfully decrypted JWE returned from userinfo endpoint: %s",
2136 payload);
2137 *response = payload;
2138 }
2139 }
2140
2141 if (provider->userinfo_signed_response_alg != NULL) {
2142 if (oidc_jwt_parse(r->pool, *response, &jwt,
2143 oidc_util_merge_symmetric_key(r->pool, cfg->private_keys, jwk),
2144 &err) == FALSE) {
2145 oidc_error(r, "oidc_jwt_parse failed: %s",
2146 oidc_jose_e2s(r->pool, err));
2147 oidc_jwt_destroy(jwt);
2148 oidc_jwk_destroy(jwk);
2149 return FALSE;
2150 }
2151 oidc_debug(r, "successfully parsed JWT with header=%s, and payload=%s",
2152 jwt->header.value.str, jwt->payload.value.str);
2153
2154 oidc_jwk_destroy(jwk);
2155
2156 jwk = NULL;
2157 if (oidc_util_create_symmetric_key(r, provider->client_secret, 0,
2158 NULL, TRUE, &jwk) == FALSE)
2159 return FALSE;
2160
2161 oidc_jwks_uri_t jwks_uri = { provider->jwks_uri,
2162 provider->jwks_refresh_interval, provider->ssl_validate_server };
2163 if (oidc_proto_jwt_verify(r, cfg, jwt, &jwks_uri,
2164 oidc_util_merge_symmetric_key(r->pool, NULL, jwk),
2165 provider->userinfo_signed_response_alg) == FALSE) {
2166
2167 oidc_error(r, "JWT signature could not be validated, aborting");
2168 oidc_jwt_destroy(jwt);
2169 oidc_jwk_destroy(jwk);
2170 return FALSE;
2171 }
2172 oidc_jwk_destroy(jwk);
2173 oidc_debug(r,
2174 "successfully verified signed JWT returned from userinfo endpoint: %s",
2175 jwt->payload.value.str);
2176
2177 *userinfo_jwt = apr_pstrdup(r->pool, *response);
2178 *claims = json_deep_copy(jwt->payload.value.json);
2179 *response = apr_pstrdup(r->pool, jwt->payload.value.str);
2180 oidc_jwt_destroy(jwt);
2181
2182 return TRUE;
2183 }
2184
2185 oidc_jwk_destroy(jwk);
2186
2187 return oidc_util_decode_json_and_check_error(r, *response, claims);
2188 }
2189
2190 #define OIDC_COMPOSITE_CLAIM_NAMES "_claim_names"
2191 #define OIDC_COMPOSITE_CLAIM_SOURCES "_claim_sources"
2192 #define OIDC_COMPOSITE_CLAIM_JWT "JWT"
2193 #define OIDC_COMPOSITE_CLAIM_ACCESS_TOKEN OIDC_PROTO_ACCESS_TOKEN
2194 #define OIDC_COMPOSITE_CLAIM_ENDPOINT "endpoint"
2195
oidc_proto_resolve_composite_claims(request_rec * r,oidc_cfg * cfg,json_t * claims)2196 static apr_byte_t oidc_proto_resolve_composite_claims(request_rec *r,
2197 oidc_cfg *cfg, json_t *claims) {
2198 const char *key;
2199 json_t *value;
2200 void *iter;
2201 json_t *sources, *names, *decoded;
2202 oidc_jose_error_t err;
2203 oidc_jwk_t *jwk = NULL;
2204
2205 oidc_debug(r, "enter");
2206
2207 names = json_object_get(claims, OIDC_COMPOSITE_CLAIM_NAMES);
2208 if ((names == NULL) || (!json_is_object(names)))
2209 return FALSE;
2210
2211 sources = json_object_get(claims, OIDC_COMPOSITE_CLAIM_SOURCES);
2212 if ((sources == NULL) || (!json_is_object(sources))) {
2213 oidc_debug(r, "%s found, but no %s found", OIDC_COMPOSITE_CLAIM_NAMES,
2214 OIDC_COMPOSITE_CLAIM_SOURCES);
2215 return FALSE;
2216 }
2217
2218 decoded = json_object();
2219
2220 iter = json_object_iter(sources);
2221 while (iter) {
2222 key = json_object_iter_key(iter);
2223 value = json_object_iter_value(iter);
2224 if ((value != NULL) && (json_is_object(value))) {
2225 json_t *jwt = json_object_get(value, OIDC_COMPOSITE_CLAIM_JWT);
2226 char *s_json = NULL;
2227 if ((jwt != NULL) && (json_is_string(jwt))) {
2228 s_json = apr_pstrdup(r->pool, json_string_value(jwt));
2229 } else {
2230 const char *access_token = json_string_value(
2231 json_object_get(value,
2232 OIDC_COMPOSITE_CLAIM_ACCESS_TOKEN));
2233 const char *endpoint = json_string_value(json_object_get(value,
2234 OIDC_COMPOSITE_CLAIM_ENDPOINT));
2235 if ((access_token != NULL) && (endpoint != NULL)) {
2236 oidc_util_http_get(r, endpoint,
2237 NULL, NULL, access_token, cfg->provider.ssl_validate_server,
2238 &s_json, cfg->http_timeout_long,
2239 cfg->outgoing_proxy, oidc_dir_cfg_pass_cookies(r),
2240 NULL, NULL);
2241 }
2242 }
2243 if ((s_json != NULL) && (strcmp(s_json, "") != 0)) {
2244 oidc_jwt_t *jwt = NULL;
2245 if (oidc_jwt_parse(r->pool, s_json, &jwt,
2246 oidc_util_merge_symmetric_key(r->pool,
2247 cfg->private_keys, jwk), &err) == FALSE) {
2248 oidc_error(r,
2249 "could not parse JWT from aggregated claim \"%s\": %s",
2250 key, oidc_jose_e2s(r->pool, err));
2251 } else {
2252 json_t *v = json_object_get(decoded, key);
2253 if (v == NULL) {
2254 v = json_object();
2255 json_object_set_new(decoded, key, v);
2256 }
2257 oidc_util_json_merge(r, jwt->payload.value.json, v);
2258 }
2259 oidc_jwt_destroy(jwt);
2260 }
2261 }
2262 iter = json_object_iter_next(sources, iter);
2263 }
2264
2265 iter = json_object_iter(names);
2266 while (iter) {
2267 key = json_object_iter_key(iter);
2268 const char *s_value = json_string_value(json_object_iter_value(iter));
2269 if (s_value != NULL) {
2270 oidc_debug(r, "processing: %s: %s", key, s_value);
2271 json_t *values = json_object_get(decoded, s_value);
2272 if (values != NULL) {
2273 json_object_set(claims, key, json_object_get(values, key));
2274 } else {
2275 oidc_warn(r, "no values for source \"%s\" found", s_value);
2276 }
2277 } else {
2278 oidc_warn(r, "no string value found for claim \"%s\"", key);
2279 }
2280 iter = json_object_iter_next(names, iter);
2281 }
2282
2283 json_object_del(claims, OIDC_COMPOSITE_CLAIM_NAMES);
2284 json_object_del(claims, OIDC_COMPOSITE_CLAIM_SOURCES);
2285 json_decref(decoded);
2286
2287 return TRUE;
2288 }
2289
2290 /*
2291 * get claims from the OP UserInfo endpoint using the provided access_token
2292 */
oidc_proto_resolve_userinfo(request_rec * r,oidc_cfg * cfg,oidc_provider_t * provider,const char * id_token_sub,const char * access_token,char ** response,char ** userinfo_jwt)2293 apr_byte_t oidc_proto_resolve_userinfo(request_rec *r, oidc_cfg *cfg,
2294 oidc_provider_t *provider, const char *id_token_sub,
2295 const char *access_token, char **response, char **userinfo_jwt) {
2296
2297 oidc_debug(r, "enter, endpoint=%s, access_token=%s",
2298 provider->userinfo_endpoint_url, access_token);
2299
2300 /* get the JSON response */
2301 if (provider->userinfo_token_method == OIDC_USER_INFO_TOKEN_METHOD_HEADER) {
2302 if (oidc_util_http_get(r, provider->userinfo_endpoint_url,
2303 NULL, NULL, access_token, provider->ssl_validate_server, response,
2304 cfg->http_timeout_long, cfg->outgoing_proxy,
2305 oidc_dir_cfg_pass_cookies(r), NULL, NULL) == FALSE)
2306 return FALSE;
2307 } else if (provider->userinfo_token_method
2308 == OIDC_USER_INFO_TOKEN_METHOD_POST) {
2309 apr_table_t *params = apr_table_make(r->pool, 4);
2310 apr_table_setn(params, OIDC_PROTO_ACCESS_TOKEN, access_token);
2311 if (oidc_util_http_post_form(r, provider->userinfo_endpoint_url, params,
2312 NULL, NULL, provider->ssl_validate_server, response,
2313 cfg->http_timeout_long, cfg->outgoing_proxy,
2314 oidc_dir_cfg_pass_cookies(r), NULL, NULL) == FALSE)
2315 return FALSE;
2316 } else {
2317 oidc_error(r, "unsupported userinfo token presentation method: %d",
2318 provider->userinfo_token_method);
2319 return FALSE;
2320 }
2321
2322 json_t *claims = NULL;
2323 if (oidc_user_info_response_validate(r, cfg, provider, response, &claims,
2324 userinfo_jwt) == FALSE)
2325 return FALSE;
2326
2327 if (oidc_proto_resolve_composite_claims(r, cfg, claims) == TRUE)
2328 *response = oidc_util_encode_json_object(r, claims,
2329 JSON_PRESERVE_ORDER | JSON_COMPACT);
2330
2331 char *user_info_sub = NULL;
2332 oidc_jose_get_string(r->pool, claims, OIDC_CLAIM_SUB, FALSE, &user_info_sub,
2333 NULL);
2334
2335 oidc_debug(r, "id_token_sub=%s, user_info_sub=%s", id_token_sub,
2336 user_info_sub);
2337
2338 if ((user_info_sub == NULL)
2339 && (apr_table_get(r->subprocess_env, "OIDC_NO_USERINFO_SUB") == NULL)) {
2340 oidc_error(r,
2341 "mandatory claim (\"%s\") was not returned from userinfo endpoint (https://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse)",
2342 OIDC_CLAIM_SUB);
2343 json_decref(claims);
2344 return FALSE;
2345 }
2346
2347 if ((id_token_sub != NULL) && (user_info_sub != NULL)) {
2348 if (apr_strnatcmp(id_token_sub, user_info_sub) != 0) {
2349 oidc_error(r,
2350 "\"%s\" claim (\"%s\") returned from userinfo endpoint does not match the one in the id_token (\"%s\")",
2351 OIDC_CLAIM_SUB, user_info_sub, id_token_sub);
2352 json_decref(claims);
2353 return FALSE;
2354 }
2355 }
2356
2357 json_decref(claims);
2358
2359 return TRUE;
2360 }
2361
2362 /*
2363 * based on a resource perform OpenID Connect Provider Issuer Discovery to find out the issuer and obtain and store its metadata
2364 */
oidc_proto_webfinger_discovery(request_rec * r,oidc_cfg * cfg,const char * resource,const char * domain,char ** issuer)2365 static apr_byte_t oidc_proto_webfinger_discovery(request_rec *r, oidc_cfg *cfg,
2366 const char *resource, const char *domain, char **issuer) {
2367
2368 const char *url = apr_psprintf(r->pool, "https://%s/.well-known/webfinger",
2369 domain);
2370
2371 apr_table_t *params = apr_table_make(r->pool, 1);
2372 apr_table_setn(params, "resource", resource);
2373 apr_table_setn(params, "rel", "http://openid.net/specs/connect/1.0/issuer");
2374
2375 char *response = NULL;
2376 if (oidc_util_http_get(r, url, params, NULL, NULL,
2377 cfg->provider.ssl_validate_server, &response,
2378 cfg->http_timeout_short, cfg->outgoing_proxy,
2379 oidc_dir_cfg_pass_cookies(r), NULL, NULL) == FALSE) {
2380 /* errors will have been logged by now */
2381 return FALSE;
2382 }
2383
2384 /* decode and see if it is not an error response somehow */
2385 json_t *j_response = NULL;
2386 if (oidc_util_decode_json_and_check_error(r, response, &j_response) == FALSE)
2387 return FALSE;
2388
2389 /* get the links parameter */
2390 json_t *j_links = json_object_get(j_response, "links");
2391 if ((j_links == NULL) || (!json_is_array(j_links))) {
2392 oidc_error(r, "response JSON object did not contain a \"links\" array");
2393 json_decref(j_response);
2394 return FALSE;
2395 }
2396
2397 /* get the one-and-only object in the "links" array */
2398 json_t *j_object = json_array_get(j_links, 0);
2399 if ((j_object == NULL) || (!json_is_object(j_object))) {
2400 oidc_error(r,
2401 "response JSON object did not contain a JSON object as the first element in the \"links\" array");
2402 json_decref(j_response);
2403 return FALSE;
2404 }
2405
2406 /* get the href from that object, which is the issuer value */
2407 json_t *j_href = json_object_get(j_object, "href");
2408 if ((j_href == NULL) || (!json_is_string(j_href))) {
2409 oidc_error(r,
2410 "response JSON object did not contain a \"href\" element in the first \"links\" array object");
2411 json_decref(j_response);
2412 return FALSE;
2413 }
2414
2415 /* check that the link is on secure HTTPs */
2416 if (oidc_valid_url(r->pool, json_string_value(j_href), "https") != NULL) {
2417 oidc_error(r,
2418 "response JSON object contains an \"href\" value that is not a valid \"https\" URL: %s",
2419 json_string_value(j_href));
2420 json_decref(j_response);
2421 return FALSE;
2422 }
2423
2424 *issuer = apr_pstrdup(r->pool, json_string_value(j_href));
2425
2426 oidc_debug(r,
2427 "returning issuer \"%s\" for resource \"%s\" after doing successful webfinger-based discovery",
2428 *issuer, resource);
2429
2430 json_decref(j_response);
2431
2432 return TRUE;
2433 }
2434
2435 /*
2436 * based on an account name, perform OpenID Connect Provider Issuer Discovery to find out the issuer and obtain and store its metadata
2437 */
oidc_proto_account_based_discovery(request_rec * r,oidc_cfg * cfg,const char * acct,char ** issuer)2438 apr_byte_t oidc_proto_account_based_discovery(request_rec *r, oidc_cfg *cfg,
2439 const char *acct, char **issuer) {
2440
2441 // TODO: maybe show intermediate/progress screen "discovering..."
2442
2443 oidc_debug(r, "enter, acct=%s", acct);
2444
2445 const char *resource = apr_psprintf(r->pool, "acct:%s", acct);
2446 const char *domain = strrchr(acct, OIDC_CHAR_AT);
2447 if (domain == NULL) {
2448 oidc_error(r, "invalid account name");
2449 return FALSE;
2450 }
2451 domain++;
2452
2453 return oidc_proto_webfinger_discovery(r, cfg, resource, domain, issuer);
2454 }
2455
2456 /*
2457 * based on user identifier URL, perform OpenID Connect Provider Issuer Discovery to find out the issuer and obtain and store its metadata
2458 */
oidc_proto_url_based_discovery(request_rec * r,oidc_cfg * cfg,const char * url,char ** issuer)2459 apr_byte_t oidc_proto_url_based_discovery(request_rec *r, oidc_cfg *cfg,
2460 const char *url, char **issuer) {
2461
2462 oidc_debug(r, "enter, url=%s", url);
2463
2464 apr_uri_t uri;
2465 apr_uri_parse(r->pool, url, &uri);
2466
2467 char *domain = uri.hostname;
2468 if (uri.port_str != NULL)
2469 domain = apr_psprintf(r->pool, "%s:%s", domain, uri.port_str);
2470
2471 return oidc_proto_webfinger_discovery(r, cfg, url, domain, issuer);
2472 }
2473
oidc_proto_javascript_implicit(request_rec * r,oidc_cfg * c)2474 int oidc_proto_javascript_implicit(request_rec *r, oidc_cfg *c) {
2475
2476 oidc_debug(r, "enter");
2477
2478 const char *java_script =
2479 " <script type=\"text/javascript\">\n"
2480 " function postOnLoad() {\n"
2481 " encoded = location.hash.substring(1).split('&');\n"
2482 " for (i = 0; i < encoded.length; i++) {\n"
2483 " encoded[i].replace(/\\+/g, ' ');\n"
2484 " var n = encoded[i].indexOf('=');\n"
2485 " var input = document.createElement('input');\n"
2486 " input.type = 'hidden';\n"
2487 " input.name = decodeURIComponent(encoded[i].substring(0, n));\n"
2488 " input.value = decodeURIComponent(encoded[i].substring(n+1));\n"
2489 " document.forms[0].appendChild(input);\n"
2490 " }\n"
2491 " document.forms[0].action = window.location.href.substr(0, window.location.href.indexOf('#'));\n"
2492 " document.forms[0].submit();\n"
2493 " }\n"
2494 " </script>\n";
2495
2496 const char *html_body =
2497 " <p>Submitting...</p>\n"
2498 " <form method=\"post\" action=\"\">\n"
2499 " <p>\n"
2500 " <input type=\"hidden\" name=\"" OIDC_PROTO_RESPONSE_MODE "\" value=\"" OIDC_PROTO_RESPONSE_MODE_FRAGMENT "\">\n"
2501 " </p>\n"
2502 " </form>\n";
2503
2504 return oidc_util_html_send(r, "Submitting...", java_script, "postOnLoad",
2505 html_body, OK);
2506 }
2507
2508 /*
2509 * check a provided hash value (at_hash|c_hash) against a corresponding hash calculated for a specified value and algorithm
2510 */
oidc_proto_validate_hash(request_rec * r,const char * alg,const char * hash,const char * value,const char * type)2511 static apr_byte_t oidc_proto_validate_hash(request_rec *r, const char *alg,
2512 const char *hash, const char *value, const char *type) {
2513
2514 char *calc = NULL;
2515 unsigned int calc_len = 0;
2516 unsigned int hash_len = oidc_jose_hash_length(alg) / 2;
2517 oidc_jose_error_t err;
2518
2519 /* hash the provided access_token */
2520 if (oidc_jose_hash_string(r->pool, alg, value, &calc, &calc_len,
2521 &err) == FALSE) {
2522 oidc_error(r, "oidc_jose_hash_string failed: %s",
2523 oidc_jose_e2s(r->pool, err));
2524 return FALSE;
2525 }
2526
2527 /* calculate the base64url-encoded value of the hash */
2528 char *decoded = NULL;
2529 unsigned int decoded_len = oidc_base64url_decode(r->pool, &decoded, hash);
2530 if (decoded_len <= 0) {
2531 oidc_error(r, "oidc_base64url_decode returned an error");
2532 return FALSE;
2533 }
2534
2535 oidc_debug(r, "hash_len=%d, decoded_len=%d, calc_len=%d", hash_len,
2536 decoded_len, calc_len);
2537
2538 /* compare the calculated hash against the provided hash */
2539 if ((decoded_len < hash_len) || (calc_len < hash_len)
2540 || (memcmp(decoded, calc, hash_len) != 0)) {
2541 oidc_error(r,
2542 "provided \"%s\" hash value (%s) does not match the calculated value",
2543 type, hash);
2544 return FALSE;
2545 }
2546
2547 oidc_debug(r,
2548 "successfully validated the provided \"%s\" hash value (%s) against the calculated value",
2549 type, hash);
2550
2551 return TRUE;
2552 }
2553
2554 /*
2555 * check a hash value in the id_token against the corresponding hash calculated over a provided value
2556 */
oidc_proto_validate_hash_value(request_rec * r,oidc_provider_t * provider,oidc_jwt_t * jwt,const char * response_type,const char * value,const char * key,apr_array_header_t * required_for_flows)2557 static apr_byte_t oidc_proto_validate_hash_value(request_rec *r,
2558 oidc_provider_t *provider, oidc_jwt_t *jwt, const char *response_type,
2559 const char *value, const char *key,
2560 apr_array_header_t *required_for_flows) {
2561
2562 /*
2563 * get the hash value from the id_token
2564 */
2565 char *hash = NULL;
2566 oidc_jose_get_string(r->pool, jwt->payload.value.json, key, FALSE, &hash,
2567 NULL);
2568
2569 /*
2570 * check if the hash was present
2571 */
2572 if (hash == NULL) {
2573
2574 /* no hash..., now see if the flow required it */
2575 int i;
2576 for (i = 0; i < required_for_flows->nelts; i++) {
2577 if (oidc_util_spaced_string_equals(r->pool, response_type,
2578 ((const char**) required_for_flows->elts)[i])) {
2579 oidc_warn(r, "flow is \"%s\", but no %s found in id_token",
2580 response_type, key);
2581 return FALSE;
2582 }
2583 }
2584
2585 /* no hash but it was not required anyway */
2586 return TRUE;
2587 }
2588
2589 /*
2590 * we have a hash, validate it and return the result
2591 */
2592 return oidc_proto_validate_hash(r, jwt->header.alg, hash, value, key);
2593 }
2594
2595 /*
2596 * check the c_hash value in the id_token against the code
2597 */
oidc_proto_validate_code(request_rec * r,oidc_provider_t * provider,oidc_jwt_t * jwt,const char * response_type,const char * code)2598 apr_byte_t oidc_proto_validate_code(request_rec *r, oidc_provider_t *provider,
2599 oidc_jwt_t *jwt, const char *response_type, const char *code) {
2600 apr_array_header_t *required_for_flows = apr_array_make(r->pool, 2,
2601 sizeof(const char*));
2602 *(const char**) apr_array_push(required_for_flows) =
2603 OIDC_PROTO_RESPONSE_TYPE_CODE_IDTOKEN;
2604 *(const char**) apr_array_push(required_for_flows) =
2605 OIDC_PROTO_RESPONSE_TYPE_CODE_IDTOKEN_TOKEN;
2606 if (oidc_proto_validate_hash_value(r, provider, jwt, response_type, code,
2607 OIDC_CLAIM_C_HASH, required_for_flows) == FALSE) {
2608 oidc_error(r, "could not validate code against \"%s\" claim value",
2609 OIDC_CLAIM_C_HASH);
2610 return FALSE;
2611 }
2612 return TRUE;
2613 }
2614
2615 /*
2616 * check the at_hash value in the id_token against the access_token
2617 */
oidc_proto_validate_access_token(request_rec * r,oidc_provider_t * provider,oidc_jwt_t * jwt,const char * response_type,const char * access_token)2618 apr_byte_t oidc_proto_validate_access_token(request_rec *r,
2619 oidc_provider_t *provider, oidc_jwt_t *jwt, const char *response_type,
2620 const char *access_token) {
2621 apr_array_header_t *required_for_flows = apr_array_make(r->pool, 2,
2622 sizeof(const char*));
2623 *(const char**) apr_array_push(required_for_flows) =
2624 OIDC_PROTO_RESPONSE_TYPE_IDTOKEN_TOKEN;
2625 *(const char**) apr_array_push(required_for_flows) =
2626 OIDC_PROTO_RESPONSE_TYPE_CODE_IDTOKEN_TOKEN;
2627 if (oidc_proto_validate_hash_value(r, provider, jwt, response_type,
2628 access_token, OIDC_CLAIM_AT_HASH, required_for_flows) == FALSE) {
2629 oidc_error(r,
2630 "could not validate access token against \"%s\" claim value",
2631 OIDC_CLAIM_AT_HASH);
2632 return FALSE;
2633 }
2634 return TRUE;
2635 }
2636
2637 /*
2638 * return the supported flows
2639 */
oidc_proto_supported_flows(apr_pool_t * pool)2640 apr_array_header_t* oidc_proto_supported_flows(apr_pool_t *pool) {
2641 apr_array_header_t *result = apr_array_make(pool, 6, sizeof(const char*));
2642 *(const char**) apr_array_push(result) = OIDC_PROTO_RESPONSE_TYPE_CODE;
2643 *(const char**) apr_array_push(result) =
2644 OIDC_PROTO_RESPONSE_TYPE_IDTOKEN;
2645 *(const char**) apr_array_push(result) =
2646 OIDC_PROTO_RESPONSE_TYPE_IDTOKEN_TOKEN;
2647 *(const char**) apr_array_push(result) =
2648 OIDC_PROTO_RESPONSE_TYPE_CODE_IDTOKEN;
2649 *(const char**) apr_array_push(result) =
2650 OIDC_PROTO_RESPONSE_TYPE_CODE_TOKEN;
2651 *(const char**) apr_array_push(result) =
2652 OIDC_PROTO_RESPONSE_TYPE_CODE_IDTOKEN_TOKEN;
2653 return result;
2654 }
2655
2656 /*
2657 * check if a particular OpenID Connect flow is supported
2658 */
oidc_proto_flow_is_supported(apr_pool_t * pool,const char * flow)2659 apr_byte_t oidc_proto_flow_is_supported(apr_pool_t *pool, const char *flow) {
2660 apr_array_header_t *flows = oidc_proto_supported_flows(pool);
2661 int i;
2662 for (i = 0; i < flows->nelts; i++) {
2663 if (oidc_util_spaced_string_equals(pool, flow,
2664 ((const char**) flows->elts)[i]))
2665 return TRUE;
2666 }
2667 return FALSE;
2668 }
2669
2670 /*
2671 * check the required parameters for the various flows after resolving the authorization code
2672 */
oidc_proto_validate_code_response(request_rec * r,const char * response_type,char * id_token,char * access_token,char * token_type)2673 static apr_byte_t oidc_proto_validate_code_response(request_rec *r,
2674 const char *response_type, char *id_token, char *access_token,
2675 char *token_type) {
2676
2677 oidc_debug(r, "enter");
2678
2679 /*
2680 * check id_token parameter
2681 */
2682 if (!oidc_util_spaced_string_contains(r->pool, response_type,
2683 OIDC_PROTO_RESPONSE_TYPE_IDTOKEN)) {
2684 if (id_token == NULL) {
2685 oidc_error(r,
2686 "requested flow is \"%s\" but no \"%s\" parameter found in the code response",
2687 response_type, OIDC_PROTO_ID_TOKEN);
2688 return FALSE;
2689 }
2690 } else {
2691 if (id_token != NULL) {
2692 oidc_warn(r,
2693 "requested flow is \"%s\" but there is an \"%s\" parameter in the code response that will be dropped",
2694 response_type, OIDC_PROTO_ID_TOKEN);
2695 }
2696 }
2697
2698 /*
2699 * check access_token parameter
2700 */
2701 if (!oidc_util_spaced_string_contains(r->pool, response_type,
2702 OIDC_PROTO_RESPONSE_TYPE_TOKEN)) {
2703 if (access_token == NULL) {
2704 oidc_error(r,
2705 "requested flow is \"%s\" but no \"%s\" parameter found in the code response",
2706 response_type, OIDC_PROTO_ACCESS_TOKEN);
2707 return FALSE;
2708 }
2709 if (token_type == NULL) {
2710 oidc_error(r,
2711 "requested flow is \"%s\" but no \"%s\" parameter found in the code response",
2712 response_type, OIDC_PROTO_TOKEN_TYPE);
2713 return FALSE;
2714 }
2715 } else {
2716 if (access_token != NULL) {
2717 oidc_warn(r,
2718 "requested flow is \"%s\" but there is an \"%s\" parameter in the code response that will be dropped",
2719 response_type, OIDC_PROTO_ACCESS_TOKEN);
2720 }
2721
2722 if (token_type != NULL) {
2723 oidc_warn(r,
2724 "requested flow is \"%s\" but there is a \"%s\" parameter in the code response that will be dropped",
2725 response_type, OIDC_PROTO_TOKEN_TYPE);
2726 }
2727 }
2728
2729 return TRUE;
2730 }
2731
2732 /*
2733 * validate the response parameters provided by the OP against the requested response type
2734 */
oidc_proto_validate_response_type(request_rec * r,const char * requested_response_type,const char * code,const char * id_token,const char * access_token)2735 static apr_byte_t oidc_proto_validate_response_type(request_rec *r,
2736 const char *requested_response_type, const char *code,
2737 const char *id_token, const char *access_token) {
2738
2739 if (oidc_util_spaced_string_contains(r->pool, requested_response_type,
2740 OIDC_PROTO_RESPONSE_TYPE_CODE)) {
2741 if (code == NULL) {
2742 oidc_error(r,
2743 "the requested response type was (%s) but the response does not contain a \"%s\" parameter",
2744 requested_response_type, OIDC_PROTO_CODE);
2745 return FALSE;
2746 }
2747 } else if (code != NULL) {
2748 oidc_error(r,
2749 "the requested response type was (%s) but the response contains a \"%s\" parameter",
2750 requested_response_type, OIDC_PROTO_CODE);
2751 return FALSE;
2752 }
2753
2754 if (oidc_util_spaced_string_contains(r->pool, requested_response_type,
2755 OIDC_PROTO_RESPONSE_TYPE_IDTOKEN)) {
2756 if (id_token == NULL) {
2757 oidc_error(r,
2758 "the requested response type was (%s) but the response does not contain an \"%s\" parameter",
2759 requested_response_type, OIDC_PROTO_ID_TOKEN);
2760 return FALSE;
2761 }
2762 } else if (id_token != NULL) {
2763 oidc_error(r,
2764 "the requested response type was (%s) but the response contains an \"%s\" parameter",
2765 requested_response_type, OIDC_PROTO_ID_TOKEN);
2766 return FALSE;
2767 }
2768
2769 if (oidc_util_spaced_string_contains(r->pool, requested_response_type,
2770 OIDC_PROTO_RESPONSE_TYPE_TOKEN)) {
2771 if (access_token == NULL) {
2772 oidc_error(r,
2773 "the requested response type was (%s) but the response does not contain an \"%s\" parameter",
2774 requested_response_type, OIDC_PROTO_ACCESS_TOKEN);
2775 return FALSE;
2776 }
2777 } else if (access_token != NULL) {
2778 oidc_error(r,
2779 "the requested response type was (%s) but the response contains an \"%s\" parameter",
2780 requested_response_type, OIDC_PROTO_ACCESS_TOKEN);
2781 return FALSE;
2782 }
2783
2784 return TRUE;
2785 }
2786
2787 /*
2788 * validate the response mode used by the OP against the requested response mode
2789 */
oidc_proto_validate_response_mode(request_rec * r,oidc_proto_state_t * proto_state,const char * response_mode,const char * default_response_mode)2790 static apr_byte_t oidc_proto_validate_response_mode(request_rec *r,
2791 oidc_proto_state_t *proto_state, const char *response_mode,
2792 const char *default_response_mode) {
2793
2794 const char *requested_response_mode = oidc_proto_state_get_response_mode(
2795 proto_state);
2796 if (requested_response_mode == NULL)
2797 requested_response_mode = default_response_mode;
2798
2799 if (apr_strnatcmp(requested_response_mode, response_mode) != 0) {
2800 oidc_error(r,
2801 "requested response mode (%s) does not match the response mode used by the OP (%s)",
2802 requested_response_mode, response_mode);
2803 return FALSE;
2804 }
2805
2806 return TRUE;
2807 }
2808
2809 /*
2810 * validate the client_id/iss provided by the OP against the client_id/iss registered with the provider that the request was sent to
2811 */
oidc_proto_validate_issuer_client_id(request_rec * r,const char * configured_issuer,const char * response_issuer,const char * configured_client_id,const char * response_client_id)2812 static apr_byte_t oidc_proto_validate_issuer_client_id(request_rec *r,
2813 const char *configured_issuer, const char *response_issuer,
2814 const char *configured_client_id, const char *response_client_id) {
2815
2816 if (response_issuer != NULL) {
2817 if (apr_strnatcmp(configured_issuer, response_issuer) != 0) {
2818 oidc_error(r,
2819 "configured issuer (%s) does not match the issuer provided in the response by the OP (%s)",
2820 configured_issuer, response_issuer);
2821 return FALSE;
2822 }
2823 }
2824
2825 if (response_client_id != NULL) {
2826 if (apr_strnatcmp(configured_client_id, response_client_id) != 0) {
2827 oidc_error(r,
2828 "configured client_id (%s) does not match the client_id provided in the response by the OP (%s)",
2829 configured_client_id, response_client_id);
2830 return FALSE;
2831 }
2832 }
2833
2834 oidc_debug(r, "iss and/or client_id matched OK: %s, %s, %s, %s",
2835 response_issuer, configured_issuer, response_client_id,
2836 configured_client_id);
2837
2838 return TRUE;
2839 }
2840
2841 /*
2842 * helper function to validate both the response type and the response mode in a single function call
2843 */
oidc_proto_validate_response_type_mode_issuer(request_rec * r,const char * requested_response_type,apr_table_t * params,oidc_proto_state_t * proto_state,const char * response_mode,const char * default_response_mode,const char * issuer,const char * c_client_id)2844 static apr_byte_t oidc_proto_validate_response_type_mode_issuer(request_rec *r,
2845 const char *requested_response_type, apr_table_t *params,
2846 oidc_proto_state_t *proto_state, const char *response_mode,
2847 const char *default_response_mode, const char *issuer,
2848 const char *c_client_id) {
2849
2850 const char *code = apr_table_get(params, OIDC_PROTO_CODE);
2851 const char *id_token = apr_table_get(params, OIDC_PROTO_ID_TOKEN);
2852 const char *access_token = apr_table_get(params,
2853 OIDC_PROTO_ACCESS_TOKEN);
2854 const char *iss = apr_table_get(params, OIDC_PROTO_ISS);
2855 const char *client_id = apr_table_get(params, OIDC_PROTO_CLIENT_ID);
2856
2857 if (oidc_proto_validate_issuer_client_id(r, issuer, iss, c_client_id,
2858 client_id) == FALSE)
2859 return FALSE;
2860
2861 if (oidc_proto_validate_response_type(r, requested_response_type, code,
2862 id_token, access_token) == FALSE)
2863 return FALSE;
2864
2865 if (oidc_proto_validate_response_mode(r, proto_state, response_mode,
2866 default_response_mode) == FALSE)
2867 return FALSE;
2868
2869 return TRUE;
2870 }
2871
2872 /*
2873 * parse and id_token and check the c_hash if the code is provided
2874 */
oidc_proto_parse_idtoken_and_validate_code(request_rec * r,oidc_cfg * c,oidc_proto_state_t * proto_state,oidc_provider_t * provider,const char * response_type,apr_table_t * params,oidc_jwt_t ** jwt,apr_byte_t must_validate_code)2875 static apr_byte_t oidc_proto_parse_idtoken_and_validate_code(request_rec *r,
2876 oidc_cfg *c, oidc_proto_state_t *proto_state, oidc_provider_t *provider,
2877 const char *response_type, apr_table_t *params, oidc_jwt_t **jwt,
2878 apr_byte_t must_validate_code) {
2879
2880 const char *code = apr_table_get(params, OIDC_PROTO_CODE);
2881 const char *id_token = apr_table_get(params, OIDC_PROTO_ID_TOKEN);
2882
2883 apr_byte_t is_code_flow = (oidc_util_spaced_string_contains(r->pool,
2884 response_type, OIDC_PROTO_RESPONSE_TYPE_CODE) == TRUE)
2885 && (oidc_util_spaced_string_contains(r->pool, response_type,
2886 OIDC_PROTO_RESPONSE_TYPE_IDTOKEN) == FALSE);
2887
2888 const char *nonce = oidc_proto_state_get_nonce(proto_state);
2889 if (oidc_proto_parse_idtoken(r, c, provider, id_token, nonce, jwt,
2890 is_code_flow) == FALSE)
2891 return FALSE;
2892
2893 if ((must_validate_code == TRUE)
2894 && (oidc_proto_validate_code(r, provider, *jwt, response_type, code)
2895 == FALSE))
2896 return FALSE;
2897
2898 return TRUE;
2899 }
2900
2901 /*
2902 * resolve the code against the token endpoint and validate the response that is returned by the OP
2903 */
oidc_proto_resolve_code_and_validate_response(request_rec * r,oidc_cfg * c,oidc_provider_t * provider,const char * response_type,apr_table_t * params,oidc_proto_state_t * proto_state)2904 static apr_byte_t oidc_proto_resolve_code_and_validate_response(request_rec *r,
2905 oidc_cfg *c, oidc_provider_t *provider, const char *response_type,
2906 apr_table_t *params, oidc_proto_state_t *proto_state) {
2907
2908 char *id_token = NULL;
2909 char *access_token = NULL;
2910 char *token_type = NULL;
2911 int expires_in = -1;
2912 char *refresh_token = NULL;
2913 char *code_verifier = NULL;
2914
2915 if (provider->pkce != NULL)
2916 provider->pkce->verifier(r,
2917 oidc_proto_state_get_pkce_state(proto_state), &code_verifier);
2918
2919 const char *state = oidc_proto_state_get_state(proto_state);
2920
2921 if (oidc_proto_resolve_code(r, c, provider,
2922 apr_table_get(params, OIDC_PROTO_CODE), code_verifier, &id_token,
2923 &access_token, &token_type, &expires_in, &refresh_token,
2924 state) == FALSE) {
2925 oidc_error(r, "failed to resolve the code");
2926 return FALSE;
2927 }
2928
2929 if (oidc_proto_validate_code_response(r, response_type, id_token,
2930 access_token, token_type) == FALSE) {
2931 oidc_error(r, "code response validation failed");
2932 return FALSE;
2933 }
2934
2935 /* don't override parameters that may already have been (rightfully) set in the authorization response */
2936 if ((apr_table_get(params, OIDC_PROTO_ID_TOKEN) == NULL)
2937 && (id_token != NULL)) {
2938 apr_table_set(params, OIDC_PROTO_ID_TOKEN, id_token);
2939 }
2940
2941 if ((apr_table_get(params, OIDC_PROTO_ACCESS_TOKEN) == NULL)
2942 && (access_token != NULL)) {
2943 apr_table_set(params, OIDC_PROTO_ACCESS_TOKEN, access_token);
2944 if (token_type != NULL)
2945 apr_table_set(params, OIDC_PROTO_TOKEN_TYPE, token_type);
2946 if (expires_in != -1)
2947 apr_table_setn(params, OIDC_PROTO_EXPIRES_IN,
2948 apr_psprintf(r->pool, "%d", expires_in));
2949 }
2950
2951 /* refresh token should not have been set before */
2952 if (refresh_token != NULL) {
2953 apr_table_set(params, OIDC_PROTO_REFRESH_TOKEN, refresh_token);
2954 }
2955
2956 return TRUE;
2957 }
2958
2959 /*
2960 * handle the "code id_token" response type
2961 */
oidc_proto_authorization_response_code_idtoken(request_rec * r,oidc_cfg * c,oidc_proto_state_t * proto_state,oidc_provider_t * provider,apr_table_t * params,const char * response_mode,oidc_jwt_t ** jwt)2962 apr_byte_t oidc_proto_authorization_response_code_idtoken(request_rec *r,
2963 oidc_cfg *c, oidc_proto_state_t *proto_state, oidc_provider_t *provider,
2964 apr_table_t *params, const char *response_mode, oidc_jwt_t **jwt) {
2965
2966 oidc_debug(r, "enter");
2967
2968 static const char *response_type = OIDC_PROTO_RESPONSE_TYPE_CODE_IDTOKEN;
2969
2970 if (oidc_proto_validate_response_type_mode_issuer(r, response_type, params,
2971 proto_state, response_mode,
2972 OIDC_PROTO_RESPONSE_MODE_FRAGMENT, provider->issuer,
2973 provider->client_id) == FALSE)
2974 return FALSE;
2975
2976 if (oidc_proto_parse_idtoken_and_validate_code(r, c, proto_state, provider,
2977 response_type, params, jwt, TRUE) == FALSE)
2978 return FALSE;
2979
2980 /* clear parameters that should only be set from the token endpoint */
2981 apr_table_unset(params, OIDC_PROTO_ACCESS_TOKEN);
2982 apr_table_unset(params, OIDC_PROTO_TOKEN_TYPE);
2983 apr_table_unset(params, OIDC_PROTO_EXPIRES_IN);
2984 apr_table_unset(params, OIDC_PROTO_REFRESH_TOKEN);
2985
2986 if (oidc_proto_resolve_code_and_validate_response(r, c, provider,
2987 response_type, params, proto_state) == FALSE)
2988 return FALSE;
2989
2990 return TRUE;
2991 }
2992
2993 /*
2994 * handle the "code token" response type
2995 */
oidc_proto_handle_authorization_response_code_token(request_rec * r,oidc_cfg * c,oidc_proto_state_t * proto_state,oidc_provider_t * provider,apr_table_t * params,const char * response_mode,oidc_jwt_t ** jwt)2996 apr_byte_t oidc_proto_handle_authorization_response_code_token(request_rec *r,
2997 oidc_cfg *c, oidc_proto_state_t *proto_state, oidc_provider_t *provider,
2998 apr_table_t *params, const char *response_mode, oidc_jwt_t **jwt) {
2999
3000 oidc_debug(r, "enter");
3001
3002 static const char *response_type = OIDC_PROTO_RESPONSE_TYPE_CODE_TOKEN;
3003
3004 if (oidc_proto_validate_response_type_mode_issuer(r, response_type, params,
3005 proto_state, response_mode,
3006 OIDC_PROTO_RESPONSE_MODE_FRAGMENT, provider->issuer,
3007 provider->client_id) == FALSE)
3008 return FALSE;
3009
3010 /* clear parameters that should only be set from the token endpoint */
3011 apr_table_unset(params, OIDC_PROTO_ID_TOKEN);
3012 apr_table_unset(params, OIDC_PROTO_REFRESH_TOKEN);
3013
3014 if (oidc_proto_resolve_code_and_validate_response(r, c, provider,
3015 response_type, params, proto_state) == FALSE)
3016 return FALSE;
3017
3018 if (oidc_proto_parse_idtoken_and_validate_code(r, c, proto_state, provider,
3019 response_type, params, jwt, FALSE) == FALSE)
3020 return FALSE;
3021
3022 return TRUE;
3023 }
3024
3025 /*
3026 * handle the "code" response type
3027 */
oidc_proto_handle_authorization_response_code(request_rec * r,oidc_cfg * c,oidc_proto_state_t * proto_state,oidc_provider_t * provider,apr_table_t * params,const char * response_mode,oidc_jwt_t ** jwt)3028 apr_byte_t oidc_proto_handle_authorization_response_code(request_rec *r,
3029 oidc_cfg *c, oidc_proto_state_t *proto_state, oidc_provider_t *provider,
3030 apr_table_t *params, const char *response_mode, oidc_jwt_t **jwt) {
3031
3032 oidc_debug(r, "enter");
3033
3034 static const char *response_type = OIDC_PROTO_RESPONSE_TYPE_CODE;
3035
3036 if (oidc_proto_validate_response_type_mode_issuer(r, response_type, params,
3037 proto_state, response_mode,
3038 OIDC_PROTO_RESPONSE_MODE_QUERY, provider->issuer,
3039 provider->client_id) == FALSE)
3040 return FALSE;
3041
3042 /* clear parameters that should only be set from the token endpoint */
3043 apr_table_unset(params, OIDC_PROTO_ACCESS_TOKEN);
3044 apr_table_unset(params, OIDC_PROTO_TOKEN_TYPE);
3045 apr_table_unset(params, OIDC_PROTO_EXPIRES_IN);
3046 apr_table_unset(params, OIDC_PROTO_ID_TOKEN);
3047 apr_table_unset(params, OIDC_PROTO_REFRESH_TOKEN);
3048
3049 if (oidc_proto_resolve_code_and_validate_response(r, c, provider,
3050 response_type, params, proto_state) == FALSE)
3051 return FALSE;
3052
3053 /*
3054 * in this flow it is actually optional to check the code token against the c_hash
3055 */
3056 if (oidc_proto_parse_idtoken_and_validate_code(r, c, proto_state, provider,
3057 response_type, params, jwt, TRUE) == FALSE)
3058 return FALSE;
3059
3060 /*
3061 * in this flow it is actually optional to check the access token against the at_hash
3062 */
3063 if ((apr_table_get(params, OIDC_PROTO_ACCESS_TOKEN) != NULL)
3064 && (oidc_proto_validate_access_token(r, provider, *jwt,
3065 response_type,
3066 apr_table_get(params, OIDC_PROTO_ACCESS_TOKEN)) == FALSE))
3067 return FALSE;
3068
3069 return TRUE;
3070 }
3071
3072 /*
3073 * helper function for implicit flows: shared code for "id_token token" and "id_token"
3074 */
oidc_proto_handle_implicit_flow(request_rec * r,oidc_cfg * c,const char * response_type,oidc_proto_state_t * proto_state,oidc_provider_t * provider,apr_table_t * params,const char * response_mode,oidc_jwt_t ** jwt)3075 static apr_byte_t oidc_proto_handle_implicit_flow(request_rec *r, oidc_cfg *c,
3076 const char *response_type, oidc_proto_state_t *proto_state,
3077 oidc_provider_t *provider, apr_table_t *params,
3078 const char *response_mode, oidc_jwt_t **jwt) {
3079
3080 if (oidc_proto_validate_response_type_mode_issuer(r, response_type, params,
3081 proto_state, response_mode,
3082 OIDC_PROTO_RESPONSE_MODE_FRAGMENT, provider->issuer,
3083 provider->client_id) == FALSE)
3084 return FALSE;
3085
3086 if (oidc_proto_parse_idtoken_and_validate_code(r, c, proto_state, provider,
3087 response_type, params, jwt, TRUE) == FALSE)
3088 return FALSE;
3089
3090 return TRUE;
3091 }
3092
3093 /*
3094 * handle the "code id_token token" response type
3095 */
oidc_proto_authorization_response_code_idtoken_token(request_rec * r,oidc_cfg * c,oidc_proto_state_t * proto_state,oidc_provider_t * provider,apr_table_t * params,const char * response_mode,oidc_jwt_t ** jwt)3096 apr_byte_t oidc_proto_authorization_response_code_idtoken_token(request_rec *r,
3097 oidc_cfg *c, oidc_proto_state_t *proto_state, oidc_provider_t *provider,
3098 apr_table_t *params, const char *response_mode, oidc_jwt_t **jwt) {
3099
3100 oidc_debug(r, "enter");
3101
3102 static const char *response_type =
3103 OIDC_PROTO_RESPONSE_TYPE_CODE_IDTOKEN_TOKEN;
3104
3105 if (oidc_proto_handle_implicit_flow(r, c, response_type, proto_state,
3106 provider, params, response_mode, jwt) == FALSE)
3107 return FALSE;
3108
3109 if (oidc_proto_validate_access_token(r, provider, *jwt, response_type,
3110 apr_table_get(params, OIDC_PROTO_ACCESS_TOKEN)) == FALSE)
3111 return FALSE;
3112
3113 /* clear parameters that should only be set from the token endpoint */
3114 apr_table_unset(params, OIDC_PROTO_REFRESH_TOKEN);
3115
3116 if (oidc_proto_resolve_code_and_validate_response(r, c, provider,
3117 response_type, params, proto_state) == FALSE)
3118 return FALSE;
3119
3120 return TRUE;
3121 }
3122
3123 /*
3124 * handle the "id_token token" response type
3125 */
oidc_proto_handle_authorization_response_idtoken_token(request_rec * r,oidc_cfg * c,oidc_proto_state_t * proto_state,oidc_provider_t * provider,apr_table_t * params,const char * response_mode,oidc_jwt_t ** jwt)3126 apr_byte_t oidc_proto_handle_authorization_response_idtoken_token(
3127 request_rec *r, oidc_cfg *c, oidc_proto_state_t *proto_state,
3128 oidc_provider_t *provider, apr_table_t *params,
3129 const char *response_mode, oidc_jwt_t **jwt) {
3130
3131 oidc_debug(r, "enter");
3132
3133 static const char *response_type =
3134 OIDC_PROTO_RESPONSE_TYPE_IDTOKEN_TOKEN;
3135
3136 if (oidc_proto_handle_implicit_flow(r, c, response_type, proto_state,
3137 provider, params, response_mode, jwt) == FALSE)
3138 return FALSE;
3139
3140 if (oidc_proto_validate_access_token(r, provider, *jwt, response_type,
3141 apr_table_get(params, OIDC_PROTO_ACCESS_TOKEN)) == FALSE)
3142 return FALSE;
3143
3144 /* clear parameters that should not be part of this flow */
3145 apr_table_unset(params, OIDC_PROTO_REFRESH_TOKEN);
3146
3147 return TRUE;
3148 }
3149
3150 /*
3151 * handle the "id_token" response type
3152 */
oidc_proto_handle_authorization_response_idtoken(request_rec * r,oidc_cfg * c,oidc_proto_state_t * proto_state,oidc_provider_t * provider,apr_table_t * params,const char * response_mode,oidc_jwt_t ** jwt)3153 apr_byte_t oidc_proto_handle_authorization_response_idtoken(request_rec *r,
3154 oidc_cfg *c, oidc_proto_state_t *proto_state, oidc_provider_t *provider,
3155 apr_table_t *params, const char *response_mode, oidc_jwt_t **jwt) {
3156
3157 oidc_debug(r, "enter");
3158
3159 static const char *response_type = OIDC_PROTO_RESPONSE_TYPE_IDTOKEN;
3160
3161 if (oidc_proto_handle_implicit_flow(r, c, response_type, proto_state,
3162 provider, params, response_mode, jwt) == FALSE)
3163 return FALSE;
3164
3165 /* clear parameters that should not be part of this flow */
3166 apr_table_unset(params, OIDC_PROTO_TOKEN_TYPE);
3167 apr_table_unset(params, OIDC_PROTO_EXPIRES_IN);
3168 apr_table_unset(params, OIDC_PROTO_REFRESH_TOKEN);
3169
3170 return TRUE;
3171 }
3172