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