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