001package com.nimbusds.openid.connect.sdk; 002 003 004import java.net.URI; 005import java.net.URISyntaxException; 006import java.util.*; 007 008import com.nimbusds.jwt.JWT; 009import com.nimbusds.jwt.JWTParser; 010import com.nimbusds.langtag.LangTag; 011import com.nimbusds.langtag.LangTagException; 012import com.nimbusds.oauth2.sdk.*; 013import com.nimbusds.oauth2.sdk.http.HTTPRequest; 014import com.nimbusds.oauth2.sdk.id.ClientID; 015import com.nimbusds.oauth2.sdk.id.State; 016import com.nimbusds.oauth2.sdk.pkce.CodeChallenge; 017import com.nimbusds.oauth2.sdk.pkce.CodeChallengeMethod; 018import com.nimbusds.oauth2.sdk.util.JSONObjectUtils; 019import com.nimbusds.oauth2.sdk.util.URIUtils; 020import com.nimbusds.oauth2.sdk.util.URLUtils; 021import com.nimbusds.openid.connect.sdk.claims.ACR; 022import net.jcip.annotations.Immutable; 023import net.minidev.json.JSONObject; 024import org.apache.commons.lang3.StringUtils; 025 026 027/** 028 * OpenID Connect authentication request. Intended to authenticate an end-user 029 * and request the end-user's authorisation to release information to the 030 * client. Supports custom request parameters. 031 * 032 * <p>Example HTTP request (code flow): 033 * 034 * <pre> 035 * https://server.example.com/op/authorize? 036 * response_type=code%20id_token 037 * &client_id=s6BhdRkqt3 038 * &redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb 039 * &scope=openid 040 * &nonce=n-0S6_WzA2Mj 041 * &state=af0ifjsldkj 042 * </pre> 043 * 044 * <p>Related specifications: 045 * 046 * <ul> 047 * <li>OpenID Connect Core 1.0, section 3.1.2.1. 048 * <li>Proof Key for Code Exchange by OAuth Public Clients (RFC 7636). 049 * </ul> 050 */ 051@Immutable 052public class AuthenticationRequest extends AuthorizationRequest { 053 054 055 /** 056 * The registered parameter names. 057 */ 058 private static final Set<String> REGISTERED_PARAMETER_NAMES; 059 060 061 /** 062 * Initialises the registered parameter name set. 063 */ 064 static { 065 Set<String> p = new HashSet<>(); 066 067 p.addAll(AuthorizationRequest.getRegisteredParameterNames()); 068 069 p.add("nonce"); 070 p.add("display"); 071 p.add("prompt"); 072 p.add("max_age"); 073 p.add("ui_locales"); 074 p.add("claims_locales"); 075 p.add("id_token_hint"); 076 p.add("login_hint"); 077 p.add("acr_values"); 078 p.add("claims"); 079 p.add("request_uri"); 080 p.add("request"); 081 082 REGISTERED_PARAMETER_NAMES = Collections.unmodifiableSet(p); 083 } 084 085 086 /** 087 * The nonce (required for implicit flow, optional for code flow). 088 */ 089 private final Nonce nonce; 090 091 092 /** 093 * The requested display type (optional). 094 */ 095 private final Display display; 096 097 098 /** 099 * The requested prompt (optional). 100 */ 101 private final Prompt prompt; 102 103 104 /** 105 * The required maximum authentication age, in seconds, 0 if not 106 * specified (optional). 107 */ 108 private final int maxAge; 109 110 111 /** 112 * The end-user's preferred languages and scripts for the user 113 * interface (optional). 114 */ 115 private final List<LangTag> uiLocales; 116 117 118 /** 119 * The end-user's preferred languages and scripts for claims being 120 * returned (optional). 121 */ 122 private final List<LangTag> claimsLocales; 123 124 125 /** 126 * Previously issued ID Token passed to the authorisation server as a 127 * hint about the end-user's current or past authenticated session with 128 * the client (optional). Should be present when {@code prompt=none} is 129 * used. 130 */ 131 private final JWT idTokenHint; 132 133 134 /** 135 * Hint to the authorisation server about the login identifier the 136 * end-user may use to log in (optional). 137 */ 138 private final String loginHint; 139 140 141 /** 142 * Requested Authentication Context Class Reference values (optional). 143 */ 144 private final List<ACR> acrValues; 145 146 147 /** 148 * Individual claims to be returned (optional). 149 */ 150 private final ClaimsRequest claims; 151 152 153 /** 154 * Request object (optional). 155 */ 156 private final JWT requestObject; 157 158 159 /** 160 * Request object URI (optional). 161 */ 162 private final URI requestURI; 163 164 165 /** 166 * Builder for constructing OpenID Connect authentication requests. 167 */ 168 public static class Builder { 169 170 171 /** 172 * The endpoint URI (optional). 173 */ 174 private URI uri; 175 176 177 /** 178 * The response type (required). 179 */ 180 private final ResponseType rt; 181 182 183 /** 184 * The client identifier (required). 185 */ 186 private final ClientID clientID; 187 188 189 /** 190 * The redirection URI where the response will be sent 191 * (required). 192 */ 193 private final URI redirectURI; 194 195 196 /** 197 * The scope (required). 198 */ 199 private final Scope scope; 200 201 202 /** 203 * The opaque value to maintain state between the request and 204 * the callback (recommended). 205 */ 206 private State state; 207 208 209 /** 210 * The nonce (required for implicit flow, optional for code 211 * flow). 212 */ 213 private Nonce nonce; 214 215 216 /** 217 * The requested display type (optional). 218 */ 219 private Display display; 220 221 222 /** 223 * The requested prompt (optional). 224 */ 225 private Prompt prompt; 226 227 228 /** 229 * The required maximum authentication age, in seconds, 0 if 230 * not specified (optional). 231 */ 232 private int maxAge; 233 234 235 /** 236 * The end-user's preferred languages and scripts for the user 237 * interface (optional). 238 */ 239 private List<LangTag> uiLocales; 240 241 242 /** 243 * The end-user's preferred languages and scripts for claims 244 * being returned (optional). 245 */ 246 private List<LangTag> claimsLocales; 247 248 249 /** 250 * Previously issued ID Token passed to the authorisation 251 * server as a hint about the end-user's current or past 252 * authenticated session with the client (optional). Should be 253 * present when {@code prompt=none} is used. 254 */ 255 private JWT idTokenHint; 256 257 258 /** 259 * Hint to the authorisation server about the login identifier 260 * the end-user may use to log in (optional). 261 */ 262 private String loginHint; 263 264 265 /** 266 * Requested Authentication Context Class Reference values 267 * (optional). 268 */ 269 private List<ACR> acrValues; 270 271 272 /** 273 * Individual claims to be returned (optional). 274 */ 275 private ClaimsRequest claims; 276 277 278 /** 279 * Request object (optional). 280 */ 281 private JWT requestObject; 282 283 284 /** 285 * Request object URI (optional). 286 */ 287 private URI requestURI; 288 289 290 /** 291 * The response mode (optional). 292 */ 293 private ResponseMode rm; 294 295 296 /** 297 * The authorisation code challenge for PKCE (optional). 298 */ 299 private CodeChallenge codeChallenge; 300 301 302 /** 303 * The authorisation code challenge method for PKCE (optional). 304 */ 305 private CodeChallengeMethod codeChallengeMethod; 306 307 308 /** 309 * The additional custom parameters. 310 */ 311 private Map<String,String> customParams = new HashMap<>(); 312 313 314 /** 315 * Creates a new OpenID Connect authentication request builder. 316 * 317 * @param rt The response type. Corresponds to the 318 * {@code response_type} parameter. Must 319 * specify a valid OpenID Connect response 320 * type. Must not be {@code null}. 321 * @param scope The request scope. Corresponds to the 322 * {@code scope} parameter. Must contain an 323 * {@link OIDCScopeValue#OPENID openid 324 * value}. Must not be {@code null}. 325 * @param clientID The client identifier. Corresponds to the 326 * {@code client_id} parameter. Must not be 327 * {@code null}. 328 * @param redirectURI The redirection URI. Corresponds to the 329 * {@code redirect_uri} parameter. Must not 330 * be {@code null} unless set by means of 331 * the optional {@code request_object} / 332 * {@code request_uri} parameter. 333 */ 334 public Builder(final ResponseType rt, 335 final Scope scope, 336 final ClientID clientID, 337 final URI redirectURI) { 338 339 if (rt == null) 340 throw new IllegalArgumentException("The response type must not be null"); 341 342 OIDCResponseTypeValidator.validate(rt); 343 344 this.rt = rt; 345 346 if (scope == null) 347 throw new IllegalArgumentException("The scope must not be null"); 348 349 if (! scope.contains(OIDCScopeValue.OPENID)) 350 throw new IllegalArgumentException("The scope must include an \"openid\" value"); 351 352 this.scope = scope; 353 354 if (clientID == null) 355 throw new IllegalArgumentException("The client ID must not be null"); 356 357 this.clientID = clientID; 358 359 // Check presence at build time 360 this.redirectURI = redirectURI; 361 } 362 363 364 /** 365 * Sets the state. Corresponds to the recommended {@code state} 366 * parameter. 367 * 368 * @param state The state, {@code null} if not specified. 369 * 370 * @return This builder. 371 */ 372 public Builder state(final State state) { 373 374 this.state = state; 375 return this; 376 } 377 378 379 /** 380 * Sets the URI of the endpoint (HTTP or HTTPS) for which the 381 * request is intended. 382 * 383 * @param uri The endpoint URI, {@code null} if not specified. 384 * 385 * @return This builder. 386 */ 387 public Builder endpointURI(final URI uri) { 388 389 this.uri = uri; 390 return this; 391 } 392 393 394 /** 395 * Sets the nonce. Corresponds to the conditionally optional 396 * {@code nonce} parameter. 397 * 398 * @param nonce The nonce, {@code null} if not specified. 399 */ 400 public Builder nonce(final Nonce nonce) { 401 402 this.nonce = nonce; 403 return this; 404 } 405 406 407 /** 408 * Sets the requested display type. Corresponds to the optional 409 * {@code display} parameter. 410 * 411 * @param display The requested display type, {@code null} if 412 * not specified. 413 */ 414 public Builder display(final Display display) { 415 416 this.display = display; 417 return this; 418 } 419 420 421 /** 422 * Sets the requested prompt. Corresponds to the optional 423 * {@code prompt} parameter. 424 * 425 * @param prompt The requested prompt, {@code null} if not 426 * specified. 427 */ 428 public Builder prompt(final Prompt prompt) { 429 430 this.prompt = prompt; 431 return this; 432 } 433 434 435 /** 436 * Sets the required maximum authentication age. Corresponds to 437 * the optional {@code max_age} parameter. 438 * 439 * @param maxAge The maximum authentication age, in seconds; 0 440 * if not specified. 441 */ 442 public Builder maxAge(final int maxAge) { 443 444 this.maxAge = maxAge; 445 return this; 446 } 447 448 449 /** 450 * Sets the end-user's preferred languages and scripts for the 451 * user interface, ordered by preference. Corresponds to the 452 * optional {@code ui_locales} parameter. 453 * 454 * @param uiLocales The preferred UI locales, {@code null} if 455 * not specified. 456 */ 457 public Builder uiLocales(final List<LangTag> uiLocales) { 458 459 this.uiLocales = uiLocales; 460 return this; 461 } 462 463 464 /** 465 * Sets the end-user's preferred languages and scripts for the 466 * claims being returned, ordered by preference. Corresponds to 467 * the optional {@code claims_locales} parameter. 468 * 469 * @param claimsLocales The preferred claims locales, 470 * {@code null} if not specified. 471 */ 472 public Builder claimsLocales(final List<LangTag> claimsLocales) { 473 474 this.claimsLocales = claimsLocales; 475 return this; 476 } 477 478 479 /** 480 * Sets the ID Token hint. Corresponds to the conditionally 481 * optional {@code id_token_hint} parameter. 482 * 483 * @param idTokenHint The ID Token hint, {@code null} if not 484 * specified. 485 */ 486 public Builder idTokenHint(final JWT idTokenHint) { 487 488 this.idTokenHint = idTokenHint; 489 return this; 490 } 491 492 493 /** 494 * Sets the login hint. Corresponds to the optional 495 * {@code login_hint} parameter. 496 * 497 * @param loginHint The login hint, {@code null} if not 498 * specified. 499 */ 500 public Builder loginHint(final String loginHint) { 501 502 this.loginHint = loginHint; 503 return this; 504 } 505 506 507 /** 508 * Sets the requested Authentication Context Class Reference 509 * values. Corresponds to the optional {@code acr_values} 510 * parameter. 511 * 512 * @param acrValues The requested ACR values, {@code null} if 513 * not specified. 514 */ 515 public Builder acrValues(final List<ACR> acrValues) { 516 517 this.acrValues = acrValues; 518 return this; 519 } 520 521 522 /** 523 * Sets the individual claims to be returned. Corresponds to 524 * the optional {@code claims} parameter. 525 * 526 * @param claims The individual claims to be returned, 527 * {@code null} if not specified. 528 */ 529 public Builder claims(final ClaimsRequest claims) { 530 531 this.claims = claims; 532 return this; 533 } 534 535 536 /** 537 * Sets the request object. Corresponds to the optional 538 * {@code request} parameter. Must not be specified together 539 * with a request object URI. 540 * 541 * @return The request object, {@code null} if not specified. 542 */ 543 public Builder requestObject(final JWT requestObject) { 544 545 this.requestObject = requestObject; 546 return this; 547 } 548 549 550 /** 551 * Sets the request object URI. Corresponds to the optional 552 * {@code request_uri} parameter. Must not be specified 553 * together with a request object. 554 * 555 * @param requestURI The request object URI, {@code null} if 556 * not specified. 557 */ 558 public Builder requestURI(final URI requestURI) { 559 560 this.requestURI = requestURI; 561 return this; 562 } 563 564 565 /** 566 * Sets the response mode. Corresponds to the optional 567 * {@code response_mode} parameter. Use of this parameter is 568 * not recommended unless a non-default response mode is 569 * requested (e.g. form_post). 570 * 571 * @param rm The response mode, {@code null} if not specified. 572 * 573 * @return This builder. 574 */ 575 public Builder responseMode(final ResponseMode rm) { 576 577 this.rm = rm; 578 return this; 579 } 580 581 582 /** 583 * Sets the code challenge for Proof Key for Code Exchange 584 * (PKCE) by public OAuth clients. 585 * 586 * @param codeChallenge The code challenge, {@code null} 587 * if not specified. 588 * @param codeChallengeMethod The code challenge method, 589 * {@code null} if not specified. 590 * 591 * @return This builder. 592 */ 593 public Builder codeChallenge(final CodeChallenge codeChallenge, final CodeChallengeMethod codeChallengeMethod) { 594 595 this.codeChallenge = codeChallenge; 596 this.codeChallengeMethod = codeChallengeMethod; 597 return this; 598 } 599 600 601 /** 602 * Sets the specified additional custom parameter. 603 * 604 * @param name The parameter name. Must not be {@code null}. 605 * @param value The parameter value, {@code null} if not 606 * specified. 607 * 608 * @return This builder. 609 */ 610 public Builder customParameter(final String name, final String value) { 611 612 customParams.put(name, value); 613 return this; 614 } 615 616 617 /** 618 * Builds a new authentication request. 619 * 620 * @return The authentication request. 621 */ 622 public AuthenticationRequest build() { 623 624 try { 625 return new AuthenticationRequest( 626 uri, rt, rm, scope, clientID, redirectURI, state, nonce, 627 display, prompt, maxAge, uiLocales, claimsLocales, 628 idTokenHint, loginHint, acrValues, claims, 629 requestObject, requestURI, 630 codeChallenge, codeChallengeMethod, 631 customParams); 632 633 } catch (IllegalArgumentException e) { 634 635 throw new IllegalStateException(e.getMessage(), e); 636 } 637 } 638 } 639 640 641 /** 642 * Creates a new minimal OpenID Connect authentication request. 643 * 644 * @param uri The URI of the OAuth 2.0 authorisation endpoint. 645 * May be {@code null} if the {@link #toHTTPRequest} 646 * method will not be used. 647 * @param rt The response type. Corresponds to the 648 * {@code response_type} parameter. Must specify a 649 * valid OpenID Connect response type. Must not be 650 * {@code null}. 651 * @param scope The request scope. Corresponds to the 652 * {@code scope} parameter. Must contain an 653 * {@link OIDCScopeValue#OPENID openid value}. Must 654 * not be {@code null}. 655 * @param clientID The client identifier. Corresponds to the 656 * {@code client_id} parameter. Must not be 657 * {@code null}. 658 * @param redirectURI The redirection URI. Corresponds to the 659 * {@code redirect_uri} parameter. Must not be 660 * {@code null}. 661 * @param state The state. Corresponds to the {@code state} 662 * parameter. May be {@code null}. 663 * @param nonce The nonce. Corresponds to the {@code nonce} 664 * parameter. May be {@code null} for code flow. 665 */ 666 public AuthenticationRequest(final URI uri, 667 final ResponseType rt, 668 final Scope scope, 669 final ClientID clientID, 670 final URI redirectURI, 671 final State state, 672 final Nonce nonce) { 673 674 // Not specified: display, prompt, maxAge, uiLocales, claimsLocales, 675 // idTokenHint, loginHint, acrValues, claims 676 // codeChallenge, codeChallengeMethod 677 this(uri, rt, null, scope, clientID, redirectURI, state, nonce, 678 null, null, 0, null, null, 679 null, null, null, null, null, null, 680 null, null); 681 } 682 683 684 /** 685 * Creates a new OpenID Connect authentication request. 686 * 687 * @param uri The URI of the OAuth 2.0 authorisation 688 * endpoint. May be {@code null} if the 689 * {@link #toHTTPRequest} method will not be 690 * used. 691 * @param rt The response type set. Corresponds to the 692 * {@code response_type} parameter. Must 693 * specify a valid OpenID Connect response 694 * type. Must not be {@code null}. 695 * @param rm The response mode. Corresponds to the 696 * optional {@code response_mode} parameter. 697 * Use of this parameter is not recommended 698 * unless a non-default response mode is 699 * requested (e.g. form_post). 700 * @param scope The request scope. Corresponds to the 701 * {@code scope} parameter. Must contain an 702 * {@link OIDCScopeValue#OPENID openid value}. 703 * Must not be {@code null}. 704 * @param clientID The client identifier. Corresponds to the 705 * {@code client_id} parameter. Must not be 706 * {@code null}. 707 * @param redirectURI The redirection URI. Corresponds to the 708 * {@code redirect_uri} parameter. Must not 709 * be {@code null} unless set by means of 710 * the optional {@code request_object} / 711 * {@code request_uri} parameter. 712 * @param state The state. Corresponds to the recommended 713 * {@code state} parameter. {@code null} if 714 * not specified. 715 * @param nonce The nonce. Corresponds to the 716 * {@code nonce} parameter. May be 717 * {@code null} for code flow. 718 * @param display The requested display type. Corresponds 719 * to the optional {@code display} 720 * parameter. 721 * {@code null} if not specified. 722 * @param prompt The requested prompt. Corresponds to the 723 * optional {@code prompt} parameter. 724 * {@code null} if not specified. 725 * @param maxAge The required maximum authentication age, 726 * in seconds. Corresponds to the optional 727 * {@code max_age} parameter. Zero if not 728 * specified. 729 * @param uiLocales The preferred languages and scripts for 730 * the user interface. Corresponds to the 731 * optional {@code ui_locales} parameter. 732 * {@code null} if not specified. 733 * @param claimsLocales The preferred languages and scripts for 734 * claims being returned. Corresponds to the 735 * optional {@code claims_locales} 736 * parameter. {@code null} if not specified. 737 * @param idTokenHint The ID Token hint. Corresponds to the 738 * optional {@code id_token_hint} parameter. 739 * {@code null} if not specified. 740 * @param loginHint The login hint. Corresponds to the 741 * optional {@code login_hint} parameter. 742 * {@code null} if not specified. 743 * @param acrValues The requested Authentication Context 744 * Class Reference values. Corresponds to 745 * the optional {@code acr_values} 746 * parameter. {@code null} if not specified. 747 * @param claims The individual claims to be returned. 748 * Corresponds to the optional 749 * {@code claims} parameter. {@code null} if 750 * not specified. 751 * @param requestObject The request object. Corresponds to the 752 * optional {@code request} parameter. Must 753 * not be specified together with a request 754 * object URI. {@code null} if not 755 * specified. 756 * @param requestURI The request object URI. Corresponds to 757 * the optional {@code request_uri} 758 * parameter. Must not be specified together 759 * with a request object. {@code null} if 760 * not specified. 761 * @param codeChallenge The code challenge for PKCE, {@code null} 762 * if not specified. 763 * @param codeChallengeMethod The code challenge method for PKCE, 764 * {@code null} if not specified. 765 */ 766 public AuthenticationRequest(final URI uri, 767 final ResponseType rt, 768 final ResponseMode rm, 769 final Scope scope, 770 final ClientID clientID, 771 final URI redirectURI, 772 final State state, 773 final Nonce nonce, 774 final Display display, 775 final Prompt prompt, 776 final int maxAge, 777 final List<LangTag> uiLocales, 778 final List<LangTag> claimsLocales, 779 final JWT idTokenHint, 780 final String loginHint, 781 final List<ACR> acrValues, 782 final ClaimsRequest claims, 783 final JWT requestObject, 784 final URI requestURI, 785 final CodeChallenge codeChallenge, 786 final CodeChallengeMethod codeChallengeMethod) { 787 788 this(uri, rt, rm, scope, clientID, redirectURI, state, 789 nonce, display, prompt, maxAge, uiLocales, claimsLocales, 790 idTokenHint, loginHint, acrValues, claims, 791 requestObject, requestURI, codeChallenge, codeChallengeMethod, 792 Collections.<String, String>emptyMap()); 793 } 794 795 796 /** 797 * Creates a new OpenID Connect authentication request with additional 798 * custom parameters. 799 * 800 * @param uri The URI of the OAuth 2.0 authorisation 801 * endpoint. May be {@code null} if the 802 * {@link #toHTTPRequest} method will not be 803 * used. 804 * @param rt The response type set. Corresponds to the 805 * {@code response_type} parameter. Must 806 * specify a valid OpenID Connect response 807 * type. Must not be {@code null}. 808 * @param rm The response mode. Corresponds to the 809 * optional {@code response_mode} parameter. 810 * Use of this parameter is not recommended 811 * unless a non-default response mode is 812 * requested (e.g. form_post). 813 * @param scope The request scope. Corresponds to the 814 * {@code scope} parameter. Must contain an 815 * {@link OIDCScopeValue#OPENID openid value}. 816 * Must not be {@code null}. 817 * @param clientID The client identifier. Corresponds to the 818 * {@code client_id} parameter. Must not be 819 * {@code null}. 820 * @param redirectURI The redirection URI. Corresponds to the 821 * {@code redirect_uri} parameter. Must not 822 * be {@code null} unless set by means of 823 * the optional {@code request_object} / 824 * {@code request_uri} parameter. 825 * @param state The state. Corresponds to the recommended 826 * {@code state} parameter. {@code null} if 827 * not specified. 828 * @param nonce The nonce. Corresponds to the 829 * {@code nonce} parameter. May be 830 * {@code null} for code flow. 831 * @param display The requested display type. Corresponds 832 * to the optional {@code display} 833 * parameter. 834 * {@code null} if not specified. 835 * @param prompt The requested prompt. Corresponds to the 836 * optional {@code prompt} parameter. 837 * {@code null} if not specified. 838 * @param maxAge The required maximum authentication age, 839 * in seconds. Corresponds to the optional 840 * {@code max_age} parameter. Zero if not 841 * specified. 842 * @param uiLocales The preferred languages and scripts for 843 * the user interface. Corresponds to the 844 * optional {@code ui_locales} parameter. 845 * {@code null} if not specified. 846 * @param claimsLocales The preferred languages and scripts for 847 * claims being returned. Corresponds to the 848 * optional {@code claims_locales} 849 * parameter. {@code null} if not specified. 850 * @param idTokenHint The ID Token hint. Corresponds to the 851 * optional {@code id_token_hint} parameter. 852 * {@code null} if not specified. 853 * @param loginHint The login hint. Corresponds to the 854 * optional {@code login_hint} parameter. 855 * {@code null} if not specified. 856 * @param acrValues The requested Authentication Context 857 * Class Reference values. Corresponds to 858 * the optional {@code acr_values} 859 * parameter. {@code null} if not specified. 860 * @param claims The individual claims to be returned. 861 * Corresponds to the optional 862 * {@code claims} parameter. {@code null} if 863 * not specified. 864 * @param requestObject The request object. Corresponds to the 865 * optional {@code request} parameter. Must 866 * not be specified together with a request 867 * object URI. {@code null} if not 868 * specified. 869 * @param requestURI The request object URI. Corresponds to 870 * the optional {@code request_uri} 871 * parameter. Must not be specified together 872 * with a request object. {@code null} if 873 * not specified. 874 * @param codeChallenge The code challenge for PKCE, {@code null} 875 * if not specified. 876 * @param codeChallengeMethod The code challenge method for PKCE, 877 * {@code null} if not specified. 878 * @param customParams Additional custom parameters, empty map 879 * or {@code null} if none. 880 */ 881 public AuthenticationRequest(final URI uri, 882 final ResponseType rt, 883 final ResponseMode rm, 884 final Scope scope, 885 final ClientID clientID, 886 final URI redirectURI, 887 final State state, 888 final Nonce nonce, 889 final Display display, 890 final Prompt prompt, 891 final int maxAge, 892 final List<LangTag> uiLocales, 893 final List<LangTag> claimsLocales, 894 final JWT idTokenHint, 895 final String loginHint, 896 final List<ACR> acrValues, 897 final ClaimsRequest claims, 898 final JWT requestObject, 899 final URI requestURI, 900 final CodeChallenge codeChallenge, 901 final CodeChallengeMethod codeChallengeMethod, 902 final Map<String,String> customParams) { 903 904 super(uri, rt, rm, clientID, redirectURI, scope, state, codeChallenge, codeChallengeMethod, customParams); 905 906 // Redirect URI required unless set in request_object / request_uri 907 if (redirectURI == null && requestObject == null && requestURI == null) 908 throw new IllegalArgumentException("The redirection URI must not be null"); 909 910 OIDCResponseTypeValidator.validate(rt); 911 912 if (scope == null) 913 throw new IllegalArgumentException("The scope must not be null"); 914 915 if (! scope.contains(OIDCScopeValue.OPENID)) 916 throw new IllegalArgumentException("The scope must include an \"openid\" token"); 917 918 919 // Nonce required for implicit protocol flow 920 if (rt.impliesImplicitFlow() && nonce == null) 921 throw new IllegalArgumentException("Nonce is required in implicit / hybrid protocol flow"); 922 923 this.nonce = nonce; 924 925 // Optional parameters 926 this.display = display; 927 this.prompt = prompt; 928 this.maxAge = maxAge; 929 930 if (uiLocales != null) 931 this.uiLocales = Collections.unmodifiableList(uiLocales); 932 else 933 this.uiLocales = null; 934 935 if (claimsLocales != null) 936 this.claimsLocales = Collections.unmodifiableList(claimsLocales); 937 else 938 this.claimsLocales = null; 939 940 this.idTokenHint = idTokenHint; 941 this.loginHint = loginHint; 942 943 if (acrValues != null) 944 this.acrValues = Collections.unmodifiableList(acrValues); 945 else 946 this.acrValues = null; 947 948 this.claims = claims; 949 950 if (requestObject != null && requestURI != null) 951 throw new IllegalArgumentException("Either a request object or a request URI must be specified, but not both"); 952 953 this.requestObject = requestObject; 954 this.requestURI = requestURI; 955 } 956 957 958 /** 959 * Returns the registered (standard) OpenID Connect authentication 960 * request parameter names. 961 * 962 * @return The registered OpenID Connect authentication request 963 * parameter names, as a unmodifiable set. 964 */ 965 public static Set<String> getRegisteredParameterNames() { 966 967 return REGISTERED_PARAMETER_NAMES; 968 } 969 970 971 /** 972 * Gets the nonce. Corresponds to the conditionally optional 973 * {@code nonce} parameter. 974 * 975 * @return The nonce, {@code null} if not specified. 976 */ 977 public Nonce getNonce() { 978 979 return nonce; 980 } 981 982 983 /** 984 * Gets the requested display type. Corresponds to the optional 985 * {@code display} parameter. 986 * 987 * @return The requested display type, {@code null} if not specified. 988 */ 989 public Display getDisplay() { 990 991 return display; 992 } 993 994 995 /** 996 * Gets the requested prompt. Corresponds to the optional 997 * {@code prompt} parameter. 998 * 999 * @return The requested prompt, {@code null} if not specified. 1000 */ 1001 public Prompt getPrompt() { 1002 1003 return prompt; 1004 } 1005 1006 1007 /** 1008 * Gets the required maximum authentication age. Corresponds to the 1009 * optional {@code max_age} parameter. 1010 * 1011 * @return The maximum authentication age, in seconds; 0 if not 1012 * specified. 1013 */ 1014 public int getMaxAge() { 1015 1016 return maxAge; 1017 } 1018 1019 1020 /** 1021 * Gets the end-user's preferred languages and scripts for the user 1022 * interface, ordered by preference. Corresponds to the optional 1023 * {@code ui_locales} parameter. 1024 * 1025 * @return The preferred UI locales, {@code null} if not specified. 1026 */ 1027 public List<LangTag> getUILocales() { 1028 1029 return uiLocales; 1030 } 1031 1032 1033 /** 1034 * Gets the end-user's preferred languages and scripts for the claims 1035 * being returned, ordered by preference. Corresponds to the optional 1036 * {@code claims_locales} parameter. 1037 * 1038 * @return The preferred claims locales, {@code null} if not specified. 1039 */ 1040 public List<LangTag> getClaimsLocales() { 1041 1042 return claimsLocales; 1043 } 1044 1045 1046 /** 1047 * Gets the ID Token hint. Corresponds to the conditionally optional 1048 * {@code id_token_hint} parameter. 1049 * 1050 * @return The ID Token hint, {@code null} if not specified. 1051 */ 1052 public JWT getIDTokenHint() { 1053 1054 return idTokenHint; 1055 } 1056 1057 1058 /** 1059 * Gets the login hint. Corresponds to the optional {@code login_hint} 1060 * parameter. 1061 * 1062 * @return The login hint, {@code null} if not specified. 1063 */ 1064 public String getLoginHint() { 1065 1066 return loginHint; 1067 } 1068 1069 1070 /** 1071 * Gets the requested Authentication Context Class Reference values. 1072 * Corresponds to the optional {@code acr_values} parameter. 1073 * 1074 * @return The requested ACR values, {@code null} if not specified. 1075 */ 1076 public List<ACR> getACRValues() { 1077 1078 return acrValues; 1079 } 1080 1081 1082 /** 1083 * Gets the individual claims to be returned. Corresponds to the 1084 * optional {@code claims} parameter. 1085 * 1086 * @return The individual claims to be returned, {@code null} if not 1087 * specified. 1088 */ 1089 public ClaimsRequest getClaims() { 1090 1091 return claims; 1092 } 1093 1094 1095 /** 1096 * Gets the request object. Corresponds to the optional {@code request} 1097 * parameter. 1098 * 1099 * @return The request object, {@code null} if not specified. 1100 */ 1101 public JWT getRequestObject() { 1102 1103 return requestObject; 1104 } 1105 1106 1107 /** 1108 * Gets the request object URI. Corresponds to the optional 1109 * {@code request_uri} parameter. 1110 * 1111 * @return The request object URI, {@code null} if not specified. 1112 */ 1113 public URI getRequestURI() { 1114 1115 return requestURI; 1116 } 1117 1118 1119 /** 1120 * Returns {@code true} if this authentication request specifies an 1121 * OpenID Connect request object (directly through the {@code request} 1122 * parameter or by reference through the {@code request_uri} parameter). 1123 * 1124 * @return {@code true} if a request object is specified, else 1125 * {@code false}. 1126 */ 1127 public boolean specifiesRequestObject() { 1128 1129 return requestObject != null || requestURI != null; 1130 } 1131 1132 1133 @Override 1134 public Map<String,String> toParameters() { 1135 1136 Map <String,String> params = super.toParameters(); 1137 1138 if (nonce != null) 1139 params.put("nonce", nonce.toString()); 1140 1141 if (display != null) 1142 params.put("display", display.toString()); 1143 1144 if (prompt != null) 1145 params.put("prompt", prompt.toString()); 1146 1147 if (maxAge > 0) 1148 params.put("max_age", "" + maxAge); 1149 1150 if (uiLocales != null) { 1151 1152 StringBuilder sb = new StringBuilder(); 1153 1154 for (LangTag locale: uiLocales) { 1155 1156 if (sb.length() > 0) 1157 sb.append(' '); 1158 1159 sb.append(locale.toString()); 1160 } 1161 1162 params.put("ui_locales", sb.toString()); 1163 } 1164 1165 if (claimsLocales != null) { 1166 1167 StringBuilder sb = new StringBuilder(); 1168 1169 for (LangTag locale: claimsLocales) { 1170 1171 if (sb.length() > 0) 1172 sb.append(' '); 1173 1174 sb.append(locale.toString()); 1175 } 1176 1177 params.put("claims_locales", sb.toString()); 1178 } 1179 1180 if (idTokenHint != null) { 1181 1182 try { 1183 params.put("id_token_hint", idTokenHint.serialize()); 1184 1185 } catch (IllegalStateException e) { 1186 1187 throw new SerializeException("Couldn't serialize ID token hint: " + e.getMessage(), e); 1188 } 1189 } 1190 1191 if (loginHint != null) 1192 params.put("login_hint", loginHint); 1193 1194 if (acrValues != null) { 1195 1196 StringBuilder sb = new StringBuilder(); 1197 1198 for (ACR acr: acrValues) { 1199 1200 if (sb.length() > 0) 1201 sb.append(' '); 1202 1203 sb.append(acr.toString()); 1204 } 1205 1206 params.put("acr_values", sb.toString()); 1207 } 1208 1209 1210 if (claims != null) 1211 params.put("claims", claims.toJSONObject().toString()); 1212 1213 if (requestObject != null) { 1214 1215 try { 1216 params.put("request", requestObject.serialize()); 1217 1218 } catch (IllegalStateException e) { 1219 1220 throw new SerializeException("Couldn't serialize request object to JWT: " + e.getMessage(), e); 1221 } 1222 } 1223 1224 if (requestURI != null) 1225 params.put("request_uri", requestURI.toString()); 1226 1227 return params; 1228 } 1229 1230 1231 /** 1232 * Parses an OpenID Connect authentication request from the specified 1233 * parameters. 1234 * 1235 * <p>Example parameters: 1236 * 1237 * <pre> 1238 * response_type = token id_token 1239 * client_id = s6BhdRkqt3 1240 * redirect_uri = https://client.example.com/cb 1241 * scope = openid profile 1242 * state = af0ifjsldkj 1243 * nonce = -0S6_WzA2Mj 1244 * </pre> 1245 * 1246 * @param params The parameters. Must not be {@code null}. 1247 * 1248 * @return The OpenID Connect authentication request. 1249 * 1250 * @throws ParseException If the parameters couldn't be parsed to an 1251 * OpenID Connect authentication request. 1252 */ 1253 public static AuthenticationRequest parse(final Map<String,String> params) 1254 throws ParseException { 1255 1256 return parse(null, params); 1257 } 1258 1259 1260 /** 1261 * Parses an OpenID Connect authentication request from the specified 1262 * parameters. 1263 * 1264 * <p>Example parameters: 1265 * 1266 * <pre> 1267 * response_type = token id_token 1268 * client_id = s6BhdRkqt3 1269 * redirect_uri = https://client.example.com/cb 1270 * scope = openid profile 1271 * state = af0ifjsldkj 1272 * nonce = -0S6_WzA2Mj 1273 * </pre> 1274 * 1275 * @param uri The URI of the OAuth 2.0 authorisation endpoint. May 1276 * be {@code null} if the {@link #toHTTPRequest} method 1277 * will not be used. 1278 * @param params The parameters. Must not be {@code null}. 1279 * 1280 * @return The OpenID Connect authentication request. 1281 * 1282 * @throws ParseException If the parameters couldn't be parsed to an 1283 * OpenID Connect authentication request. 1284 */ 1285 public static AuthenticationRequest parse(final URI uri, final Map<String,String> params) 1286 throws ParseException { 1287 1288 // Parse and validate the core OAuth 2.0 autz request params in 1289 // the context of OIDC 1290 AuthorizationRequest ar = AuthorizationRequest.parse(uri, params); 1291 1292 ClientID clientID = ar.getClientID(); 1293 State state = ar.getState(); 1294 ResponseMode rm = ar.getResponseMode(); 1295 1296 // Required in OIDC, check later after optional request_object / request_uri is parsed 1297 URI redirectURI = ar.getRedirectionURI(); 1298 1299 ResponseType rt = ar.getResponseType(); 1300 1301 try { 1302 OIDCResponseTypeValidator.validate(rt); 1303 1304 } catch (IllegalArgumentException e) { 1305 String msg = "Unsupported \"response_type\" parameter: " + e.getMessage(); 1306 throw new ParseException(msg, OAuth2Error.UNSUPPORTED_RESPONSE_TYPE.appendDescription(": " + msg), 1307 clientID, redirectURI, ar.impliedResponseMode(), state); 1308 } 1309 1310 // Required in OIDC, must include "openid" parameter 1311 Scope scope = ar.getScope(); 1312 1313 if (scope == null) { 1314 String msg = "Missing \"scope\" parameter"; 1315 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1316 clientID, redirectURI, ar.impliedResponseMode(), state); 1317 } 1318 1319 if (! scope.contains(OIDCScopeValue.OPENID)) { 1320 String msg = "The scope must include an \"openid\" value"; 1321 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1322 clientID, redirectURI, ar.impliedResponseMode(), state); 1323 } 1324 1325 1326 // Parse the remaining OIDC parameters 1327 Nonce nonce = Nonce.parse(params.get("nonce")); 1328 1329 // Nonce required in implicit flow 1330 if (rt.impliesImplicitFlow() && nonce == null) { 1331 String msg = "Missing \"nonce\" parameter: Required in implicit flow"; 1332 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1333 clientID, redirectURI, ar.impliedResponseMode(), state); 1334 } 1335 1336 Display display = null; 1337 1338 if (params.containsKey("display")) { 1339 try { 1340 display = Display.parse(params.get("display")); 1341 1342 } catch (ParseException e) { 1343 String msg = "Invalid \"display\" parameter: " + e.getMessage(); 1344 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1345 clientID, redirectURI, ar.impliedResponseMode(), state, e); 1346 } 1347 } 1348 1349 1350 Prompt prompt; 1351 1352 try { 1353 prompt = Prompt.parse(params.get("prompt")); 1354 1355 } catch (ParseException e) { 1356 String msg = "Invalid \"prompt\" parameter: " + e.getMessage(); 1357 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1358 clientID, redirectURI, ar.impliedResponseMode(), state, e); 1359 } 1360 1361 1362 String v = params.get("max_age"); 1363 1364 int maxAge = 0; 1365 1366 if (StringUtils.isNotBlank(v)) { 1367 1368 try { 1369 maxAge = Integer.parseInt(v); 1370 1371 } catch (NumberFormatException e) { 1372 String msg = "Invalid \"max_age\" parameter: " + v; 1373 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1374 clientID, redirectURI, ar.impliedResponseMode(), state, e); 1375 } 1376 } 1377 1378 1379 v = params.get("ui_locales"); 1380 1381 List<LangTag> uiLocales = null; 1382 1383 if (StringUtils.isNotBlank(v)) { 1384 1385 uiLocales = new LinkedList<>(); 1386 1387 StringTokenizer st = new StringTokenizer(v, " "); 1388 1389 while (st.hasMoreTokens()) { 1390 1391 try { 1392 uiLocales.add(LangTag.parse(st.nextToken())); 1393 1394 } catch (LangTagException e) { 1395 String msg = "Invalid \"ui_locales\" parameter: " + e.getMessage(); 1396 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1397 clientID, redirectURI, ar.impliedResponseMode(), state, e); 1398 } 1399 } 1400 } 1401 1402 1403 v = params.get("claims_locales"); 1404 1405 List<LangTag> claimsLocales = null; 1406 1407 if (StringUtils.isNotBlank(v)) { 1408 1409 claimsLocales = new LinkedList<>(); 1410 1411 StringTokenizer st = new StringTokenizer(v, " "); 1412 1413 while (st.hasMoreTokens()) { 1414 1415 try { 1416 claimsLocales.add(LangTag.parse(st.nextToken())); 1417 1418 } catch (LangTagException e) { 1419 String msg = "Invalid \"claims_locales\" parameter: " + e.getMessage(); 1420 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1421 clientID, redirectURI, ar.impliedResponseMode(), state, e); 1422 } 1423 } 1424 } 1425 1426 1427 v = params.get("id_token_hint"); 1428 1429 JWT idTokenHint = null; 1430 1431 if (StringUtils.isNotBlank(v)) { 1432 1433 try { 1434 idTokenHint = JWTParser.parse(v); 1435 1436 } catch (java.text.ParseException e) { 1437 String msg = "Invalid \"id_token_hint\" parameter: " + e.getMessage(); 1438 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1439 clientID, redirectURI, ar.impliedResponseMode(), state, e); 1440 } 1441 } 1442 1443 String loginHint = params.get("login_hint"); 1444 1445 1446 v = params.get("acr_values"); 1447 1448 List<ACR> acrValues = null; 1449 1450 if (StringUtils.isNotBlank(v)) { 1451 1452 acrValues = new LinkedList<>(); 1453 1454 StringTokenizer st = new StringTokenizer(v, " "); 1455 1456 while (st.hasMoreTokens()) { 1457 1458 acrValues.add(new ACR(st.nextToken())); 1459 } 1460 } 1461 1462 1463 v = params.get("claims"); 1464 1465 ClaimsRequest claims = null; 1466 1467 if (StringUtils.isNotBlank(v)) { 1468 1469 JSONObject jsonObject; 1470 1471 try { 1472 jsonObject = JSONObjectUtils.parse(v); 1473 1474 } catch (ParseException e) { 1475 String msg = "Invalid \"claims\" parameter: " + e.getMessage(); 1476 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1477 clientID, redirectURI, ar.impliedResponseMode(), state, e); 1478 } 1479 1480 // Parse exceptions silently ignored 1481 claims = ClaimsRequest.parse(jsonObject); 1482 } 1483 1484 1485 v = params.get("request_uri"); 1486 1487 URI requestURI = null; 1488 1489 if (StringUtils.isNotBlank(v)) { 1490 1491 try { 1492 requestURI = new URI(v); 1493 1494 } catch (URISyntaxException e) { 1495 String msg = "Invalid \"request_uri\" parameter: " + e.getMessage(); 1496 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1497 clientID, redirectURI, ar.impliedResponseMode(), state, e); 1498 } 1499 } 1500 1501 v = params.get("request"); 1502 1503 JWT requestObject = null; 1504 1505 if (StringUtils.isNotBlank(v)) { 1506 1507 // request_object and request_uri must not be defined at the same time 1508 if (requestURI != null) { 1509 String msg = "Invalid request: Found mutually exclusive \"request\" and \"request_uri\" parameters"; 1510 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1511 clientID, redirectURI, ar.impliedResponseMode(), state, null); 1512 } 1513 1514 try { 1515 requestObject = JWTParser.parse(v); 1516 1517 } catch (java.text.ParseException e) { 1518 String msg = "Invalid \"request_object\" parameter: " + e.getMessage(); 1519 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1520 clientID, redirectURI, ar.impliedResponseMode(), state, e); 1521 } 1522 } 1523 1524 1525 // Redirect URI required unless request_object / request_uri present 1526 if (redirectURI == null && requestObject == null && requestURI == null) { 1527 String msg = "Missing \"redirect_uri\" parameter"; 1528 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1529 clientID, null, ar.impliedResponseMode(), state); 1530 } 1531 1532 // Parse additional custom parameters 1533 Map<String,String> customParams = null; 1534 1535 for (Map.Entry<String,String> p: params.entrySet()) { 1536 1537 if (! REGISTERED_PARAMETER_NAMES.contains(p.getKey())) { 1538 // We have a custom parameter 1539 if (customParams == null) { 1540 customParams = new HashMap<>(); 1541 } 1542 customParams.put(p.getKey(), p.getValue()); 1543 } 1544 } 1545 1546 1547 return new AuthenticationRequest( 1548 uri, rt, rm, scope, clientID, redirectURI, state, nonce, 1549 display, prompt, maxAge, uiLocales, claimsLocales, 1550 idTokenHint, loginHint, acrValues, claims, requestObject, requestURI, 1551 ar.getCodeChallenge(), ar.getCodeChallengeMethod(), 1552 customParams); 1553 } 1554 1555 1556 /** 1557 * Parses an OpenID Connect authentication request from the specified 1558 * URI query string. 1559 * 1560 * <p>Example URI query string: 1561 * 1562 * <pre> 1563 * response_type=token%20id_token 1564 * &client_id=s6BhdRkqt3 1565 * &redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb 1566 * &scope=openid%20profile 1567 * &state=af0ifjsldkj 1568 * &nonce=n-0S6_WzA2Mj 1569 * </pre> 1570 * 1571 * @param query The URI query string. Must not be {@code null}. 1572 * 1573 * @return The OpenID Connect authentication request. 1574 * 1575 * @throws ParseException If the query string couldn't be parsed to an 1576 * OpenID Connect authentication request. 1577 */ 1578 public static AuthenticationRequest parse(final String query) 1579 throws ParseException { 1580 1581 return parse(null, URLUtils.parseParameters(query)); 1582 } 1583 1584 1585 /** 1586 * Parses an OpenID Connect authentication request from the specified 1587 * URI query string. 1588 * 1589 * <p>Example URI query string: 1590 * 1591 * <pre> 1592 * response_type=token%20id_token 1593 * &client_id=s6BhdRkqt3 1594 * &redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb 1595 * &scope=openid%20profile 1596 * &state=af0ifjsldkj 1597 * &nonce=n-0S6_WzA2Mj 1598 * </pre> 1599 * 1600 * @param uri The URI of the OAuth 2.0 authorisation endpoint. May be 1601 * {@code null} if the {@link #toHTTPRequest} method will 1602 * not be used. 1603 * @param query The URI query string. Must not be {@code null}. 1604 * 1605 * @return The OpenID Connect authentication request. 1606 * 1607 * @throws ParseException If the query string couldn't be parsed to an 1608 * OpenID Connect authentication request. 1609 */ 1610 public static AuthenticationRequest parse(final URI uri, final String query) 1611 throws ParseException { 1612 1613 return parse(uri, URLUtils.parseParameters(query)); 1614 } 1615 1616 1617 /** 1618 * Parses an OpenID Connect authentication request from the specified 1619 * URI. 1620 * 1621 * <p>Example URI: 1622 * 1623 * <pre> 1624 * https://server.example.com/authorize? 1625 * response_type=token%20id_token 1626 * &client_id=s6BhdRkqt3 1627 * &redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb 1628 * &scope=openid%20profile 1629 * &state=af0ifjsldkj 1630 * &nonce=n-0S6_WzA2Mj 1631 * </pre> 1632 * 1633 * @param uri The URI. Must not be {@code null}. 1634 * 1635 * @return The OpenID Connect authentication request. 1636 * 1637 * @throws ParseException If the query string couldn't be parsed to an 1638 * OpenID Connect authentication request. 1639 */ 1640 public static AuthenticationRequest parse(final URI uri) 1641 throws ParseException { 1642 1643 return parse(URIUtils.getBaseURI(uri), URLUtils.parseParameters(uri.getRawQuery())); 1644 } 1645 1646 1647 /** 1648 * Parses an authentication request from the specified HTTP GET or HTTP 1649 * POST request. 1650 * 1651 * <p>Example HTTP request (GET): 1652 * 1653 * <pre> 1654 * https://server.example.com/op/authorize? 1655 * response_type=code%20id_token 1656 * &client_id=s6BhdRkqt3 1657 * &redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb 1658 * &scope=openid 1659 * &nonce=n-0S6_WzA2Mj 1660 * &state=af0ifjsldkj 1661 * </pre> 1662 * 1663 * @param httpRequest The HTTP request. Must not be {@code null}. 1664 * 1665 * @return The OpenID Connect authentication request. 1666 * 1667 * @throws ParseException If the HTTP request couldn't be parsed to an 1668 * OpenID Connect authentication request. 1669 */ 1670 public static AuthenticationRequest parse(final HTTPRequest httpRequest) 1671 throws ParseException { 1672 1673 String query = httpRequest.getQuery(); 1674 1675 if (query == null) 1676 throw new ParseException("Missing URI query string"); 1677 1678 URI endpointURI; 1679 1680 try { 1681 endpointURI = httpRequest.getURL().toURI(); 1682 1683 } catch (URISyntaxException e) { 1684 1685 throw new ParseException(e.getMessage(), e); 1686 } 1687 1688 return parse(endpointURI, query); 1689 } 1690}