001/* 002 * oauth2-oidc-sdk 003 * 004 * Copyright 2012-2016, 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.net.URI; 022import java.util.*; 023 024import net.jcip.annotations.Immutable; 025 026import com.nimbusds.jwt.JWT; 027import com.nimbusds.jwt.JWTClaimsSet; 028import com.nimbusds.jwt.JWTParser; 029import com.nimbusds.langtag.LangTag; 030import com.nimbusds.langtag.LangTagException; 031import com.nimbusds.langtag.LangTagUtils; 032import com.nimbusds.oauth2.sdk.*; 033import com.nimbusds.oauth2.sdk.dpop.JWKThumbprintConfirmation; 034import com.nimbusds.oauth2.sdk.http.HTTPRequest; 035import com.nimbusds.oauth2.sdk.id.ClientID; 036import com.nimbusds.oauth2.sdk.id.State; 037import com.nimbusds.oauth2.sdk.pkce.CodeChallenge; 038import com.nimbusds.oauth2.sdk.pkce.CodeChallengeMethod; 039import com.nimbusds.oauth2.sdk.pkce.CodeVerifier; 040import com.nimbusds.oauth2.sdk.util.*; 041import com.nimbusds.openid.connect.sdk.claims.ACR; 042import com.nimbusds.openid.connect.sdk.federation.trust.TrustChain; 043 044 045/** 046 * OpenID Connect authentication request. Intended to authenticate an end-user 047 * and request the end-user's authorisation to release information to the 048 * client. Supports custom request parameters. 049 * 050 * <p>Example HTTP request (code flow): 051 * 052 * <pre> 053 * https://server.example.com/op/authorize? 054 * response_type=code%20id_token 055 * &client_id=s6BhdRkqt3 056 * &redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb 057 * &scope=openid 058 * &nonce=n-0S6_WzA2Mj 059 * &state=af0ifjsldkj 060 * </pre> 061 * 062 * <p>Related specifications: 063 * 064 * <ul> 065 * <li>OpenID Connect Core 1.0, section 3.1.2.1. 066 * <li>Proof Key for Code Exchange by OAuth Public Clients (RFC 7636). 067 * <li>Resource Indicators for OAuth 2.0 (RFC 8707) 068 * <li>The OAuth 2.0 Authorization Framework: JWT Secured Authorization 069 * Request (JAR) (RFC 9101) 070 * <li>Financial-grade API: JWT Secured Authorization Response Mode for 071 * OAuth 2.0 (JARM) 072 * <li>OAuth 2.0 Demonstrating Proof-of-Possession at the Application Layer 073 * (DPoP) (draft-ietf-oauth-dpop-11). 074 * <li>OpenID Connect Federation 1.0, section 10.1. 075 * <li>OpenID Connect for Identity Assurance 1.0, section 8. 076 * </ul> 077 */ 078@Immutable 079public class AuthenticationRequest extends AuthorizationRequest { 080 081 082 /** 083 * The purpose string parameter minimal length. 084 */ 085 public static final int PURPOSE_MIN_LENGTH = 3; 086 087 088 /** 089 * The purpose string parameter maximum length. 090 */ 091 public static final int PURPOSE_MAX_LENGTH = 300; 092 093 094 /** 095 * The registered parameter names. 096 */ 097 private static final Set<String> REGISTERED_PARAMETER_NAMES; 098 099 100 static { 101 102 Set<String> p = new HashSet<>(AuthorizationRequest.getRegisteredParameterNames()); 103 104 p.add("nonce"); 105 p.add("display"); 106 p.add("max_age"); 107 p.add("ui_locales"); 108 p.add("claims_locales"); 109 p.add("id_token_hint"); 110 p.add("login_hint"); 111 p.add("acr_values"); 112 p.add("claims"); 113 p.add("purpose"); 114 115 REGISTERED_PARAMETER_NAMES = Collections.unmodifiableSet(p); 116 } 117 118 119 /** 120 * The nonce (required for implicit flow (unless in JAR), optional for 121 * code flow). 122 */ 123 private final Nonce nonce; 124 125 126 /** 127 * The requested display type (optional). 128 */ 129 private final Display display; 130 131 132 /** 133 * The required maximum authentication age, in seconds, -1 if not 134 * specified, zero implies prompt=login (optional). 135 */ 136 private final int maxAge; 137 138 139 /** 140 * The end-user's preferred languages and scripts for the user 141 * interface (optional). 142 */ 143 private final List<LangTag> uiLocales; 144 145 146 /** 147 * The end-user's preferred languages and scripts for claims being 148 * returned (optional). 149 */ 150 private final List<LangTag> claimsLocales; 151 152 153 /** 154 * Previously issued ID Token passed to the authorisation server as a 155 * hint about the end-user's current or past authenticated session with 156 * the client (optional). Should be present when {@code prompt=none} is 157 * used. 158 */ 159 private final JWT idTokenHint; 160 161 162 /** 163 * Hint to the authorisation server about the login identifier the 164 * end-user may use to log in (optional). 165 */ 166 private final String loginHint; 167 168 169 /** 170 * Requested Authentication Context Class Reference values (optional). 171 */ 172 private final List<ACR> acrValues; 173 174 175 /** 176 * Individual claims to be returned (optional). 177 */ 178 private final OIDCClaimsRequest claims; 179 180 181 /** 182 * The transaction specific purpose, for use in OpenID Connect Identity 183 * Assurance. 184 */ 185 private final String purpose; 186 187 188 /** 189 * Builder for constructing OpenID Connect authentication requests. 190 */ 191 public static class Builder { 192 193 194 /** 195 * The endpoint URI (optional). 196 */ 197 private URI uri; 198 199 200 /** 201 * The response type (required unless in JAR). 202 */ 203 private ResponseType rt; 204 205 206 /** 207 * The client identifier (required unless in JAR). 208 */ 209 private final ClientID clientID; 210 211 212 /** 213 * The redirection URI where the response will be sent 214 * (required unless in JAR). 215 */ 216 private URI redirectURI; 217 218 219 /** 220 * The scope (required unless in JAR). 221 */ 222 private Scope scope; 223 224 225 /** 226 * The opaque value to maintain state between the request and 227 * the callback (recommended). 228 */ 229 private State state; 230 231 232 /** 233 * The nonce (required for implicit flow (unless in JAR), 234 * optional for code flow). 235 */ 236 private Nonce nonce; 237 238 239 /** 240 * The requested display type (optional). 241 */ 242 private Display display; 243 244 245 /** 246 * The requested prompt (optional). 247 */ 248 private Prompt prompt; 249 250 251 /** 252 * The DPoP JWK SHA-256 thumbprint (optional). 253 */ 254 private JWKThumbprintConfirmation dpopJKT; 255 256 257 /** 258 * The OpenID Connect Federation 1.0 trust chain (optional). 259 */ 260 private TrustChain trustChain; 261 262 263 /** 264 * The required maximum authentication age, in seconds, -1 if 265 * not specified, zero implies prompt=login (optional). 266 */ 267 private int maxAge = -1; 268 269 270 /** 271 * The end-user's preferred languages and scripts for the user 272 * interface (optional). 273 */ 274 private List<LangTag> uiLocales; 275 276 277 /** 278 * The end-user's preferred languages and scripts for claims 279 * being returned (optional). 280 */ 281 private List<LangTag> claimsLocales; 282 283 284 /** 285 * Previously issued ID Token passed to the authorisation 286 * server as a hint about the end-user's current or past 287 * authenticated session with the client (optional). Should be 288 * present when {@code prompt=none} is used. 289 */ 290 private JWT idTokenHint; 291 292 293 /** 294 * Hint to the authorisation server about the login identifier 295 * the end-user may use to log in (optional). 296 */ 297 private String loginHint; 298 299 300 /** 301 * Requested Authentication Context Class Reference values 302 * (optional). 303 */ 304 private List<ACR> acrValues; 305 306 307 /** 308 * Individual claims to be returned (optional). 309 */ 310 private OIDCClaimsRequest claims; 311 312 313 /** 314 * The transaction specific purpose (optional). 315 */ 316 private String purpose; 317 318 319 /** 320 * Request object (optional). 321 */ 322 private JWT requestObject; 323 324 325 /** 326 * Request object URI (optional). 327 */ 328 private URI requestURI; 329 330 331 /** 332 * The response mode (optional). 333 */ 334 private ResponseMode rm; 335 336 337 /** 338 * The authorisation code challenge for PKCE (optional). 339 */ 340 private CodeChallenge codeChallenge; 341 342 343 /** 344 * The authorisation code challenge method for PKCE (optional). 345 */ 346 private CodeChallengeMethod codeChallengeMethod; 347 348 349 /** 350 * The resource URI(s) (optional). 351 */ 352 private List<URI> resources; 353 354 355 /** 356 * Indicates incremental authorisation (optional). 357 */ 358 private boolean includeGrantedScopes; 359 360 361 /** 362 * Custom parameters. 363 */ 364 private final Map<String,List<String>> customParams = new HashMap<>(); 365 366 367 /** 368 * Creates a new OpenID Connect authentication request builder. 369 * 370 * @param rt The response type. Corresponds to the 371 * {@code response_type} parameter. Must 372 * specify a valid OpenID Connect response 373 * type. Must not be {@code null}. 374 * @param scope The request scope. Corresponds to the 375 * {@code scope} parameter. Must contain an 376 * {@link OIDCScopeValue#OPENID openid 377 * value}. Must not be {@code null}. 378 * @param clientID The client identifier. Corresponds to the 379 * {@code client_id} parameter. Must not be 380 * {@code null}. 381 * @param redirectURI The redirection URI. Corresponds to the 382 * {@code redirect_uri} parameter. Must not 383 * be {@code null} unless set by means of 384 * the optional {@code request_object} / 385 * {@code request_uri} parameter. 386 */ 387 public Builder(final ResponseType rt, 388 final Scope scope, 389 final ClientID clientID, 390 final URI redirectURI) { 391 392 if (rt == null) 393 throw new IllegalArgumentException("The response type must not be null"); 394 395 OIDCResponseTypeValidator.validate(rt); 396 397 this.rt = rt; 398 399 if (scope == null) 400 throw new IllegalArgumentException("The scope must not be null"); 401 402 if (! scope.contains(OIDCScopeValue.OPENID)) 403 throw new IllegalArgumentException("The scope must include an \"openid\" value"); 404 405 this.scope = scope; 406 407 if (clientID == null) 408 throw new IllegalArgumentException("The client ID must not be null"); 409 410 this.clientID = clientID; 411 412 // Check presence at build time 413 this.redirectURI = redirectURI; 414 } 415 416 417 /** 418 * Creates a new JWT secured OpenID Connect authentication 419 * request (JAR) builder. 420 * 421 * @param requestObject The request object. Must not be 422 * {@code null}. 423 * @param clientID The client ID. Must not be 424 * {@code null}. 425 */ 426 public Builder(final JWT requestObject, final ClientID clientID) { 427 428 if (requestObject == null) 429 throw new IllegalArgumentException("The request object must not be null"); 430 431 this.requestObject = requestObject; 432 433 if (clientID == null) 434 throw new IllegalArgumentException("The client ID must not be null"); 435 436 this.clientID = clientID; 437 } 438 439 440 /** 441 * Creates a new JWT secured OpenID Connect authentication 442 * request (JAR) builder. 443 * 444 * @param requestURI The request object URI. Must not be 445 * {@code null}. 446 * @param clientID The client ID. Must not be {@code null}. 447 */ 448 public Builder(final URI requestURI, final ClientID clientID) { 449 450 if (requestURI == null) 451 throw new IllegalArgumentException("The request URI must not be null"); 452 453 this.requestURI = requestURI; 454 455 if (clientID == null) 456 throw new IllegalArgumentException("The client ID must not be null"); 457 458 this.clientID = clientID; 459 } 460 461 462 /** 463 * Creates a new OpenID Connect authentication request builder 464 * from the specified request. 465 * 466 * @param request The OpenID Connect authentication request. 467 * Must not be {@code null}. 468 */ 469 public Builder(final AuthenticationRequest request) { 470 uri = request.getEndpointURI(); 471 rt = request.getResponseType(); 472 clientID = request.getClientID(); 473 redirectURI = request.getRedirectionURI(); 474 scope = request.getScope(); 475 state = request.getState(); 476 nonce = request.getNonce(); 477 display = request.getDisplay(); 478 prompt = request.getPrompt(); 479 dpopJKT = request.getDPoPJWKThumbprintConfirmation(); 480 trustChain = request.getTrustChain(); 481 maxAge = request.getMaxAge(); 482 uiLocales = request.getUILocales(); 483 claimsLocales = request.getClaimsLocales(); 484 idTokenHint = request.getIDTokenHint(); 485 loginHint = request.getLoginHint(); 486 acrValues = request.getACRValues(); 487 claims = request.getOIDCClaims(); 488 purpose = request.getPurpose(); 489 requestObject = request.getRequestObject(); 490 requestURI = request.getRequestURI(); 491 rm = request.getResponseMode(); 492 codeChallenge = request.getCodeChallenge(); 493 codeChallengeMethod = request.getCodeChallengeMethod(); 494 resources = request.getResources(); 495 includeGrantedScopes = request.includeGrantedScopes(); 496 customParams.putAll(request.getCustomParameters()); 497 } 498 499 500 /** 501 * Sets the response type. Corresponds to the 502 * {@code response_type} parameter. 503 * 504 * @param rt The response type. Must not be {@code null}. 505 * 506 * @return This builder. 507 */ 508 public Builder responseType(final ResponseType rt) { 509 510 if (rt == null) 511 throw new IllegalArgumentException("The response type must not be null"); 512 513 this.rt = rt; 514 return this; 515 } 516 517 518 /** 519 * Sets the scope. Corresponds to the {@code scope} parameter. 520 * 521 * @param scope The scope. Must not be {@code null}. 522 * 523 * @return This builder. 524 */ 525 public Builder scope(final Scope scope) { 526 527 if (scope == null) 528 throw new IllegalArgumentException("The scope must not be null"); 529 530 if (! scope.contains(OIDCScopeValue.OPENID)) 531 throw new IllegalArgumentException("The scope must include an openid value"); 532 533 this.scope = scope; 534 return this; 535 } 536 537 538 /** 539 * Sets the redirection URI. Corresponds to the 540 * {@code redirection_uri} parameter. 541 * 542 * @param redirectURI The redirection URI. Must not be 543 * {@code null}. 544 * 545 * @return This builder. 546 */ 547 public Builder redirectionURI(final URI redirectURI) { 548 549 if (redirectURI == null) 550 throw new IllegalArgumentException("The redirection URI must not be null"); 551 552 this.redirectURI = redirectURI; 553 return this; 554 } 555 556 557 /** 558 * Sets the state. Corresponds to the recommended {@code state} 559 * parameter. 560 * 561 * @param state The state, {@code null} if not specified. 562 * 563 * @return This builder. 564 */ 565 public Builder state(final State state) { 566 this.state = state; 567 return this; 568 } 569 570 571 /** 572 * Sets the URI of the endpoint (HTTP or HTTPS) for which the 573 * request is intended. 574 * 575 * @param uri The endpoint URI, {@code null} if not specified. 576 * 577 * @return This builder. 578 */ 579 public Builder endpointURI(final URI uri) { 580 this.uri = uri; 581 return this; 582 } 583 584 585 /** 586 * Sets the nonce. Corresponds to the conditionally optional 587 * {@code nonce} parameter. 588 * 589 * @param nonce The nonce, {@code null} if not specified. 590 * 591 * @return This builder. 592 */ 593 public Builder nonce(final Nonce nonce) { 594 this.nonce = nonce; 595 return this; 596 } 597 598 599 /** 600 * Sets the requested display type. Corresponds to the optional 601 * {@code display} parameter. 602 * 603 * @param display The requested display type, {@code null} if 604 * not specified. 605 * 606 * @return This builder. 607 */ 608 public Builder display(final Display display) { 609 this.display = display; 610 return this; 611 } 612 613 614 /** 615 * Sets the requested prompt. Corresponds to the optional 616 * {@code prompt} parameter. 617 * 618 * @param prompt The requested prompt, {@code null} if not 619 * specified. 620 * 621 * @return This builder. 622 */ 623 public Builder prompt(final Prompt prompt) { 624 this.prompt = prompt; 625 return this; 626 } 627 628 629 /** 630 * Sets the DPoP JWK SHA-256 thumbprint. Corresponds to the 631 * optional {@code dpop_jkt} parameter. 632 * 633 * @param dpopJKT DPoP JWK SHA-256 thumbprint, {@code null} if 634 * not specified. 635 * 636 * @return This builder. 637 */ 638 public Builder dPoPJWKThumbprintConfirmation(final JWKThumbprintConfirmation dpopJKT) { 639 this.dpopJKT = dpopJKT; 640 return this; 641 } 642 643 644 /** 645 * Sets the OpenID Connect Federation 1.0 trust chain. 646 * Corresponds to the optional {@code trust_chain} parameter. 647 * 648 * @param trustChain The trust chain, {@code null} if not 649 * specified. 650 * 651 * @return This builder. 652 */ 653 public Builder trustChain(final TrustChain trustChain) { 654 this.trustChain = trustChain; 655 return this; 656 } 657 658 659 /** 660 * Sets the required maximum authentication age. Corresponds to 661 * the optional {@code max_age} parameter. 662 * 663 * @param maxAge The maximum authentication age, in seconds; 0 664 * if not specified. 665 * 666 * @return This builder. 667 */ 668 public Builder maxAge(final int maxAge) { 669 this.maxAge = maxAge; 670 return this; 671 } 672 673 674 /** 675 * Sets the end-user's preferred languages and scripts for the 676 * user interface, ordered by preference. Corresponds to the 677 * optional {@code ui_locales} parameter. 678 * 679 * @param uiLocales The preferred UI locales, {@code null} if 680 * not specified. 681 * 682 * @return This builder. 683 */ 684 public Builder uiLocales(final List<LangTag> uiLocales) { 685 this.uiLocales = uiLocales; 686 return this; 687 } 688 689 690 /** 691 * Sets the end-user's preferred languages and scripts for the 692 * claims being returned, ordered by preference. Corresponds to 693 * the optional {@code claims_locales} parameter. 694 * 695 * @param claimsLocales The preferred claims locales, 696 * {@code null} if not specified. 697 * 698 * @return This builder. 699 */ 700 public Builder claimsLocales(final List<LangTag> claimsLocales) { 701 this.claimsLocales = claimsLocales; 702 return this; 703 } 704 705 706 /** 707 * Sets the ID Token hint. Corresponds to the conditionally 708 * optional {@code id_token_hint} parameter. 709 * 710 * @param idTokenHint The ID Token hint, {@code null} if not 711 * specified. 712 * 713 * @return This builder. 714 */ 715 public Builder idTokenHint(final JWT idTokenHint) { 716 this.idTokenHint = idTokenHint; 717 return this; 718 } 719 720 721 /** 722 * Sets the login hint. Corresponds to the optional 723 * {@code login_hint} parameter. 724 * 725 * @param loginHint The login hint, {@code null} if not 726 * specified. 727 * 728 * @return This builder. 729 */ 730 public Builder loginHint(final String loginHint) { 731 this.loginHint = loginHint; 732 return this; 733 } 734 735 736 /** 737 * Sets the requested Authentication Context Class Reference 738 * values. Corresponds to the optional {@code acr_values} 739 * parameter. 740 * 741 * @param acrValues The requested ACR values, {@code null} if 742 * not specified. 743 * 744 * @return This builder. 745 */ 746 public Builder acrValues(final List<ACR> acrValues) { 747 this.acrValues = acrValues; 748 return this; 749 } 750 751 752 /** 753 * Sets the individual claims to be returned. Corresponds to 754 * the optional {@code claims} parameter. 755 * 756 * @see #claims(OIDCClaimsRequest) 757 * 758 * @param claims The individual claims to be returned, 759 * {@code null} if not specified. 760 * 761 * @return This builder. 762 */ 763 @Deprecated 764 public Builder claims(final ClaimsRequest claims) { 765 766 if (claims == null) { 767 this.claims = null; 768 } else { 769 try { 770 this.claims = OIDCClaimsRequest.parse(claims.toJSONObject()); 771 } catch (ParseException e) { 772 // Should never happen 773 throw new IllegalArgumentException("Invalid claims: " + e.getMessage(), e); 774 } 775 } 776 return this; 777 } 778 779 780 /** 781 * Sets the individual OpenID claims to be returned. 782 * Corresponds to the optional {@code claims} parameter. 783 * 784 * @param claims The individual OpenID claims to be returned, 785 * {@code null} if not specified. 786 * 787 * @return This builder. 788 */ 789 public Builder claims(final OIDCClaimsRequest claims) { 790 this.claims = claims; 791 return this; 792 } 793 794 795 /** 796 * Sets the transaction specific purpose. Corresponds to the 797 * optional {@code purpose} parameter. 798 * 799 * @param purpose The purpose, {@code null} if not specified. 800 * 801 * @return This builder. 802 */ 803 public Builder purpose(final String purpose) { 804 this.purpose = purpose; 805 return this; 806 } 807 808 809 /** 810 * Sets the request object. Corresponds to the optional 811 * {@code request} parameter. Must not be specified together 812 * with a request object URI. 813 * 814 * @param requestObject The request object, {@code null} if not 815 * specified. 816 * 817 * @return This builder. 818 */ 819 public Builder requestObject(final JWT requestObject) { 820 this.requestObject = requestObject; 821 return this; 822 } 823 824 825 /** 826 * Sets the request object URI. Corresponds to the optional 827 * {@code request_uri} parameter. Must not be specified 828 * together with a request object. 829 * 830 * @param requestURI The request object URI, {@code null} if 831 * not specified. 832 * 833 * @return This builder. 834 */ 835 public Builder requestURI(final URI requestURI) { 836 this.requestURI = requestURI; 837 return this; 838 } 839 840 841 /** 842 * Sets the response mode. Corresponds to the optional 843 * {@code response_mode} parameter. Use of this parameter is 844 * not recommended unless a non-default response mode is 845 * requested (e.g. form_post). 846 * 847 * @param rm The response mode, {@code null} if not specified. 848 * 849 * @return This builder. 850 */ 851 public Builder responseMode(final ResponseMode rm) { 852 this.rm = rm; 853 return this; 854 } 855 856 857 /** 858 * Sets the code challenge for Proof Key for Code Exchange 859 * (PKCE) by public OAuth clients. 860 * 861 * @param codeChallenge The code challenge, {@code null} 862 * if not specified. 863 * @param codeChallengeMethod The code challenge method, 864 * {@code null} if not specified. 865 * 866 * @return This builder. 867 */ 868 @Deprecated 869 public Builder codeChallenge(final CodeChallenge codeChallenge, final CodeChallengeMethod codeChallengeMethod) { 870 this.codeChallenge = codeChallenge; 871 this.codeChallengeMethod = codeChallengeMethod; 872 return this; 873 } 874 875 876 /** 877 * Sets the code challenge for Proof Key for Code Exchange 878 * (PKCE) by public OAuth clients. 879 * 880 * @param codeVerifier The code verifier to use to 881 * compute the code challenge, 882 * {@code null} if PKCE is not 883 * specified. 884 * @param codeChallengeMethod The code challenge method, 885 * {@code null} if not specified. 886 * Defaults to 887 * {@link CodeChallengeMethod#PLAIN} 888 * if a code verifier is specified. 889 * 890 * @return This builder. 891 */ 892 public Builder codeChallenge(final CodeVerifier codeVerifier, final CodeChallengeMethod codeChallengeMethod) { 893 if (codeVerifier != null) { 894 CodeChallengeMethod method = codeChallengeMethod != null ? codeChallengeMethod : CodeChallengeMethod.getDefault(); 895 this.codeChallenge = CodeChallenge.compute(method, codeVerifier); 896 this.codeChallengeMethod = method; 897 } else { 898 this.codeChallenge = null; 899 this.codeChallengeMethod = null; 900 } 901 return this; 902 } 903 904 905 /** 906 * Sets the resource server URI. 907 * 908 * @param resource The resource URI, {@code null} if not 909 * specified. 910 * 911 * @return This builder. 912 */ 913 public Builder resource(final URI resource) { 914 if (resource != null) { 915 this.resources = Collections.singletonList(resource); 916 } else { 917 this.resources = null; 918 } 919 return this; 920 } 921 922 923 /** 924 * Sets the resource server URI(s). 925 * 926 * @param resources The resource URI(s), {@code null} if not 927 * specified. 928 * 929 * @return This builder. 930 */ 931 public Builder resources(final URI ... resources) { 932 if (resources != null) { 933 this.resources = Arrays.asList(resources); 934 } else { 935 this.resources = null; 936 } 937 return this; 938 } 939 940 941 /** 942 * Requests incremental authorisation. 943 * 944 * @param includeGrantedScopes {@code true} to request 945 * incremental authorisation. 946 * 947 * @return This builder. 948 */ 949 public Builder includeGrantedScopes(final boolean includeGrantedScopes) { 950 this.includeGrantedScopes = includeGrantedScopes; 951 return this; 952 } 953 954 955 /** 956 * Sets a custom parameter. 957 * 958 * @param name The parameter name. Must not be {@code null}. 959 * @param values The parameter values, {@code null} if not 960 * specified. 961 * 962 * @return This builder. 963 */ 964 public Builder customParameter(final String name, final String ... values) { 965 if (values == null || values.length == 0) { 966 customParams.remove(name); 967 } else { 968 customParams.put(name, Arrays.asList(values)); 969 } 970 return this; 971 } 972 973 974 /** 975 * Builds a new authentication request. 976 * 977 * @return The authentication request. 978 */ 979 public AuthenticationRequest build() { 980 981 try { 982 return new AuthenticationRequest( 983 uri, rt, rm, scope, clientID, redirectURI, state, nonce, 984 display, prompt, dpopJKT, trustChain, maxAge, uiLocales, claimsLocales, 985 idTokenHint, loginHint, acrValues, claims, 986 purpose, 987 requestObject, requestURI, 988 codeChallenge, codeChallengeMethod, 989 resources, 990 includeGrantedScopes, 991 customParams); 992 993 } catch (IllegalArgumentException e) { 994 throw new IllegalStateException(e.getMessage(), e); 995 } 996 } 997 } 998 999 1000 /** 1001 * Creates a new minimal OpenID Connect authentication request. 1002 * 1003 * @param uri The URI of the OAuth 2.0 authorisation endpoint. 1004 * May be {@code null} if the {@link #toHTTPRequest} 1005 * method will not be used. 1006 * @param rt The response type. Corresponds to the 1007 * {@code response_type} parameter. Must specify a 1008 * valid OpenID Connect response type. Must not be 1009 * {@code null}. 1010 * @param scope The request scope. Corresponds to the 1011 * {@code scope} parameter. Must contain an 1012 * {@link OIDCScopeValue#OPENID openid value}. Must 1013 * not be {@code null}. 1014 * @param clientID The client identifier. Corresponds to the 1015 * {@code client_id} parameter. Must not be 1016 * {@code null}. 1017 * @param redirectURI The redirection URI. Corresponds to the 1018 * {@code redirect_uri} parameter. Must not be 1019 * {@code null}. 1020 * @param state The state. Corresponds to the {@code state} 1021 * parameter. May be {@code null}. 1022 * @param nonce The nonce. Corresponds to the {@code nonce} 1023 * parameter. May be {@code null} for code flow. 1024 */ 1025 public AuthenticationRequest(final URI uri, 1026 final ResponseType rt, 1027 final Scope scope, 1028 final ClientID clientID, 1029 final URI redirectURI, 1030 final State state, 1031 final Nonce nonce) { 1032 1033 // Not specified: display, prompt, maxAge, uiLocales, claimsLocales, 1034 // idTokenHint, loginHint, acrValues, claims, purpose 1035 // codeChallenge, codeChallengeMethod 1036 this(uri, rt, null, scope, clientID, redirectURI, state, nonce, 1037 null, null, -1, null, null, 1038 null, null, null, (OIDCClaimsRequest) null, null, 1039 null, null, 1040 null, null, 1041 null, false, null); 1042 } 1043 1044 1045 /** 1046 * Creates a new OpenID Connect authentication request with extension 1047 * and custom parameters. 1048 * 1049 * @param uri The URI of the OAuth 2.0 authorisation 1050 * endpoint. May be {@code null} if the 1051 * {@link #toHTTPRequest} method will not 1052 * be used. 1053 * @param rt The response type set. Corresponds to 1054 * the {@code response_type} parameter. 1055 * Must specify a valid OpenID Connect 1056 * response type. Must not be {@code null}. 1057 * @param rm The response mode. Corresponds to the 1058 * optional {@code response_mode} 1059 * parameter. Use of this parameter is not 1060 * recommended unless a non-default 1061 * response mode is requested (e.g. 1062 * form_post). 1063 * @param scope The request scope. Corresponds to the 1064 * {@code scope} parameter. Must contain an 1065 * {@link OIDCScopeValue#OPENID openid 1066 * value}. Must not be {@code null}. 1067 * @param clientID The client identifier. Corresponds to 1068 * the {@code client_id} parameter. Must 1069 * not be {@code null}. 1070 * @param redirectURI The redirection URI. Corresponds to the 1071 * {@code redirect_uri} parameter. Must not 1072 * be {@code null} unless set by means of 1073 * the optional {@code request_object} / 1074 * {@code request_uri} parameter. 1075 * @param state The state. Corresponds to the 1076 * recommended {@code state} parameter. 1077 * {@code null} if not specified. 1078 * @param nonce The nonce. Corresponds to the 1079 * {@code nonce} parameter. May be 1080 * {@code null} for code flow. 1081 * @param display The requested display type. Corresponds 1082 * to the optional {@code display} 1083 * parameter. 1084 * {@code null} if not specified. 1085 * @param prompt The requested prompt. Corresponds to the 1086 * optional {@code prompt} parameter. 1087 * {@code null} if not specified. 1088 * @param maxAge The required maximum authentication age, 1089 * in seconds. Corresponds to the optional 1090 * {@code max_age} parameter. -1 if not 1091 * specified, zero implies 1092 * {@code prompt=login}. 1093 * @param uiLocales The preferred languages and scripts for 1094 * the user interface. Corresponds to the 1095 * optional {@code ui_locales} parameter. 1096 * {@code null} if not specified. 1097 * @param claimsLocales The preferred languages and scripts for 1098 * claims being returned. Corresponds to 1099 * the optional {@code claims_locales} 1100 * parameter. {@code null} if not 1101 * specified. 1102 * @param idTokenHint The ID Token hint. Corresponds to the 1103 * optional {@code id_token_hint} 1104 * parameter. {@code null} if not 1105 * specified. 1106 * @param loginHint The login hint. Corresponds to the 1107 * optional {@code login_hint} parameter. 1108 * {@code null} if not specified. 1109 * @param acrValues The requested Authentication Context 1110 * Class Reference values. Corresponds to 1111 * the optional {@code acr_values} 1112 * parameter. {@code null} if not 1113 * specified. 1114 * @param claims The individual claims to be returned. 1115 * Corresponds to the optional 1116 * {@code claims} parameter. {@code null} 1117 * if not specified. 1118 * @param purpose The transaction specific purpose, 1119 * {@code null} if not specified. 1120 * @param requestObject The request object. Corresponds to the 1121 * optional {@code request} parameter. Must 1122 * not be specified together with a request 1123 * object URI. {@code null} if not 1124 * specified. 1125 * @param requestURI The request object URI. Corresponds to 1126 * the optional {@code request_uri} 1127 * parameter. Must not be specified 1128 * together with a request object. 1129 * {@code null} if not specified. 1130 * @param codeChallenge The code challenge for PKCE, 1131 * {@code null} if not specified. 1132 * @param codeChallengeMethod The code challenge method for PKCE, 1133 * {@code null} if not specified. 1134 * @param resources The resource URI(s), {@code null} if not 1135 * specified. 1136 * @param includeGrantedScopes {@code true} to request incremental 1137 * authorisation. 1138 * @param customParams Additional custom parameters, empty map 1139 * or {@code null} if none. 1140 */ 1141 @Deprecated 1142 public AuthenticationRequest(final URI uri, 1143 final ResponseType rt, 1144 final ResponseMode rm, 1145 final Scope scope, 1146 final ClientID clientID, 1147 final URI redirectURI, 1148 final State state, 1149 final Nonce nonce, 1150 final Display display, 1151 final Prompt prompt, 1152 final int maxAge, 1153 final List<LangTag> uiLocales, 1154 final List<LangTag> claimsLocales, 1155 final JWT idTokenHint, 1156 final String loginHint, 1157 final List<ACR> acrValues, 1158 final ClaimsRequest claims, 1159 final String purpose, 1160 final JWT requestObject, 1161 final URI requestURI, 1162 final CodeChallenge codeChallenge, 1163 final CodeChallengeMethod codeChallengeMethod, 1164 final List<URI> resources, 1165 final boolean includeGrantedScopes, 1166 final Map<String,List<String>> customParams) { 1167 1168 this(uri, rt, rm, scope, clientID, redirectURI, state, nonce, 1169 display, prompt, maxAge, uiLocales, claimsLocales, 1170 idTokenHint, loginHint, acrValues, toOIDCClaimsRequestWithSilentFail(claims), purpose, 1171 requestObject, requestURI, 1172 codeChallenge, codeChallengeMethod, 1173 resources, includeGrantedScopes, customParams); 1174 } 1175 1176 1177 /** 1178 * Creates a new OpenID Connect authentication request with extension 1179 * and custom parameters. 1180 * 1181 * @param uri The URI of the OAuth 2.0 authorisation 1182 * endpoint. May be {@code null} if the 1183 * {@link #toHTTPRequest} method will not 1184 * be used. 1185 * @param rt The response type set. Corresponds to 1186 * the {@code response_type} parameter. 1187 * Must specify a valid OpenID Connect 1188 * response type. Must not be {@code null}. 1189 * @param rm The response mode. Corresponds to the 1190 * optional {@code response_mode} 1191 * parameter. Use of this parameter is not 1192 * recommended unless a non-default 1193 * response mode is requested (e.g. 1194 * form_post). 1195 * @param scope The request scope. Corresponds to the 1196 * {@code scope} parameter. Must contain an 1197 * {@link OIDCScopeValue#OPENID openid 1198 * value}. Must not be {@code null}. 1199 * @param clientID The client identifier. Corresponds to 1200 * the {@code client_id} parameter. Must 1201 * not be {@code null}. 1202 * @param redirectURI The redirection URI. Corresponds to the 1203 * {@code redirect_uri} parameter. Must not 1204 * be {@code null} unless set by means of 1205 * the optional {@code request_object} / 1206 * {@code request_uri} parameter. 1207 * @param state The state. Corresponds to the 1208 * recommended {@code state} parameter. 1209 * {@code null} if not specified. 1210 * @param nonce The nonce. Corresponds to the 1211 * {@code nonce} parameter. May be 1212 * {@code null} for code flow. 1213 * @param display The requested display type. Corresponds 1214 * to the optional {@code display} 1215 * parameter. 1216 * {@code null} if not specified. 1217 * @param prompt The requested prompt. Corresponds to the 1218 * optional {@code prompt} parameter. 1219 * {@code null} if not specified. 1220 * @param maxAge The required maximum authentication age, 1221 * in seconds. Corresponds to the optional 1222 * {@code max_age} parameter. -1 if not 1223 * specified, zero implies 1224 * {@code prompt=login}. 1225 * @param uiLocales The preferred languages and scripts for 1226 * the user interface. Corresponds to the 1227 * optional {@code ui_locales} parameter. 1228 * {@code null} if not specified. 1229 * @param claimsLocales The preferred languages and scripts for 1230 * claims being returned. Corresponds to 1231 * the optional {@code claims_locales} 1232 * parameter. {@code null} if not 1233 * specified. 1234 * @param idTokenHint The ID Token hint. Corresponds to the 1235 * optional {@code id_token_hint} 1236 * parameter. {@code null} if not 1237 * specified. 1238 * @param loginHint The login hint. Corresponds to the 1239 * optional {@code login_hint} parameter. 1240 * {@code null} if not specified. 1241 * @param acrValues The requested Authentication Context 1242 * Class Reference values. Corresponds to 1243 * the optional {@code acr_values} 1244 * parameter. {@code null} if not 1245 * specified. 1246 * @param claims The individual OpenID claims to be 1247 * returned. Corresponds to the optional 1248 * {@code claims} parameter. {@code null} 1249 * if not specified. 1250 * @param purpose The transaction specific purpose, 1251 * {@code null} if not specified. 1252 * @param requestObject The request object. Corresponds to the 1253 * optional {@code request} parameter. Must 1254 * not be specified together with a request 1255 * object URI. {@code null} if not 1256 * specified. 1257 * @param requestURI The request object URI. Corresponds to 1258 * the optional {@code request_uri} 1259 * parameter. Must not be specified 1260 * together with a request object. 1261 * {@code null} if not specified. 1262 * @param codeChallenge The code challenge for PKCE, 1263 * {@code null} if not specified. 1264 * @param codeChallengeMethod The code challenge method for PKCE, 1265 * {@code null} if not specified. 1266 * @param resources The resource URI(s), {@code null} if not 1267 * specified. 1268 * @param includeGrantedScopes {@code true} to request incremental 1269 * authorisation. 1270 * @param customParams Additional custom parameters, empty map 1271 * or {@code null} if none. 1272 */ 1273 @Deprecated 1274 public AuthenticationRequest(final URI uri, 1275 final ResponseType rt, 1276 final ResponseMode rm, 1277 final Scope scope, 1278 final ClientID clientID, 1279 final URI redirectURI, 1280 final State state, 1281 final Nonce nonce, 1282 final Display display, 1283 final Prompt prompt, 1284 final int maxAge, 1285 final List<LangTag> uiLocales, 1286 final List<LangTag> claimsLocales, 1287 final JWT idTokenHint, 1288 final String loginHint, 1289 final List<ACR> acrValues, 1290 final OIDCClaimsRequest claims, 1291 final String purpose, 1292 final JWT requestObject, 1293 final URI requestURI, 1294 final CodeChallenge codeChallenge, 1295 final CodeChallengeMethod codeChallengeMethod, 1296 final List<URI> resources, 1297 final boolean includeGrantedScopes, 1298 final Map<String,List<String>> customParams) { 1299 1300 this(uri, rt, rm, scope, clientID, redirectURI, state, nonce, display, prompt, null, null, 1301 maxAge, uiLocales, claimsLocales, idTokenHint, loginHint, acrValues, claims, purpose, 1302 requestObject, requestURI, codeChallenge, codeChallengeMethod, 1303 resources, includeGrantedScopes, 1304 customParams); 1305 } 1306 1307 1308 /** 1309 * Creates a new OpenID Connect authentication request with extension 1310 * and custom parameters. 1311 * 1312 * @param uri The URI of the OAuth 2.0 authorisation 1313 * endpoint. May be {@code null} if the 1314 * {@link #toHTTPRequest} method will not 1315 * be used. 1316 * @param rt The response type set. Corresponds to 1317 * the {@code response_type} parameter. 1318 * Must specify a valid OpenID Connect 1319 * response type. Must not be {@code null}. 1320 * @param rm The response mode. Corresponds to the 1321 * optional {@code response_mode} 1322 * parameter. Use of this parameter is not 1323 * recommended unless a non-default 1324 * response mode is requested (e.g. 1325 * form_post). 1326 * @param scope The request scope. Corresponds to the 1327 * {@code scope} parameter. Must contain an 1328 * {@link OIDCScopeValue#OPENID openid 1329 * value}. Must not be {@code null}. 1330 * @param clientID The client identifier. Corresponds to 1331 * the {@code client_id} parameter. Must 1332 * not be {@code null}. 1333 * @param redirectURI The redirection URI. Corresponds to the 1334 * {@code redirect_uri} parameter. Must not 1335 * be {@code null} unless set by means of 1336 * the optional {@code request_object} / 1337 * {@code request_uri} parameter. 1338 * @param state The state. Corresponds to the 1339 * recommended {@code state} parameter. 1340 * {@code null} if not specified. 1341 * @param nonce The nonce. Corresponds to the 1342 * {@code nonce} parameter. May be 1343 * {@code null} for code flow. 1344 * @param display The requested display type. Corresponds 1345 * to the optional {@code display} 1346 * parameter. 1347 * {@code null} if not specified. 1348 * @param prompt The requested prompt. Corresponds to the 1349 * optional {@code prompt} parameter. 1350 * {@code null} if not specified. 1351 * @param dpopJKT The DPoP JWK SHA-256 thumbprint, 1352 * {@code null} if not specified. 1353 * @param maxAge The required maximum authentication age, 1354 * in seconds. Corresponds to the optional 1355 * {@code max_age} parameter. -1 if not 1356 * specified, zero implies 1357 * {@code prompt=login}. 1358 * @param uiLocales The preferred languages and scripts for 1359 * the user interface. Corresponds to the 1360 * optional {@code ui_locales} parameter. 1361 * {@code null} if not specified. 1362 * @param claimsLocales The preferred languages and scripts for 1363 * claims being returned. Corresponds to 1364 * the optional {@code claims_locales} 1365 * parameter. {@code null} if not 1366 * specified. 1367 * @param idTokenHint The ID Token hint. Corresponds to the 1368 * optional {@code id_token_hint} 1369 * parameter. {@code null} if not 1370 * specified. 1371 * @param loginHint The login hint. Corresponds to the 1372 * optional {@code login_hint} parameter. 1373 * {@code null} if not specified. 1374 * @param acrValues The requested Authentication Context 1375 * Class Reference values. Corresponds to 1376 * the optional {@code acr_values} 1377 * parameter. {@code null} if not 1378 * specified. 1379 * @param claims The individual OpenID claims to be 1380 * returned. Corresponds to the optional 1381 * {@code claims} parameter. {@code null} 1382 * if not specified. 1383 * @param purpose The transaction specific purpose, 1384 * {@code null} if not specified. 1385 * @param requestObject The request object. Corresponds to the 1386 * optional {@code request} parameter. Must 1387 * not be specified together with a request 1388 * object URI. {@code null} if not 1389 * specified. 1390 * @param requestURI The request object URI. Corresponds to 1391 * the optional {@code request_uri} 1392 * parameter. Must not be specified 1393 * together with a request object. 1394 * {@code null} if not specified. 1395 * @param codeChallenge The code challenge for PKCE, 1396 * {@code null} if not specified. 1397 * @param codeChallengeMethod The code challenge method for PKCE, 1398 * {@code null} if not specified. 1399 * @param resources The resource URI(s), {@code null} if not 1400 * specified. 1401 * @param includeGrantedScopes {@code true} to request incremental 1402 * authorisation. 1403 * @param customParams Additional custom parameters, empty map 1404 * or {@code null} if none. 1405 */ 1406 @Deprecated 1407 public AuthenticationRequest(final URI uri, 1408 final ResponseType rt, 1409 final ResponseMode rm, 1410 final Scope scope, 1411 final ClientID clientID, 1412 final URI redirectURI, 1413 final State state, 1414 final Nonce nonce, 1415 final Display display, 1416 final Prompt prompt, 1417 final JWKThumbprintConfirmation dpopJKT, 1418 final int maxAge, 1419 final List<LangTag> uiLocales, 1420 final List<LangTag> claimsLocales, 1421 final JWT idTokenHint, 1422 final String loginHint, 1423 final List<ACR> acrValues, 1424 final OIDCClaimsRequest claims, 1425 final String purpose, 1426 final JWT requestObject, 1427 final URI requestURI, 1428 final CodeChallenge codeChallenge, 1429 final CodeChallengeMethod codeChallengeMethod, 1430 final List<URI> resources, 1431 final boolean includeGrantedScopes, 1432 final Map<String,List<String>> customParams) { 1433 1434 this(uri, rt, rm, scope, clientID, redirectURI, state, nonce, display, prompt, dpopJKT, null, 1435 maxAge, uiLocales, claimsLocales, idTokenHint, loginHint, acrValues, claims, purpose, 1436 requestObject, requestURI, codeChallenge, codeChallengeMethod, 1437 resources, includeGrantedScopes, 1438 customParams); 1439 } 1440 1441 1442 /** 1443 * Creates a new OpenID Connect authentication request with extension 1444 * and custom parameters. 1445 * 1446 * @param uri The URI of the OAuth 2.0 authorisation 1447 * endpoint. May be {@code null} if the 1448 * {@link #toHTTPRequest} method will not 1449 * be used. 1450 * @param rt The response type set. Corresponds to 1451 * the {@code response_type} parameter. 1452 * Must specify a valid OpenID Connect 1453 * response type. Must not be {@code null}. 1454 * @param rm The response mode. Corresponds to the 1455 * optional {@code response_mode} 1456 * parameter. Use of this parameter is not 1457 * recommended unless a non-default 1458 * response mode is requested (e.g. 1459 * form_post). 1460 * @param scope The request scope. Corresponds to the 1461 * {@code scope} parameter. Must contain an 1462 * {@link OIDCScopeValue#OPENID openid 1463 * value}. Must not be {@code null}. 1464 * @param clientID The client identifier. Corresponds to 1465 * the {@code client_id} parameter. Must 1466 * not be {@code null}. 1467 * @param redirectURI The redirection URI. Corresponds to the 1468 * {@code redirect_uri} parameter. Must not 1469 * be {@code null} unless set by means of 1470 * the optional {@code request_object} / 1471 * {@code request_uri} parameter. 1472 * @param state The state. Corresponds to the 1473 * recommended {@code state} parameter. 1474 * {@code null} if not specified. 1475 * @param nonce The nonce. Corresponds to the 1476 * {@code nonce} parameter. May be 1477 * {@code null} for code flow. 1478 * @param display The requested display type. Corresponds 1479 * to the optional {@code display} 1480 * parameter. 1481 * {@code null} if not specified. 1482 * @param prompt The requested prompt. Corresponds to the 1483 * optional {@code prompt} parameter. 1484 * {@code null} if not specified. 1485 * @param dpopJKT The DPoP JWK SHA-256 thumbprint, 1486 * {@code null} if not specified. 1487 * @param trustChain The OpenID Connect Federation 1.0 trust 1488 * chain, {@code null} if not specified. 1489 * @param maxAge The required maximum authentication age, 1490 * in seconds. Corresponds to the optional 1491 * {@code max_age} parameter. -1 if not 1492 * specified, zero implies 1493 * {@code prompt=login}. 1494 * @param uiLocales The preferred languages and scripts for 1495 * the user interface. Corresponds to the 1496 * optional {@code ui_locales} parameter. 1497 * {@code null} if not specified. 1498 * @param claimsLocales The preferred languages and scripts for 1499 * claims being returned. Corresponds to 1500 * the optional {@code claims_locales} 1501 * parameter. {@code null} if not 1502 * specified. 1503 * @param idTokenHint The ID Token hint. Corresponds to the 1504 * optional {@code id_token_hint} 1505 * parameter. {@code null} if not 1506 * specified. 1507 * @param loginHint The login hint. Corresponds to the 1508 * optional {@code login_hint} parameter. 1509 * {@code null} if not specified. 1510 * @param acrValues The requested Authentication Context 1511 * Class Reference values. Corresponds to 1512 * the optional {@code acr_values} 1513 * parameter. {@code null} if not 1514 * specified. 1515 * @param claims The individual OpenID claims to be 1516 * returned. Corresponds to the optional 1517 * {@code claims} parameter. {@code null} 1518 * if not specified. 1519 * @param purpose The transaction specific purpose, 1520 * {@code null} if not specified. 1521 * @param requestObject The request object. Corresponds to the 1522 * optional {@code request} parameter. Must 1523 * not be specified together with a request 1524 * object URI. {@code null} if not 1525 * specified. 1526 * @param requestURI The request object URI. Corresponds to 1527 * the optional {@code request_uri} 1528 * parameter. Must not be specified 1529 * together with a request object. 1530 * {@code null} if not specified. 1531 * @param codeChallenge The code challenge for PKCE, 1532 * {@code null} if not specified. 1533 * @param codeChallengeMethod The code challenge method for PKCE, 1534 * {@code null} if not specified. 1535 * @param resources The resource URI(s), {@code null} if not 1536 * specified. 1537 * @param includeGrantedScopes {@code true} to request incremental 1538 * authorisation. 1539 * @param customParams Additional custom parameters, empty map 1540 * or {@code null} if none. 1541 */ 1542 public AuthenticationRequest(final URI uri, 1543 final ResponseType rt, 1544 final ResponseMode rm, 1545 final Scope scope, 1546 final ClientID clientID, 1547 final URI redirectURI, 1548 final State state, 1549 final Nonce nonce, 1550 final Display display, 1551 final Prompt prompt, 1552 final JWKThumbprintConfirmation dpopJKT, 1553 final TrustChain trustChain, 1554 final int maxAge, 1555 final List<LangTag> uiLocales, 1556 final List<LangTag> claimsLocales, 1557 final JWT idTokenHint, 1558 final String loginHint, 1559 final List<ACR> acrValues, 1560 final OIDCClaimsRequest claims, 1561 final String purpose, 1562 final JWT requestObject, 1563 final URI requestURI, 1564 final CodeChallenge codeChallenge, 1565 final CodeChallengeMethod codeChallengeMethod, 1566 final List<URI> resources, 1567 final boolean includeGrantedScopes, 1568 final Map<String,List<String>> customParams) { 1569 1570 super(uri, rt, rm, clientID, redirectURI, scope, state, codeChallenge, codeChallengeMethod, resources, includeGrantedScopes, requestObject, requestURI, prompt, dpopJKT, trustChain, customParams); 1571 1572 if (! specifiesRequestObject()) { 1573 1574 // Check parameters required by OpenID Connect if no JAR 1575 1576 if (redirectURI == null) 1577 throw new IllegalArgumentException("The redirection URI must not be null"); 1578 1579 OIDCResponseTypeValidator.validate(rt); 1580 1581 if (scope == null) 1582 throw new IllegalArgumentException("The scope must not be null"); 1583 1584 if (!scope.contains(OIDCScopeValue.OPENID)) 1585 throw new IllegalArgumentException("The scope must include an \"openid\" value"); 1586 1587 // Check nonce requirement 1588 if (nonce == null && Nonce.isRequired(rt)) { 1589 throw new IllegalArgumentException("Nonce required for response_type=" + rt); 1590 } 1591 } 1592 1593 this.nonce = nonce; 1594 1595 // Optional parameters 1596 this.display = display; 1597 this.maxAge = maxAge; 1598 1599 if (uiLocales != null) 1600 this.uiLocales = Collections.unmodifiableList(uiLocales); 1601 else 1602 this.uiLocales = null; 1603 1604 if (claimsLocales != null) 1605 this.claimsLocales = Collections.unmodifiableList(claimsLocales); 1606 else 1607 this.claimsLocales = null; 1608 1609 this.idTokenHint = idTokenHint; 1610 this.loginHint = loginHint; 1611 1612 if (acrValues != null) 1613 this.acrValues = Collections.unmodifiableList(acrValues); 1614 else 1615 this.acrValues = null; 1616 1617 this.claims = claims; 1618 1619 if (purpose != null) { 1620 if (purpose.length() < PURPOSE_MIN_LENGTH) { 1621 throw new IllegalArgumentException("The purpose must not be shorter than " + PURPOSE_MIN_LENGTH + " characters"); 1622 } 1623 if (purpose.length() > PURPOSE_MAX_LENGTH) { 1624 throw new IllegalArgumentException("The purpose must not be longer than " + PURPOSE_MAX_LENGTH +" characters"); 1625 } 1626 } 1627 1628 this.purpose = purpose; 1629 } 1630 1631 1632 /** 1633 * Returns the registered (standard) OpenID Connect authentication 1634 * request parameter names. 1635 * 1636 * @return The registered OpenID Connect authentication request 1637 * parameter names, as a unmodifiable set. 1638 */ 1639 public static Set<String> getRegisteredParameterNames() { 1640 1641 return REGISTERED_PARAMETER_NAMES; 1642 } 1643 1644 1645 /** 1646 * Returns the nonce. Corresponds to the conditionally optional 1647 * {@code nonce} parameter. 1648 * 1649 * @return The nonce, {@code null} if not specified. 1650 */ 1651 public Nonce getNonce() { 1652 1653 return nonce; 1654 } 1655 1656 1657 /** 1658 * Returns the requested display type. Corresponds to the optional 1659 * {@code display} parameter. 1660 * 1661 * @return The requested display type, {@code null} if not specified. 1662 */ 1663 public Display getDisplay() { 1664 1665 return display; 1666 } 1667 1668 1669 /** 1670 * Returns the required maximum authentication age. Corresponds to the 1671 * optional {@code max_age} parameter. 1672 * 1673 * @return The maximum authentication age, in seconds; -1 if not 1674 * specified, zero implies {@code prompt=login}. 1675 */ 1676 public int getMaxAge() { 1677 1678 return maxAge; 1679 } 1680 1681 1682 /** 1683 * Returns the end-user's preferred languages and scripts for the user 1684 * interface, ordered by preference. Corresponds to the optional 1685 * {@code ui_locales} parameter. 1686 * 1687 * @return The preferred UI locales, {@code null} if not specified. 1688 */ 1689 public List<LangTag> getUILocales() { 1690 1691 return uiLocales; 1692 } 1693 1694 1695 /** 1696 * Returns the end-user's preferred languages and scripts for the 1697 * claims being returned, ordered by preference. Corresponds to the 1698 * optional {@code claims_locales} parameter. 1699 * 1700 * @return The preferred claims locales, {@code null} if not specified. 1701 */ 1702 public List<LangTag> getClaimsLocales() { 1703 1704 return claimsLocales; 1705 } 1706 1707 1708 /** 1709 * Returns the ID Token hint. Corresponds to the conditionally optional 1710 * {@code id_token_hint} parameter. 1711 * 1712 * @return The ID Token hint, {@code null} if not specified. 1713 */ 1714 public JWT getIDTokenHint() { 1715 1716 return idTokenHint; 1717 } 1718 1719 1720 /** 1721 * Returns the login hint. Corresponds to the optional {@code login_hint} 1722 * parameter. 1723 * 1724 * @return The login hint, {@code null} if not specified. 1725 */ 1726 public String getLoginHint() { 1727 1728 return loginHint; 1729 } 1730 1731 1732 /** 1733 * Returns the requested Authentication Context Class Reference values. 1734 * Corresponds to the optional {@code acr_values} parameter. 1735 * 1736 * @return The requested ACR values, {@code null} if not specified. 1737 */ 1738 public List<ACR> getACRValues() { 1739 1740 return acrValues; 1741 } 1742 1743 1744 /** 1745 * Returns the individual claims to be returned. Corresponds to the 1746 * optional {@code claims} parameter. 1747 * 1748 * @see #getOIDCClaims() 1749 * 1750 * @return The individual claims to be returned, {@code null} if not 1751 * specified. 1752 */ 1753 @Deprecated 1754 public ClaimsRequest getClaims() { 1755 1756 return toClaimsRequestWithSilentFail(claims); 1757 } 1758 1759 1760 private static OIDCClaimsRequest toOIDCClaimsRequestWithSilentFail(final ClaimsRequest claims) { 1761 if (claims == null) { 1762 return null; 1763 } 1764 try { 1765 return OIDCClaimsRequest.parse(claims.toJSONObject()); 1766 } catch (ParseException e) { 1767 return null; 1768 } 1769 } 1770 1771 1772 private static ClaimsRequest toClaimsRequestWithSilentFail(final OIDCClaimsRequest claims) { 1773 if (claims == null) { 1774 return null; 1775 } 1776 try { 1777 return ClaimsRequest.parse(claims.toJSONObject()); 1778 } catch (ParseException e) { 1779 return null; 1780 } 1781 } 1782 1783 1784 /** 1785 * Returns the individual OpenID claims to be returned. Corresponds to 1786 * the optional {@code claims} parameter. 1787 * 1788 * @return The individual claims to be returned, {@code null} if not 1789 * specified. 1790 */ 1791 public OIDCClaimsRequest getOIDCClaims() { 1792 1793 return claims; 1794 } 1795 1796 1797 /** 1798 * Returns the transaction specific purpose. Corresponds to the 1799 * optional {@code purpose} parameter. 1800 * 1801 * @return The purpose, {@code null} if not specified. 1802 */ 1803 public String getPurpose() { 1804 1805 return purpose; 1806 } 1807 1808 1809 @Override 1810 public Map<String,List<String>> toParameters() { 1811 1812 Map <String,List<String>> params = super.toParameters(); 1813 1814 if (nonce != null) 1815 params.put("nonce", Collections.singletonList(nonce.toString())); 1816 1817 if (display != null) 1818 params.put("display", Collections.singletonList(display.toString())); 1819 1820 if (maxAge >= 0) 1821 params.put("max_age", Collections.singletonList("" + maxAge)); 1822 1823 if (uiLocales != null) { 1824 params.put("ui_locales", Collections.singletonList(LangTagUtils.concat(uiLocales))); 1825 } 1826 1827 if (CollectionUtils.isNotEmpty(claimsLocales)) { 1828 params.put("claims_locales", Collections.singletonList(LangTagUtils.concat(claimsLocales))); 1829 } 1830 1831 if (idTokenHint != null) { 1832 1833 try { 1834 params.put("id_token_hint", Collections.singletonList(idTokenHint.serialize())); 1835 1836 } catch (IllegalStateException e) { 1837 1838 throw new SerializeException("Couldn't serialize ID token hint: " + e.getMessage(), e); 1839 } 1840 } 1841 1842 if (loginHint != null) 1843 params.put("login_hint", Collections.singletonList(loginHint)); 1844 1845 if (acrValues != null) { 1846 1847 StringBuilder sb = new StringBuilder(); 1848 1849 for (ACR acr: acrValues) { 1850 1851 if (sb.length() > 0) 1852 sb.append(' '); 1853 1854 sb.append(acr.toString()); 1855 } 1856 1857 params.put("acr_values", Collections.singletonList(sb.toString())); 1858 } 1859 1860 1861 if (claims != null) 1862 params.put("claims", Collections.singletonList(claims.toJSONObject().toString())); 1863 1864 if (purpose != null) 1865 params.put("purpose", Collections.singletonList(purpose)); 1866 1867 return params; 1868 } 1869 1870 1871 @Override 1872 public JWTClaimsSet toJWTClaimsSet() { 1873 1874 JWTClaimsSet jwtClaimsSet = super.toJWTClaimsSet(); 1875 1876 if (jwtClaimsSet.getClaim("max_age") != null) { 1877 // Convert max_age to number in JSON object 1878 try { 1879 String maxAgeString = jwtClaimsSet.getStringClaim("max_age"); 1880 JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder(jwtClaimsSet); 1881 builder.claim("max_age", Integer.parseInt(maxAgeString)); 1882 return builder.build(); 1883 } catch (java.text.ParseException e) { 1884 throw new SerializeException(e.getMessage()); 1885 } 1886 } 1887 1888 return jwtClaimsSet; 1889 } 1890 1891 1892 /** 1893 * Parses an OpenID Connect authentication request from the specified 1894 * URI query parameters. 1895 * 1896 * <p>Example parameters: 1897 * 1898 * <pre> 1899 * response_type = token id_token 1900 * client_id = s6BhdRkqt3 1901 * redirect_uri = https://client.example.com/cb 1902 * scope = openid profile 1903 * state = af0ifjsldkj 1904 * nonce = -0S6_WzA2Mj 1905 * </pre> 1906 * 1907 * @param params The parameters. Must not be {@code null}. 1908 * 1909 * @return The OpenID Connect authentication request. 1910 * 1911 * @throws ParseException If the parameters couldn't be parsed to an 1912 * OpenID Connect authentication request. 1913 */ 1914 public static AuthenticationRequest parse(final Map<String,List<String>> params) 1915 throws ParseException { 1916 1917 return parse(null, params); 1918 } 1919 1920 1921 /** 1922 * Parses an OpenID Connect authentication request from the specified 1923 * URI and query parameters. 1924 * 1925 * <p>Example parameters: 1926 * 1927 * <pre> 1928 * response_type = token id_token 1929 * client_id = s6BhdRkqt3 1930 * redirect_uri = https://client.example.com/cb 1931 * scope = openid profile 1932 * state = af0ifjsldkj 1933 * nonce = -0S6_WzA2Mj 1934 * </pre> 1935 * 1936 * @param uri The URI of the OAuth 2.0 authorisation endpoint. May 1937 * be {@code null} if the {@link #toHTTPRequest} method 1938 * will not be used. 1939 * @param params The parameters. Must not be {@code null}. 1940 * 1941 * @return The OpenID Connect authentication request. 1942 * 1943 * @throws ParseException If the parameters couldn't be parsed to an 1944 * OpenID Connect authentication request. 1945 */ 1946 public static AuthenticationRequest parse(final URI uri, final Map<String,List<String>> params) 1947 throws ParseException { 1948 1949 // Parse and validate the core OAuth 2.0 autz request params in 1950 // the context of OIDC 1951 AuthorizationRequest ar = AuthorizationRequest.parse(uri, params); 1952 1953 Nonce nonce = Nonce.parse(MultivaluedMapUtils.getFirstValue(params, "nonce")); 1954 1955 if (! ar.specifiesRequestObject()) { 1956 1957 // Required params if no JAR is present 1958 1959 if (ar.getRedirectionURI() == null) { 1960 String msg = "Missing redirect_uri parameter"; 1961 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1962 ar.getClientID(), null, ar.impliedResponseMode(), ar.getState()); 1963 } 1964 1965 if (ar.getScope() == null) { 1966 String msg = "Missing scope parameter"; 1967 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1968 ar.getClientID(), ar.getRedirectionURI(), ar.impliedResponseMode(), ar.getState()); 1969 } 1970 1971 // Check nonce requirement 1972 if (nonce == null && Nonce.isRequired(ar.getResponseType())) { 1973 String msg = "Missing nonce parameter: Required for response_type=" + ar.getResponseType(); 1974 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1975 ar.getClientID(), ar.getRedirectionURI(), ar.impliedResponseMode(), ar.getState()); 1976 } 1977 } 1978 1979 // Check if present (not in JAR) 1980 if (ar.getResponseType() != null) { 1981 try { 1982 OIDCResponseTypeValidator.validate(ar.getResponseType()); 1983 } catch (IllegalArgumentException e) { 1984 String msg = "Unsupported response_type parameter: " + e.getMessage(); 1985 throw new ParseException(msg, OAuth2Error.UNSUPPORTED_RESPONSE_TYPE.appendDescription(": " + msg), 1986 ar.getClientID(), ar.getRedirectionURI(), ar.impliedResponseMode(), ar.getState()); 1987 } 1988 } 1989 1990 // Check if present (not in JAR) 1991 if (ar.getScope() != null && ! ar.getScope().contains(OIDCScopeValue.OPENID)) { 1992 String msg = "The scope must include an openid value"; 1993 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1994 ar.getClientID(), ar.getRedirectionURI(), ar.impliedResponseMode(), ar.getState()); 1995 } 1996 1997 Display display = null; 1998 1999 if (params.containsKey("display")) { 2000 try { 2001 display = Display.parse(MultivaluedMapUtils.getFirstValue(params, "display")); 2002 2003 } catch (ParseException e) { 2004 String msg = "Invalid display parameter: " + e.getMessage(); 2005 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 2006 ar.getClientID(), ar.getRedirectionURI(), ar.impliedResponseMode(), ar.getState(), e); 2007 } 2008 } 2009 2010 2011 String v = MultivaluedMapUtils.getFirstValue(params, "max_age"); 2012 2013 int maxAge = -1; 2014 2015 if (StringUtils.isNotBlank(v)) { 2016 2017 try { 2018 maxAge = Integer.parseInt(v); 2019 2020 } catch (NumberFormatException e) { 2021 String msg = "Invalid max_age parameter: " + v; 2022 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 2023 ar.getClientID(), ar.getRedirectionURI(), ar.impliedResponseMode(), ar.getState(), e); 2024 } 2025 } 2026 2027 2028 List<LangTag> uiLocales; 2029 try { 2030 uiLocales = LangTagUtils.parseLangTagList(MultivaluedMapUtils.getFirstValue(params, "ui_locales")); 2031 } catch (LangTagException e) { 2032 String msg = "Invalid ui_locales parameter: " + e.getMessage(); 2033 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 2034 ar.getClientID(), ar.getRedirectionURI(), ar.impliedResponseMode(), ar.getState(), e); 2035 } 2036 2037 2038 List<LangTag> claimsLocales; 2039 try { 2040 claimsLocales = LangTagUtils.parseLangTagList(MultivaluedMapUtils.getFirstValue(params, "claims_locales")); 2041 2042 } catch (LangTagException e) { 2043 String msg = "Invalid claims_locales parameter: " + e.getMessage(); 2044 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 2045 ar.getClientID(), ar.getRedirectionURI(), ar.impliedResponseMode(), ar.getState(), e); 2046 } 2047 2048 2049 v = MultivaluedMapUtils.getFirstValue(params, "id_token_hint"); 2050 2051 JWT idTokenHint = null; 2052 2053 if (StringUtils.isNotBlank(v)) { 2054 2055 try { 2056 idTokenHint = JWTParser.parse(v); 2057 2058 } catch (java.text.ParseException e) { 2059 String msg = "Invalid id_token_hint parameter: " + e.getMessage(); 2060 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 2061 ar.getClientID(), ar.getRedirectionURI(), ar.impliedResponseMode(), ar.getState(), e); 2062 } 2063 } 2064 2065 String loginHint = MultivaluedMapUtils.getFirstValue(params, "login_hint"); 2066 2067 2068 v = MultivaluedMapUtils.getFirstValue(params, "acr_values"); 2069 2070 List<ACR> acrValues = null; 2071 2072 if (StringUtils.isNotBlank(v)) { 2073 2074 acrValues = new LinkedList<>(); 2075 2076 StringTokenizer st = new StringTokenizer(v, " "); 2077 2078 while (st.hasMoreTokens()) { 2079 2080 acrValues.add(new ACR(st.nextToken())); 2081 } 2082 } 2083 2084 2085 v = MultivaluedMapUtils.getFirstValue(params, "claims"); 2086 2087 OIDCClaimsRequest claims = null; 2088 2089 if (StringUtils.isNotBlank(v)) { 2090 try { 2091 claims = OIDCClaimsRequest.parse(v); 2092 } catch (ParseException e) { 2093 String msg = "Invalid claims parameter: " + e.getMessage(); 2094 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 2095 ar.getClientID(), ar.getRedirectionURI(), ar.impliedResponseMode(), ar.getState(), e); 2096 } 2097 } 2098 2099 String purpose = MultivaluedMapUtils.getFirstValue(params, "purpose"); 2100 2101 if (purpose != null && (purpose.length() < PURPOSE_MIN_LENGTH || purpose.length() > PURPOSE_MAX_LENGTH)) { 2102 String msg = "Invalid purpose parameter: Must not be shorter than " + PURPOSE_MIN_LENGTH + " and longer than " + PURPOSE_MAX_LENGTH + " characters"; 2103 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 2104 ar.getClientID(), ar.getRedirectionURI(), ar.impliedResponseMode(), ar.getState()); 2105 } 2106 2107 2108 // Parse additional custom parameters 2109 Map<String,List<String>> customParams = null; 2110 2111 for (Map.Entry<String,List<String>> p: params.entrySet()) { 2112 2113 if (! REGISTERED_PARAMETER_NAMES.contains(p.getKey())) { 2114 // We have a custom parameter 2115 if (customParams == null) { 2116 customParams = new HashMap<>(); 2117 } 2118 customParams.put(p.getKey(), p.getValue()); 2119 } 2120 } 2121 2122 2123 return new AuthenticationRequest( 2124 uri, ar.getResponseType(), ar.getResponseMode(), ar.getScope(), ar.getClientID(), ar.getRedirectionURI(), ar.getState(), nonce, 2125 display, ar.getPrompt(), ar.getDPoPJWKThumbprintConfirmation(), ar.getTrustChain(), maxAge, uiLocales, claimsLocales, 2126 idTokenHint, loginHint, acrValues, claims, purpose, 2127 ar.getRequestObject(), ar.getRequestURI(), 2128 ar.getCodeChallenge(), ar.getCodeChallengeMethod(), 2129 ar.getResources(), 2130 ar.includeGrantedScopes(), 2131 customParams); 2132 } 2133 2134 2135 /** 2136 * Parses an OpenID Connect authentication request from the specified 2137 * URI query string. 2138 * 2139 * <p>Example URI query string: 2140 * 2141 * <pre> 2142 * response_type=token%20id_token 2143 * &client_id=s6BhdRkqt3 2144 * &redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb 2145 * &scope=openid%20profile 2146 * &state=af0ifjsldkj 2147 * &nonce=n-0S6_WzA2Mj 2148 * </pre> 2149 * 2150 * @param query The URI query string. Must not be {@code null}. 2151 * 2152 * @return The OpenID Connect authentication request. 2153 * 2154 * @throws ParseException If the query string couldn't be parsed to an 2155 * OpenID Connect authentication request. 2156 */ 2157 public static AuthenticationRequest parse(final String query) 2158 throws ParseException { 2159 2160 return parse(null, URLUtils.parseParameters(query)); 2161 } 2162 2163 2164 /** 2165 * Parses an OpenID Connect authentication request from the specified 2166 * URI query string. 2167 * 2168 * <p>Example URI query string: 2169 * 2170 * <pre> 2171 * response_type=token%20id_token 2172 * &client_id=s6BhdRkqt3 2173 * &redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb 2174 * &scope=openid%20profile 2175 * &state=af0ifjsldkj 2176 * &nonce=n-0S6_WzA2Mj 2177 * </pre> 2178 * 2179 * @param uri The URI of the OAuth 2.0 authorisation endpoint. May be 2180 * {@code null} if the {@link #toHTTPRequest} method will 2181 * not be used. 2182 * @param query The URI query string. Must not be {@code null}. 2183 * 2184 * @return The OpenID Connect authentication request. 2185 * 2186 * @throws ParseException If the query string couldn't be parsed to an 2187 * OpenID Connect authentication request. 2188 */ 2189 public static AuthenticationRequest parse(final URI uri, final String query) 2190 throws ParseException { 2191 2192 return parse(uri, URLUtils.parseParameters(query)); 2193 } 2194 2195 2196 /** 2197 * Parses an OpenID Connect authentication request from the specified 2198 * URI. 2199 * 2200 * <p>Example URI: 2201 * 2202 * <pre> 2203 * https://server.example.com/authorize? 2204 * response_type=token%20id_token 2205 * &client_id=s6BhdRkqt3 2206 * &redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb 2207 * &scope=openid%20profile 2208 * &state=af0ifjsldkj 2209 * &nonce=n-0S6_WzA2Mj 2210 * </pre> 2211 * 2212 * @param uri The URI. Must not be {@code null}. 2213 * 2214 * @return The OpenID Connect authentication request. 2215 * 2216 * @throws ParseException If the query string couldn't be parsed to an 2217 * OpenID Connect authentication request. 2218 */ 2219 public static AuthenticationRequest parse(final URI uri) 2220 throws ParseException { 2221 2222 return parse(URIUtils.getBaseURI(uri), URLUtils.parseParameters(uri.getRawQuery())); 2223 } 2224 2225 2226 /** 2227 * Parses an authentication request from the specified HTTP GET or HTTP 2228 * POST request. 2229 * 2230 * <p>Example HTTP request (GET): 2231 * 2232 * <pre> 2233 * https://server.example.com/op/authorize? 2234 * response_type=code%20id_token 2235 * &client_id=s6BhdRkqt3 2236 * &redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb 2237 * &scope=openid 2238 * &nonce=n-0S6_WzA2Mj 2239 * &state=af0ifjsldkj 2240 * </pre> 2241 * 2242 * @param httpRequest The HTTP request. Must not be {@code null}. 2243 * 2244 * @return The OpenID Connect authentication request. 2245 * 2246 * @throws ParseException If the HTTP request couldn't be parsed to an 2247 * OpenID Connect authentication request. 2248 */ 2249 public static AuthenticationRequest parse(final HTTPRequest httpRequest) 2250 throws ParseException { 2251 2252 String query = httpRequest.getQuery(); 2253 2254 if (query == null) 2255 throw new ParseException("Missing URI query string"); 2256 2257 URI endpointURI = httpRequest.getURI(); 2258 2259 return parse(endpointURI, query); 2260 } 2261}