001/* 002 * oauth2-oidc-sdk 003 * 004 * Copyright 2012-2020, Connect2id Ltd and contributors. 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 007 * this file except in compliance with the License. You may obtain a copy of the 008 * License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software distributed 013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the 015 * specific language governing permissions and limitations under the License. 016 */ 017 018package com.nimbusds.openid.connect.sdk; 019 020 021import java.util.*; 022 023import net.jcip.annotations.Immutable; 024import net.minidev.json.JSONArray; 025import net.minidev.json.JSONAware; 026import net.minidev.json.JSONObject; 027 028import com.nimbusds.oauth2.sdk.ParseException; 029import com.nimbusds.oauth2.sdk.ResponseType; 030import com.nimbusds.oauth2.sdk.Scope; 031import com.nimbusds.oauth2.sdk.util.CollectionUtils; 032import com.nimbusds.oauth2.sdk.util.JSONArrayUtils; 033import com.nimbusds.oauth2.sdk.util.JSONObjectUtils; 034import com.nimbusds.openid.connect.sdk.assurance.request.VerifiedClaimsSetRequest; 035import com.nimbusds.openid.connect.sdk.claims.ClaimRequirement; 036import com.nimbusds.openid.connect.sdk.claims.ClaimsSetRequest; 037 038 039/** 040 * Specifies individual OpenID claims to return from the UserInfo endpoint and 041 * / or in the ID Token. Replaces the deprecated {@link ClaimsRequest} class. 042 * 043 * <p>Example: 044 * 045 * <pre> 046 * { 047 * "userinfo": 048 * { 049 * "given_name": {"essential": true}, 050 * "nickname": null, 051 * "email": {"essential": true}, 052 * "email_verified": {"essential": true}, 053 * "picture": null, 054 * "http://example.info/claims/groups": null 055 * }, 056 * "id_token": 057 * { 058 * "auth_time": {"essential": true}, 059 * "acr": {"values": ["urn:mace:incommon:iap:silver"] } 060 * } 061 * } 062 * </pre> 063 * 064 * <p>Related specifications: 065 * 066 * <ul> 067 * <li>OpenID Connect Core 1.0 068 * <li>OpenID Connect for Identity Assurance 1.0 069 * </ul> 070 */ 071@Immutable 072public class OIDCClaimsRequest implements JSONAware { 073 074 075 /** 076 * Claims requested in the ID token, {@code null} if not specified. 077 */ 078 private final ClaimsSetRequest idToken; 079 080 081 /** 082 * Claims requested at the UserInfo endpoint, {@code null} if not 083 * specified. 084 */ 085 private final ClaimsSetRequest userInfo; 086 087 088 /** 089 * Verified claims requested in the ID token, empty list if none. 090 */ 091 private final List<VerifiedClaimsSetRequest> idTokenVerified; 092 093 094 /** 095 * Verified claims requested at the UserInfo endpoint, empty list if 096 * none. 097 */ 098 private final List<VerifiedClaimsSetRequest> userInfoVerified; 099 100 101 /** 102 * Creates a new empty OpenID claims request. 103 */ 104 public OIDCClaimsRequest() { 105 this(null, null, Collections.<VerifiedClaimsSetRequest>emptyList(), Collections.<VerifiedClaimsSetRequest>emptyList()); 106 } 107 108 109 private OIDCClaimsRequest(final ClaimsSetRequest idToken, 110 final ClaimsSetRequest userInfo, 111 final List<VerifiedClaimsSetRequest> idTokenVerified, 112 final List<VerifiedClaimsSetRequest> userInfoVerified) { 113 114 this.idToken = idToken; 115 this.userInfo = userInfo; 116 this.idTokenVerified = Collections.unmodifiableList(idTokenVerified); 117 this.userInfoVerified = Collections.unmodifiableList(userInfoVerified); 118 } 119 120 121 /** 122 * Adds the entries from the specified other OpenID claims request. 123 * 124 * @param other The other OpenID claims request. If {@code null} no 125 * claims request entries will be added to this claims 126 * request. 127 * 128 * @return The updated OpenID claims request. 129 */ 130 public OIDCClaimsRequest add(final OIDCClaimsRequest other) { 131 132 if (other == null) 133 return this; 134 135 // Regular id_token 136 Collection<ClaimsSetRequest.Entry> idTokenEntries = new LinkedList<>(); 137 if (idToken != null) { 138 idTokenEntries.addAll(idToken.getEntries()); 139 } 140 if (other.getIDTokenClaimsRequest() != null) { 141 idTokenEntries.addAll(other.getIDTokenClaimsRequest().getEntries()); 142 } 143 144 // Regular userinfo 145 Collection<ClaimsSetRequest.Entry> userInfoEntries = new LinkedList<>(); 146 if (userInfo != null) { 147 userInfoEntries.addAll(userInfo.getEntries()); 148 } 149 if (other.getUserInfoClaimsRequest() != null) { 150 userInfoEntries.addAll(other.getUserInfoClaimsRequest().getEntries()); 151 } 152 153 // Verified id_token 154 List<VerifiedClaimsSetRequest> idTokenVerifiedList = new LinkedList<>(idTokenVerified); 155 idTokenVerifiedList.addAll(other.getIDTokenVerifiedClaimsRequests()); 156 157 // Verified userinfo 158 List<VerifiedClaimsSetRequest> userInfoVerifiedList = new LinkedList<>(userInfoVerified); 159 userInfoVerifiedList.addAll(other.getUserInfoVerifiedClaimsRequests()); 160 161 return new OIDCClaimsRequest( 162 idTokenEntries.isEmpty() ? null : new ClaimsSetRequest(idTokenEntries), 163 userInfoEntries.isEmpty() ? null : new ClaimsSetRequest(userInfoEntries), 164 idTokenVerifiedList, 165 userInfoVerifiedList 166 ); 167 } 168 169 170 /** 171 * Returns the claims requested in the ID token. 172 * 173 * @return The ID token claims request, {@code null} if not specified. 174 */ 175 public ClaimsSetRequest getIDTokenClaimsRequest() { 176 return idToken; 177 } 178 179 180 /** 181 * Sets the claims requested in the ID token. 182 * 183 * @param idToken The ID token claims request, {@code null} if not 184 * specified. 185 * 186 * @return The updated OpenID claims request. 187 */ 188 public OIDCClaimsRequest withIDTokenClaimsRequest(final ClaimsSetRequest idToken) { 189 return new OIDCClaimsRequest( 190 idToken, 191 getUserInfoClaimsRequest(), 192 getIDTokenVerifiedClaimsRequests(), 193 getUserInfoVerifiedClaimsRequests()); 194 } 195 196 197 /** 198 * Returns the claims requested at the UserInfo endpoint. 199 * 200 * @return The UserInfo claims request, {@code null} if not specified. 201 */ 202 public ClaimsSetRequest getUserInfoClaimsRequest() { 203 return userInfo; 204 } 205 206 207 /** 208 * Sets the claims requested at the UserInfo endpoint. 209 * 210 * @param userInfo The UserInfo claims request, {@code null} if not 211 * specified. 212 * 213 * @return The updated OpenID claims request. 214 */ 215 public OIDCClaimsRequest withUserInfoClaimsRequest(final ClaimsSetRequest userInfo) { 216 return new OIDCClaimsRequest( 217 getIDTokenClaimsRequest(), 218 userInfo, 219 getIDTokenVerifiedClaimsRequests(), 220 getUserInfoVerifiedClaimsRequests()); 221 } 222 223 224 private static List<VerifiedClaimsSetRequest> toCurrent(final List<com.nimbusds.openid.connect.sdk.assurance.claims.VerifiedClaimsSetRequest> list) { 225 226 if (CollectionUtils.isEmpty(list)) { 227 return Collections.emptyList(); 228 } 229 230 List<VerifiedClaimsSetRequest> out = new LinkedList<>(); 231 for (com.nimbusds.openid.connect.sdk.assurance.claims.VerifiedClaimsSetRequest r: list) { 232 if (r == null) { 233 continue; 234 } 235 try { 236 out.add(VerifiedClaimsSetRequest.parse(r.toJSONObject())); 237 } catch (ParseException e) { 238 // should never happen 239 } 240 } 241 return out; 242 } 243 244 245 private static List<com.nimbusds.openid.connect.sdk.assurance.claims.VerifiedClaimsSetRequest> toDeprecated(final List<VerifiedClaimsSetRequest> list) { 246 247 if (CollectionUtils.isEmpty(list)) { 248 return Collections.emptyList(); 249 } 250 251 List<com.nimbusds.openid.connect.sdk.assurance.claims.VerifiedClaimsSetRequest> out = new LinkedList<>(); 252 for (VerifiedClaimsSetRequest r: list) { 253 if (r == null) { 254 continue; 255 } 256 try { 257 out.add(com.nimbusds.openid.connect.sdk.assurance.claims.VerifiedClaimsSetRequest.parse(r.toJSONObject())); 258 } catch (ParseException e) { 259 // should never happen 260 } 261 } 262 return out; 263 } 264 265 266 /** 267 * Returns the list of verified claims sets requested in the ID token. 268 * 269 * @return The ID token verified claims request list, empty list if not 270 * specified. 271 */ 272 public List<VerifiedClaimsSetRequest> getIDTokenVerifiedClaimsRequests() { 273 return idTokenVerified; 274 } 275 276 277 /** 278 * Returns the list of verified claims sets requested in the ID token. 279 * 280 * @return The ID token verified claims request list, empty list if not 281 * specified. 282 */ 283 @Deprecated 284 public List<com.nimbusds.openid.connect.sdk.assurance.claims.VerifiedClaimsSetRequest> getIDTokenVerifiedClaimsRequestList() { 285 return toDeprecated(idTokenVerified); 286 } 287 288 289 /** 290 * Sets the list of verified claims sets requested in the ID token. 291 * 292 * @param idTokenVerifiedList One or more ID token verified claims 293 * requests, empty list if not specified. 294 * 295 * @return The updated OpenID claims request. 296 */ 297 public OIDCClaimsRequest withIDTokenVerifiedClaimsRequests(final List<VerifiedClaimsSetRequest> idTokenVerifiedList) { 298 return new OIDCClaimsRequest( 299 getIDTokenClaimsRequest(), 300 getUserInfoClaimsRequest(), 301 idTokenVerifiedList != null ? idTokenVerifiedList : Collections.<VerifiedClaimsSetRequest>emptyList(), 302 getUserInfoVerifiedClaimsRequests()); 303 } 304 305 306 /** 307 * Sets the list of verified claims sets requested in the ID token. 308 * 309 * @param idTokenVerifiedList One or more ID token verified claims 310 * requests, empty list if not specified. 311 * 312 * @return The updated OpenID claims request. 313 */ 314 @Deprecated 315 public OIDCClaimsRequest withIDTokenVerifiedClaimsRequestList(final List<com.nimbusds.openid.connect.sdk.assurance.claims.VerifiedClaimsSetRequest> idTokenVerifiedList) { 316 return new OIDCClaimsRequest( 317 getIDTokenClaimsRequest(), 318 getUserInfoClaimsRequest(), 319 idTokenVerifiedList != null ? toCurrent(idTokenVerifiedList) : Collections.<VerifiedClaimsSetRequest>emptyList(), 320 getUserInfoVerifiedClaimsRequests()); 321 } 322 323 324 /** 325 * Sets a single verified claims set requested in the ID token. 326 * 327 * @param idTokenVerified The ID token verified claims request, 328 * {@code null} if not specified. 329 * 330 * @return The updated OpenID claims request. 331 */ 332 public OIDCClaimsRequest withIDTokenVerifiedClaimsRequest(final VerifiedClaimsSetRequest idTokenVerified) { 333 return new OIDCClaimsRequest( 334 getIDTokenClaimsRequest(), 335 getUserInfoClaimsRequest(), 336 idTokenVerified != null ? Collections.singletonList(idTokenVerified) : Collections.<VerifiedClaimsSetRequest>emptyList(), 337 getUserInfoVerifiedClaimsRequests()); 338 } 339 340 341 /** 342 * Sets a single verified claims set requested in the ID token. 343 * 344 * @param idTokenVerified The ID token verified claims request, 345 * {@code null} if not specified. 346 * 347 * @return The updated OpenID claims request. 348 */ 349 @Deprecated 350 public OIDCClaimsRequest withIDTokenVerifiedClaimsRequest(final com.nimbusds.openid.connect.sdk.assurance.claims.VerifiedClaimsSetRequest idTokenVerified) { 351 return new OIDCClaimsRequest( 352 getIDTokenClaimsRequest(), 353 getUserInfoClaimsRequest(), 354 idTokenVerified != null ? toCurrent(Collections.singletonList(idTokenVerified)) : Collections.<VerifiedClaimsSetRequest>emptyList(), 355 getUserInfoVerifiedClaimsRequests()); 356 } 357 358 359 /** 360 * Returns the list of verified claims sets requested at the UserInfo 361 * endpoint. 362 * 363 * @return The UserInfo verified claims request list, empty list if not 364 * specified. 365 */ 366 public List<VerifiedClaimsSetRequest> getUserInfoVerifiedClaimsRequests() { 367 return userInfoVerified; 368 } 369 370 371 /** 372 * Returns the list of verified claims sets requested at the UserInfo 373 * endpoint. 374 * 375 * @return The UserInfo verified claims request list, empty list if not 376 * specified. 377 */ 378 @Deprecated 379 public List<com.nimbusds.openid.connect.sdk.assurance.claims.VerifiedClaimsSetRequest> getUserInfoVerifiedClaimsRequestList() { 380 return toDeprecated(userInfoVerified); 381 } 382 383 384 /** 385 * Sets the list of verified claims sets requested at the UserInfo 386 * endpoint. 387 * 388 * @param userInfoVerifiedList One or more UserInfo verified claims 389 * requests, empty list if not specified. 390 * 391 * @return The updated OpenID claims request. 392 */ 393 public OIDCClaimsRequest withUserInfoVerifiedClaimsRequests(final List<VerifiedClaimsSetRequest> userInfoVerifiedList) { 394 return new OIDCClaimsRequest( 395 getIDTokenClaimsRequest(), 396 getUserInfoClaimsRequest(), 397 getIDTokenVerifiedClaimsRequests(), 398 userInfoVerifiedList != null ? userInfoVerifiedList : Collections.<VerifiedClaimsSetRequest>emptyList()); 399 } 400 401 402 /** 403 * Sets the list of verified claims sets requested at the UserInfo 404 * endpoint. 405 * 406 * @param userInfoVerifiedList One or more UserInfo verified claims 407 * requests, empty list if not specified. 408 * 409 * @return The updated OpenID claims request. 410 */ 411 @Deprecated 412 public OIDCClaimsRequest withUserInfoVerifiedClaimsRequestList(final List<com.nimbusds.openid.connect.sdk.assurance.claims.VerifiedClaimsSetRequest> userInfoVerifiedList) { 413 return new OIDCClaimsRequest( 414 getIDTokenClaimsRequest(), 415 getUserInfoClaimsRequest(), 416 getIDTokenVerifiedClaimsRequests(), 417 userInfoVerifiedList != null ? toCurrent(userInfoVerifiedList) : Collections.<VerifiedClaimsSetRequest>emptyList()); 418 } 419 420 421 /** 422 * Sets a single verified claims set requested at the UserInfo 423 * endpoint. 424 * 425 * @param userInfoVerified The UserInfo verified claims request, 426 * {@code null} if not specified. 427 * 428 * @return The updated OpenID claims request. 429 */ 430 public OIDCClaimsRequest withUserInfoVerifiedClaimsRequest(final VerifiedClaimsSetRequest userInfoVerified) { 431 return new OIDCClaimsRequest( 432 getIDTokenClaimsRequest(), 433 getUserInfoClaimsRequest(), 434 getIDTokenVerifiedClaimsRequests(), 435 userInfoVerified != null ? Collections.singletonList(userInfoVerified) : Collections.<VerifiedClaimsSetRequest>emptyList()); 436 } 437 438 439 /** 440 * Sets a single verified claims set requested at the UserInfo 441 * endpoint. 442 * 443 * @param userInfoVerified The UserInfo verified claims request, 444 * {@code null} if not specified. 445 * 446 * @return The updated OpenID claims request. 447 */ 448 @Deprecated 449 public OIDCClaimsRequest withUserInfoVerifiedClaimsRequest(final com.nimbusds.openid.connect.sdk.assurance.claims.VerifiedClaimsSetRequest userInfoVerified) { 450 return new OIDCClaimsRequest( 451 getIDTokenClaimsRequest(), 452 getUserInfoClaimsRequest(), 453 getIDTokenVerifiedClaimsRequests(), 454 userInfoVerified != null ? toCurrent(Collections.singletonList(userInfoVerified)) : Collections.<VerifiedClaimsSetRequest>emptyList()); 455 } 456 457 458 private static JSONObject addVerified(final List<VerifiedClaimsSetRequest> verified, 459 final JSONObject containingJSONObject) { 460 461 if (verified != null) { 462 463 if (verified.size() == 1 && verified.get(0) != null) { 464 JSONObject out = new JSONObject(); 465 if (containingJSONObject != null) { 466 out.putAll(containingJSONObject); 467 } 468 out.put("verified_claims", verified.get(0).toJSONObject()); 469 return out; 470 } else if (verified.size() > 1) { 471 JSONObject out = new JSONObject(); 472 if (containingJSONObject != null) { 473 out.putAll(containingJSONObject); 474 } 475 JSONArray jsonArray = new JSONArray(); 476 for (VerifiedClaimsSetRequest verifiedClaims: verified) { 477 jsonArray.add(verifiedClaims.toJSONObject()); 478 } 479 out.put("verified_claims", jsonArray); 480 return out; 481 } 482 } 483 return containingJSONObject; 484 } 485 486 487 /** 488 * Returns the JSON object representation of this OpenID claims 489 * request. 490 * 491 * <p>Example: 492 * 493 * <pre> 494 * { 495 * "userinfo": 496 * { 497 * "given_name": {"essential": true}, 498 * "nickname": null, 499 * "email": {"essential": true}, 500 * "email_verified": {"essential": true}, 501 * "picture": null, 502 * "http://example.info/claims/groups": null 503 * }, 504 * "id_token": 505 * { 506 * "auth_time": {"essential": true}, 507 * "acr": {"values": ["urn:mace:incommon:iap:silver"] } 508 * } 509 * } 510 * </pre> 511 * 512 * @return The JSON object, empty if no ID token and UserInfo claims 513 * are specified. 514 */ 515 public JSONObject toJSONObject() { 516 517 JSONObject o = new JSONObject(); 518 519 // id_token 520 JSONObject idTokenJSONObject = null; 521 if (idToken != null) { 522 idTokenJSONObject = idToken.toJSONObject(); 523 } 524 idTokenJSONObject = addVerified(idTokenVerified, idTokenJSONObject); 525 if (idTokenJSONObject != null && ! idTokenJSONObject.isEmpty()) { 526 o.put("id_token", idTokenJSONObject); 527 } 528 529 // userinfo 530 JSONObject userInfoJSONObject = null; 531 if (userInfo != null) { 532 userInfoJSONObject = userInfo.toJSONObject(); 533 } 534 userInfoJSONObject = addVerified(userInfoVerified, userInfoJSONObject); 535 if (userInfoJSONObject != null && ! userInfoJSONObject.isEmpty()) { 536 o.put("userinfo", userInfoJSONObject); 537 } 538 539 return o; 540 } 541 542 543 @Override 544 public String toJSONString() { 545 return toJSONObject().toJSONString(); 546 } 547 548 549 @Override 550 public String toString() { 551 552 return toJSONString(); 553 } 554 555 556 /** 557 * Resolves the OpenID claims request for the specified response type 558 * and scope. The scope values that are {@link OIDCScopeValue standard 559 * OpenID scope values} are resolved to their respective individual 560 * claims requests, any other scope values are ignored. 561 * 562 * @param responseType The response type. Must not be {@code null}. 563 * @param scope The scope, {@code null} if not specified (for a 564 * plain OAuth 2.0 authorisation request with no 565 * scope explicitly specified). 566 * 567 * @return The OpenID claims request. 568 */ 569 public static OIDCClaimsRequest resolve(final ResponseType responseType, final Scope scope) { 570 571 return resolve(responseType, scope, Collections.<Scope.Value, Set<String>>emptyMap()); 572 } 573 574 575 /** 576 * Resolves the OpenID claims request for the specified response type 577 * and scope. The scope values that are {@link OIDCScopeValue standard 578 * OpenID scope values} are resolved to their respective individual 579 * claims requests, any other scope values are checked in the specified 580 * custom claims map and resolved accordingly. 581 * 582 * @param responseType The response type. Must not be {@code null}. 583 * @param scope The scope, {@code null} if not specified (for a 584 * plain OAuth 2.0 authorisation request with no 585 * scope explicitly specified). 586 * @param customClaims Custom scope value to set of claim names map, 587 * {@code null} if not specified. 588 * 589 * @return The OpenID claims request. 590 */ 591 public static OIDCClaimsRequest resolve(final ResponseType responseType, 592 final Scope scope, 593 final Map<Scope.Value, Set<String>> customClaims) { 594 595 OIDCClaimsRequest claimsRequest = new OIDCClaimsRequest(); 596 597 if (scope == null) { 598 // Plain OAuth 2.0 mode 599 return claimsRequest; 600 } 601 602 List<ClaimsSetRequest.Entry> entries = new LinkedList<>(); 603 for (Scope.Value value : scope) { 604 605 if (value.equals(OIDCScopeValue.PROFILE)) { 606 607 entries.addAll(OIDCScopeValue.PROFILE.toClaimsSetRequestEntries()); 608 609 } else if (value.equals(OIDCScopeValue.EMAIL)) { 610 611 entries.addAll(OIDCScopeValue.EMAIL.toClaimsSetRequestEntries()); 612 613 } else if (value.equals(OIDCScopeValue.PHONE)) { 614 615 entries.addAll(OIDCScopeValue.PHONE.toClaimsSetRequestEntries()); 616 617 } else if (value.equals(OIDCScopeValue.ADDRESS)) { 618 619 entries.addAll(OIDCScopeValue.ADDRESS.toClaimsSetRequestEntries()); 620 621 } else if (customClaims != null && customClaims.containsKey(value)) { 622 623 // Process custom scope value -> claim names expansion, e.g. 624 // "corp_profile" -> ["employeeNumber", "dept", "ext"] 625 Set<String> claimNames = customClaims.get(value); 626 627 if (claimNames == null || claimNames.isEmpty()) { 628 continue; // skip 629 } 630 631 for (String claimName : claimNames) { 632 entries.add(new ClaimsSetRequest.Entry(claimName).withClaimRequirement(ClaimRequirement.VOLUNTARY)); 633 } 634 635 } 636 } 637 638 if (entries.isEmpty()) { 639 return claimsRequest; 640 } 641 642 ClaimsSetRequest claimsSetRequest = new ClaimsSetRequest(entries); 643 644 // Determine the claims target (ID token or UserInfo) 645 final boolean switchToIDToken = 646 responseType.contains(OIDCResponseTypeValue.ID_TOKEN) && 647 !responseType.contains(ResponseType.Value.CODE) && 648 !responseType.contains(ResponseType.Value.TOKEN); 649 650 if (switchToIDToken) { 651 return claimsRequest.withIDTokenClaimsRequest(claimsSetRequest); 652 } else { 653 return claimsRequest.withUserInfoClaimsRequest(claimsSetRequest); 654 } 655 } 656 657 658 /** 659 * Resolves the merged OpenID claims request from the specified OpenID 660 * authentication request parameters. The scope values that are {@link 661 * OIDCScopeValue standard OpenID scope values} are resolved to their 662 * respective individual claims requests, any other scope values are 663 * ignored. 664 * 665 * @param responseType The response type. Must not be {@code null}. 666 * @param scope The scope, {@code null} if not specified (for a 667 * plain OAuth 2.0 authorisation request with no 668 * scope explicitly specified). 669 * @param claimsRequest The OpenID claims request, corresponding to the 670 * optional {@code claims} OpenID authentication 671 * request parameter, {@code null} if not 672 * specified. 673 * 674 * @return The merged OpenID claims request. 675 */ 676 public static OIDCClaimsRequest resolve(final ResponseType responseType, 677 final Scope scope, 678 final OIDCClaimsRequest claimsRequest) { 679 680 return resolve(responseType, scope, claimsRequest, Collections.<Scope.Value, Set<String>>emptyMap()); 681 } 682 683 684 /** 685 * Resolves the merged OpenID claims request from the specified OpenID 686 * authentication request parameters. The scope values that are {@link 687 * OIDCScopeValue standard OpenID scope values} are resolved to their 688 * respective individual claims requests, any other scope values are 689 * checked in the specified custom claims map and resolved accordingly. 690 * 691 * @param responseType The response type. Must not be {@code null}. 692 * @param scope The scope, {@code null} if not specified (for a 693 * plain OAuth 2.0 authorisation request with no 694 * scope explicitly specified). 695 * @param claimsRequest The OpenID claims request, corresponding to the 696 * optional {@code claims} OpenID authentication 697 * request parameter, {@code null} if not 698 * specified. 699 * @param customClaims Custom scope value to set of claim names map, 700 * {@code null} if not specified. 701 * 702 * @return The merged OpenID claims request. 703 */ 704 public static OIDCClaimsRequest resolve(final ResponseType responseType, 705 final Scope scope, 706 final OIDCClaimsRequest claimsRequest, 707 final Map<Scope.Value, Set<String>> customClaims) { 708 709 return resolve(responseType, scope, customClaims).add(claimsRequest); 710 } 711 712 713 /** 714 * Resolves the merged OpenID claims request for the specified OpenID 715 * authentication request. The scope values that are {@link 716 * OIDCScopeValue standard OpenID scope values} are resolved to their 717 * respective individual claims requests, any other scope values are 718 * ignored. 719 * 720 * @param authRequest The OpenID authentication request. Must not be 721 * {@code null}. 722 * 723 * @return The merged OpenID claims request. 724 */ 725 public static OIDCClaimsRequest resolve(final AuthenticationRequest authRequest) { 726 727 return resolve(authRequest.getResponseType(), authRequest.getScope(), authRequest.getOIDCClaims()); 728 } 729 730 731 private static VerifiedClaimsSetRequest parseVerifiedClaimsSetRequest(final JSONObject jsonObject, 732 final int position) 733 throws ParseException { 734 735 try { 736 return VerifiedClaimsSetRequest.parse(jsonObject); 737 } catch (ParseException e) { 738 throw new ParseException("Invalid verified claims request" + 739 (position > -1 ? " at position " + position : "") + 740 ": " + e.getMessage(), 741 e); 742 } 743 } 744 745 746 private static List<VerifiedClaimsSetRequest> parseVerified(final JSONObject containingJSONObject) 747 throws ParseException { 748 749 if (! containingJSONObject.containsKey("verified_claims")) { 750 // No verified claims 751 return Collections.emptyList(); 752 } 753 754 if (containingJSONObject.get("verified_claims") instanceof JSONObject) { 755 // Single verified claims element 756 JSONObject vo = JSONObjectUtils.getJSONObject(containingJSONObject, "verified_claims"); 757 return Collections.singletonList(parseVerifiedClaimsSetRequest(vo, -1)); 758 759 } else { 760 // Array of one or more verified claims elements 761 JSONArray va = JSONObjectUtils.getJSONArray(containingJSONObject, "verified_claims"); 762 List<VerifiedClaimsSetRequest> out = new LinkedList<>(); 763 int pos = 0; 764 for (JSONObject vo: JSONArrayUtils.toJSONObjectList(va)) { 765 out.add(parseVerifiedClaimsSetRequest(vo, pos++)); 766 } 767 return out; 768 } 769 } 770 771 772 /** 773 * Parses an OpenID claims request from the specified JSON object 774 * representation. 775 * 776 * <p>Example: 777 * 778 * <pre> 779 * { 780 * "userinfo": 781 * { 782 * "given_name": {"essential": true}, 783 * "nickname": null, 784 * "email": {"essential": true}, 785 * "email_verified": {"essential": true}, 786 * "picture": null, 787 * "http://example.info/claims/groups": null 788 * }, 789 * "id_token": 790 * { 791 * "auth_time": {"essential": true}, 792 * "acr": {"values": ["urn:mace:incommon:iap:silver"] } 793 * } 794 * } 795 * </pre> 796 * 797 * @param jsonObject The JSON object to parse. Must not be 798 * {@code null}. 799 * 800 * @return The OpenID claims request. 801 * 802 * @throws ParseException If parsing failed. 803 */ 804 public static OIDCClaimsRequest parse(final JSONObject jsonObject) 805 throws ParseException { 806 807 OIDCClaimsRequest claimsRequest = new OIDCClaimsRequest(); 808 809 JSONObject idTokenObject = JSONObjectUtils.getJSONObject(jsonObject, "id_token", null); 810 811 if (idTokenObject != null) { 812 ClaimsSetRequest csr = ClaimsSetRequest.parse(idTokenObject); 813 if (! csr.getEntries().isEmpty()) { 814 claimsRequest = claimsRequest.withIDTokenClaimsRequest(csr); 815 } 816 claimsRequest = claimsRequest.withIDTokenVerifiedClaimsRequests(parseVerified(idTokenObject)); 817 } 818 819 JSONObject userInfoObject = JSONObjectUtils.getJSONObject(jsonObject, "userinfo", null); 820 821 if (userInfoObject != null) { 822 ClaimsSetRequest csr = ClaimsSetRequest.parse(userInfoObject); 823 if (! csr.getEntries().isEmpty()) { 824 claimsRequest = claimsRequest.withUserInfoClaimsRequest(ClaimsSetRequest.parse(userInfoObject)); 825 } 826 claimsRequest = claimsRequest.withUserInfoVerifiedClaimsRequests(parseVerified(userInfoObject)); 827 } 828 829 return claimsRequest; 830 } 831 832 833 /** 834 * Parses an OpenID claims request from the specified JSON object 835 * string representation. 836 * 837 * <p>Example: 838 * 839 * <pre> 840 * { 841 * "userinfo": 842 * { 843 * "given_name": {"essential": true}, 844 * "nickname": null, 845 * "email": {"essential": true}, 846 * "email_verified": {"essential": true}, 847 * "picture": null, 848 * "http://example.info/claims/groups": null 849 * }, 850 * "id_token": 851 * { 852 * "auth_time": {"essential": true}, 853 * "acr": {"values": ["urn:mace:incommon:iap:silver"] } 854 * } 855 * } 856 * </pre> 857 * 858 * @param json The JSON object string to parse. Must not be 859 * {@code null}. 860 * 861 * @return The OpenID claims request. 862 * 863 * @throws ParseException If parsing failed. 864 */ 865 public static OIDCClaimsRequest parse(final String json) 866 throws ParseException { 867 868 JSONObject jsonObject; 869 try { 870 jsonObject = JSONObjectUtils.parse(json); 871 } catch (ParseException e) { 872 throw new ParseException("Invalid JSON"); 873 } 874 return parse(jsonObject); 875 } 876}