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, -1 if not 106 * specified, zero implies prompt=login (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, -1 if 230 * not specified, zero implies prompt=login (optional). 231 */ 232 private int maxAge = -1; 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, -1, 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. -1 if not 841 * specified, zero implies 842 * {@code prompt=login}. 843 * @param uiLocales The preferred languages and scripts for 844 * the user interface. Corresponds to the 845 * optional {@code ui_locales} parameter. 846 * {@code null} if not specified. 847 * @param claimsLocales The preferred languages and scripts for 848 * claims being returned. Corresponds to the 849 * optional {@code claims_locales} 850 * parameter. {@code null} if not specified. 851 * @param idTokenHint The ID Token hint. Corresponds to the 852 * optional {@code id_token_hint} parameter. 853 * {@code null} if not specified. 854 * @param loginHint The login hint. Corresponds to the 855 * optional {@code login_hint} parameter. 856 * {@code null} if not specified. 857 * @param acrValues The requested Authentication Context 858 * Class Reference values. Corresponds to 859 * the optional {@code acr_values} 860 * parameter. {@code null} if not specified. 861 * @param claims The individual claims to be returned. 862 * Corresponds to the optional 863 * {@code claims} parameter. {@code null} if 864 * not specified. 865 * @param requestObject The request object. Corresponds to the 866 * optional {@code request} parameter. Must 867 * not be specified together with a request 868 * object URI. {@code null} if not 869 * specified. 870 * @param requestURI The request object URI. Corresponds to 871 * the optional {@code request_uri} 872 * parameter. Must not be specified together 873 * with a request object. {@code null} if 874 * not specified. 875 * @param codeChallenge The code challenge for PKCE, {@code null} 876 * if not specified. 877 * @param codeChallengeMethod The code challenge method for PKCE, 878 * {@code null} if not specified. 879 * @param customParams Additional custom parameters, empty map 880 * or {@code null} if none. 881 */ 882 public AuthenticationRequest(final URI uri, 883 final ResponseType rt, 884 final ResponseMode rm, 885 final Scope scope, 886 final ClientID clientID, 887 final URI redirectURI, 888 final State state, 889 final Nonce nonce, 890 final Display display, 891 final Prompt prompt, 892 final int maxAge, 893 final List<LangTag> uiLocales, 894 final List<LangTag> claimsLocales, 895 final JWT idTokenHint, 896 final String loginHint, 897 final List<ACR> acrValues, 898 final ClaimsRequest claims, 899 final JWT requestObject, 900 final URI requestURI, 901 final CodeChallenge codeChallenge, 902 final CodeChallengeMethod codeChallengeMethod, 903 final Map<String,String> customParams) { 904 905 super(uri, rt, rm, clientID, redirectURI, scope, state, codeChallenge, codeChallengeMethod, customParams); 906 907 // Redirect URI required unless set in request_object / request_uri 908 if (redirectURI == null && requestObject == null && requestURI == null) 909 throw new IllegalArgumentException("The redirection URI must not be null"); 910 911 OIDCResponseTypeValidator.validate(rt); 912 913 if (scope == null) 914 throw new IllegalArgumentException("The scope must not be null"); 915 916 if (! scope.contains(OIDCScopeValue.OPENID)) 917 throw new IllegalArgumentException("The scope must include an \"openid\" token"); 918 919 920 // Nonce required for implicit protocol flow 921 if (rt.impliesImplicitFlow() && nonce == null) 922 throw new IllegalArgumentException("Nonce is required in implicit / hybrid protocol flow"); 923 924 this.nonce = nonce; 925 926 // Optional parameters 927 this.display = display; 928 this.prompt = prompt; 929 this.maxAge = maxAge; 930 931 if (uiLocales != null) 932 this.uiLocales = Collections.unmodifiableList(uiLocales); 933 else 934 this.uiLocales = null; 935 936 if (claimsLocales != null) 937 this.claimsLocales = Collections.unmodifiableList(claimsLocales); 938 else 939 this.claimsLocales = null; 940 941 this.idTokenHint = idTokenHint; 942 this.loginHint = loginHint; 943 944 if (acrValues != null) 945 this.acrValues = Collections.unmodifiableList(acrValues); 946 else 947 this.acrValues = null; 948 949 this.claims = claims; 950 951 if (requestObject != null && requestURI != null) 952 throw new IllegalArgumentException("Either a request object or a request URI must be specified, but not both"); 953 954 this.requestObject = requestObject; 955 this.requestURI = requestURI; 956 } 957 958 959 /** 960 * Returns the registered (standard) OpenID Connect authentication 961 * request parameter names. 962 * 963 * @return The registered OpenID Connect authentication request 964 * parameter names, as a unmodifiable set. 965 */ 966 public static Set<String> getRegisteredParameterNames() { 967 968 return REGISTERED_PARAMETER_NAMES; 969 } 970 971 972 /** 973 * Gets the nonce. Corresponds to the conditionally optional 974 * {@code nonce} parameter. 975 * 976 * @return The nonce, {@code null} if not specified. 977 */ 978 public Nonce getNonce() { 979 980 return nonce; 981 } 982 983 984 /** 985 * Gets the requested display type. Corresponds to the optional 986 * {@code display} parameter. 987 * 988 * @return The requested display type, {@code null} if not specified. 989 */ 990 public Display getDisplay() { 991 992 return display; 993 } 994 995 996 /** 997 * Gets the requested prompt. Corresponds to the optional 998 * {@code prompt} parameter. 999 * 1000 * @return The requested prompt, {@code null} if not specified. 1001 */ 1002 public Prompt getPrompt() { 1003 1004 return prompt; 1005 } 1006 1007 1008 /** 1009 * Gets the required maximum authentication age. Corresponds to the 1010 * optional {@code max_age} parameter. 1011 * 1012 * @return The maximum authentication age, in seconds; -1 if not 1013 * specified, zero implies {@code prompt=login}. 1014 */ 1015 public int getMaxAge() { 1016 1017 return maxAge; 1018 } 1019 1020 1021 /** 1022 * Gets the end-user's preferred languages and scripts for the user 1023 * interface, ordered by preference. Corresponds to the optional 1024 * {@code ui_locales} parameter. 1025 * 1026 * @return The preferred UI locales, {@code null} if not specified. 1027 */ 1028 public List<LangTag> getUILocales() { 1029 1030 return uiLocales; 1031 } 1032 1033 1034 /** 1035 * Gets the end-user's preferred languages and scripts for the claims 1036 * being returned, ordered by preference. Corresponds to the optional 1037 * {@code claims_locales} parameter. 1038 * 1039 * @return The preferred claims locales, {@code null} if not specified. 1040 */ 1041 public List<LangTag> getClaimsLocales() { 1042 1043 return claimsLocales; 1044 } 1045 1046 1047 /** 1048 * Gets the ID Token hint. Corresponds to the conditionally optional 1049 * {@code id_token_hint} parameter. 1050 * 1051 * @return The ID Token hint, {@code null} if not specified. 1052 */ 1053 public JWT getIDTokenHint() { 1054 1055 return idTokenHint; 1056 } 1057 1058 1059 /** 1060 * Gets the login hint. Corresponds to the optional {@code login_hint} 1061 * parameter. 1062 * 1063 * @return The login hint, {@code null} if not specified. 1064 */ 1065 public String getLoginHint() { 1066 1067 return loginHint; 1068 } 1069 1070 1071 /** 1072 * Gets the requested Authentication Context Class Reference values. 1073 * Corresponds to the optional {@code acr_values} parameter. 1074 * 1075 * @return The requested ACR values, {@code null} if not specified. 1076 */ 1077 public List<ACR> getACRValues() { 1078 1079 return acrValues; 1080 } 1081 1082 1083 /** 1084 * Gets the individual claims to be returned. Corresponds to the 1085 * optional {@code claims} parameter. 1086 * 1087 * @return The individual claims to be returned, {@code null} if not 1088 * specified. 1089 */ 1090 public ClaimsRequest getClaims() { 1091 1092 return claims; 1093 } 1094 1095 1096 /** 1097 * Gets the request object. Corresponds to the optional {@code request} 1098 * parameter. 1099 * 1100 * @return The request object, {@code null} if not specified. 1101 */ 1102 public JWT getRequestObject() { 1103 1104 return requestObject; 1105 } 1106 1107 1108 /** 1109 * Gets the request object URI. Corresponds to the optional 1110 * {@code request_uri} parameter. 1111 * 1112 * @return The request object URI, {@code null} if not specified. 1113 */ 1114 public URI getRequestURI() { 1115 1116 return requestURI; 1117 } 1118 1119 1120 /** 1121 * Returns {@code true} if this authentication request specifies an 1122 * OpenID Connect request object (directly through the {@code request} 1123 * parameter or by reference through the {@code request_uri} parameter). 1124 * 1125 * @return {@code true} if a request object is specified, else 1126 * {@code false}. 1127 */ 1128 public boolean specifiesRequestObject() { 1129 1130 return requestObject != null || requestURI != null; 1131 } 1132 1133 1134 @Override 1135 public Map<String,String> toParameters() { 1136 1137 Map <String,String> params = super.toParameters(); 1138 1139 if (nonce != null) 1140 params.put("nonce", nonce.toString()); 1141 1142 if (display != null) 1143 params.put("display", display.toString()); 1144 1145 if (prompt != null) 1146 params.put("prompt", prompt.toString()); 1147 1148 if (maxAge >= 0) 1149 params.put("max_age", "" + maxAge); 1150 1151 if (uiLocales != null) { 1152 1153 StringBuilder sb = new StringBuilder(); 1154 1155 for (LangTag locale: uiLocales) { 1156 1157 if (sb.length() > 0) 1158 sb.append(' '); 1159 1160 sb.append(locale.toString()); 1161 } 1162 1163 params.put("ui_locales", sb.toString()); 1164 } 1165 1166 if (claimsLocales != null) { 1167 1168 StringBuilder sb = new StringBuilder(); 1169 1170 for (LangTag locale: claimsLocales) { 1171 1172 if (sb.length() > 0) 1173 sb.append(' '); 1174 1175 sb.append(locale.toString()); 1176 } 1177 1178 params.put("claims_locales", sb.toString()); 1179 } 1180 1181 if (idTokenHint != null) { 1182 1183 try { 1184 params.put("id_token_hint", idTokenHint.serialize()); 1185 1186 } catch (IllegalStateException e) { 1187 1188 throw new SerializeException("Couldn't serialize ID token hint: " + e.getMessage(), e); 1189 } 1190 } 1191 1192 if (loginHint != null) 1193 params.put("login_hint", loginHint); 1194 1195 if (acrValues != null) { 1196 1197 StringBuilder sb = new StringBuilder(); 1198 1199 for (ACR acr: acrValues) { 1200 1201 if (sb.length() > 0) 1202 sb.append(' '); 1203 1204 sb.append(acr.toString()); 1205 } 1206 1207 params.put("acr_values", sb.toString()); 1208 } 1209 1210 1211 if (claims != null) 1212 params.put("claims", claims.toJSONObject().toString()); 1213 1214 if (requestObject != null) { 1215 1216 try { 1217 params.put("request", requestObject.serialize()); 1218 1219 } catch (IllegalStateException e) { 1220 1221 throw new SerializeException("Couldn't serialize request object to JWT: " + e.getMessage(), e); 1222 } 1223 } 1224 1225 if (requestURI != null) 1226 params.put("request_uri", requestURI.toString()); 1227 1228 return params; 1229 } 1230 1231 1232 /** 1233 * Parses an OpenID Connect authentication request from the specified 1234 * parameters. 1235 * 1236 * <p>Example parameters: 1237 * 1238 * <pre> 1239 * response_type = token id_token 1240 * client_id = s6BhdRkqt3 1241 * redirect_uri = https://client.example.com/cb 1242 * scope = openid profile 1243 * state = af0ifjsldkj 1244 * nonce = -0S6_WzA2Mj 1245 * </pre> 1246 * 1247 * @param params The parameters. Must not be {@code null}. 1248 * 1249 * @return The OpenID Connect authentication request. 1250 * 1251 * @throws ParseException If the parameters couldn't be parsed to an 1252 * OpenID Connect authentication request. 1253 */ 1254 public static AuthenticationRequest parse(final Map<String,String> params) 1255 throws ParseException { 1256 1257 return parse(null, params); 1258 } 1259 1260 1261 /** 1262 * Parses an OpenID Connect authentication request from the specified 1263 * parameters. 1264 * 1265 * <p>Example parameters: 1266 * 1267 * <pre> 1268 * response_type = token id_token 1269 * client_id = s6BhdRkqt3 1270 * redirect_uri = https://client.example.com/cb 1271 * scope = openid profile 1272 * state = af0ifjsldkj 1273 * nonce = -0S6_WzA2Mj 1274 * </pre> 1275 * 1276 * @param uri The URI of the OAuth 2.0 authorisation endpoint. May 1277 * be {@code null} if the {@link #toHTTPRequest} method 1278 * will not be used. 1279 * @param params The parameters. Must not be {@code null}. 1280 * 1281 * @return The OpenID Connect authentication request. 1282 * 1283 * @throws ParseException If the parameters couldn't be parsed to an 1284 * OpenID Connect authentication request. 1285 */ 1286 public static AuthenticationRequest parse(final URI uri, final Map<String,String> params) 1287 throws ParseException { 1288 1289 // Parse and validate the core OAuth 2.0 autz request params in 1290 // the context of OIDC 1291 AuthorizationRequest ar = AuthorizationRequest.parse(uri, params); 1292 1293 ClientID clientID = ar.getClientID(); 1294 State state = ar.getState(); 1295 ResponseMode rm = ar.getResponseMode(); 1296 1297 // Required in OIDC, check later after optional request_object / request_uri is parsed 1298 URI redirectURI = ar.getRedirectionURI(); 1299 1300 ResponseType rt = ar.getResponseType(); 1301 1302 try { 1303 OIDCResponseTypeValidator.validate(rt); 1304 1305 } catch (IllegalArgumentException e) { 1306 String msg = "Unsupported \"response_type\" parameter: " + e.getMessage(); 1307 throw new ParseException(msg, OAuth2Error.UNSUPPORTED_RESPONSE_TYPE.appendDescription(": " + msg), 1308 clientID, redirectURI, ar.impliedResponseMode(), state); 1309 } 1310 1311 // Required in OIDC, must include "openid" parameter 1312 Scope scope = ar.getScope(); 1313 1314 if (scope == null) { 1315 String msg = "Missing \"scope\" parameter"; 1316 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1317 clientID, redirectURI, ar.impliedResponseMode(), state); 1318 } 1319 1320 if (! scope.contains(OIDCScopeValue.OPENID)) { 1321 String msg = "The scope must include an \"openid\" value"; 1322 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1323 clientID, redirectURI, ar.impliedResponseMode(), state); 1324 } 1325 1326 1327 // Parse the remaining OIDC parameters 1328 Nonce nonce = Nonce.parse(params.get("nonce")); 1329 1330 // Nonce required in implicit flow 1331 if (rt.impliesImplicitFlow() && nonce == null) { 1332 String msg = "Missing \"nonce\" parameter: Required in implicit flow"; 1333 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1334 clientID, redirectURI, ar.impliedResponseMode(), state); 1335 } 1336 1337 Display display = null; 1338 1339 if (params.containsKey("display")) { 1340 try { 1341 display = Display.parse(params.get("display")); 1342 1343 } catch (ParseException e) { 1344 String msg = "Invalid \"display\" parameter: " + e.getMessage(); 1345 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1346 clientID, redirectURI, ar.impliedResponseMode(), state, e); 1347 } 1348 } 1349 1350 1351 Prompt prompt; 1352 1353 try { 1354 prompt = Prompt.parse(params.get("prompt")); 1355 1356 } catch (ParseException e) { 1357 String msg = "Invalid \"prompt\" parameter: " + e.getMessage(); 1358 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1359 clientID, redirectURI, ar.impliedResponseMode(), state, e); 1360 } 1361 1362 1363 String v = params.get("max_age"); 1364 1365 int maxAge = -1; 1366 1367 if (StringUtils.isNotBlank(v)) { 1368 1369 try { 1370 maxAge = Integer.parseInt(v); 1371 1372 } catch (NumberFormatException e) { 1373 String msg = "Invalid \"max_age\" parameter: " + v; 1374 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1375 clientID, redirectURI, ar.impliedResponseMode(), state, e); 1376 } 1377 } 1378 1379 1380 v = params.get("ui_locales"); 1381 1382 List<LangTag> uiLocales = null; 1383 1384 if (StringUtils.isNotBlank(v)) { 1385 1386 uiLocales = new LinkedList<>(); 1387 1388 StringTokenizer st = new StringTokenizer(v, " "); 1389 1390 while (st.hasMoreTokens()) { 1391 1392 try { 1393 uiLocales.add(LangTag.parse(st.nextToken())); 1394 1395 } catch (LangTagException e) { 1396 String msg = "Invalid \"ui_locales\" parameter: " + e.getMessage(); 1397 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1398 clientID, redirectURI, ar.impliedResponseMode(), state, e); 1399 } 1400 } 1401 } 1402 1403 1404 v = params.get("claims_locales"); 1405 1406 List<LangTag> claimsLocales = null; 1407 1408 if (StringUtils.isNotBlank(v)) { 1409 1410 claimsLocales = new LinkedList<>(); 1411 1412 StringTokenizer st = new StringTokenizer(v, " "); 1413 1414 while (st.hasMoreTokens()) { 1415 1416 try { 1417 claimsLocales.add(LangTag.parse(st.nextToken())); 1418 1419 } catch (LangTagException e) { 1420 String msg = "Invalid \"claims_locales\" parameter: " + e.getMessage(); 1421 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1422 clientID, redirectURI, ar.impliedResponseMode(), state, e); 1423 } 1424 } 1425 } 1426 1427 1428 v = params.get("id_token_hint"); 1429 1430 JWT idTokenHint = null; 1431 1432 if (StringUtils.isNotBlank(v)) { 1433 1434 try { 1435 idTokenHint = JWTParser.parse(v); 1436 1437 } catch (java.text.ParseException e) { 1438 String msg = "Invalid \"id_token_hint\" parameter: " + e.getMessage(); 1439 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1440 clientID, redirectURI, ar.impliedResponseMode(), state, e); 1441 } 1442 } 1443 1444 String loginHint = params.get("login_hint"); 1445 1446 1447 v = params.get("acr_values"); 1448 1449 List<ACR> acrValues = null; 1450 1451 if (StringUtils.isNotBlank(v)) { 1452 1453 acrValues = new LinkedList<>(); 1454 1455 StringTokenizer st = new StringTokenizer(v, " "); 1456 1457 while (st.hasMoreTokens()) { 1458 1459 acrValues.add(new ACR(st.nextToken())); 1460 } 1461 } 1462 1463 1464 v = params.get("claims"); 1465 1466 ClaimsRequest claims = null; 1467 1468 if (StringUtils.isNotBlank(v)) { 1469 1470 JSONObject jsonObject; 1471 1472 try { 1473 jsonObject = JSONObjectUtils.parse(v); 1474 1475 } catch (ParseException e) { 1476 String msg = "Invalid \"claims\" parameter: " + e.getMessage(); 1477 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1478 clientID, redirectURI, ar.impliedResponseMode(), state, e); 1479 } 1480 1481 // Parse exceptions silently ignored 1482 claims = ClaimsRequest.parse(jsonObject); 1483 } 1484 1485 1486 v = params.get("request_uri"); 1487 1488 URI requestURI = null; 1489 1490 if (StringUtils.isNotBlank(v)) { 1491 1492 try { 1493 requestURI = new URI(v); 1494 1495 } catch (URISyntaxException e) { 1496 String msg = "Invalid \"request_uri\" parameter: " + e.getMessage(); 1497 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1498 clientID, redirectURI, ar.impliedResponseMode(), state, e); 1499 } 1500 } 1501 1502 v = params.get("request"); 1503 1504 JWT requestObject = null; 1505 1506 if (StringUtils.isNotBlank(v)) { 1507 1508 // request_object and request_uri must not be defined at the same time 1509 if (requestURI != null) { 1510 String msg = "Invalid request: Found mutually exclusive \"request\" and \"request_uri\" parameters"; 1511 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1512 clientID, redirectURI, ar.impliedResponseMode(), state, null); 1513 } 1514 1515 try { 1516 requestObject = JWTParser.parse(v); 1517 1518 } catch (java.text.ParseException e) { 1519 String msg = "Invalid \"request_object\" parameter: " + e.getMessage(); 1520 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1521 clientID, redirectURI, ar.impliedResponseMode(), state, e); 1522 } 1523 } 1524 1525 1526 // Redirect URI required unless request_object / request_uri present 1527 if (redirectURI == null && requestObject == null && requestURI == null) { 1528 String msg = "Missing \"redirect_uri\" parameter"; 1529 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1530 clientID, null, ar.impliedResponseMode(), state); 1531 } 1532 1533 // Parse additional custom parameters 1534 Map<String,String> customParams = null; 1535 1536 for (Map.Entry<String,String> p: params.entrySet()) { 1537 1538 if (! REGISTERED_PARAMETER_NAMES.contains(p.getKey())) { 1539 // We have a custom parameter 1540 if (customParams == null) { 1541 customParams = new HashMap<>(); 1542 } 1543 customParams.put(p.getKey(), p.getValue()); 1544 } 1545 } 1546 1547 1548 return new AuthenticationRequest( 1549 uri, rt, rm, scope, clientID, redirectURI, state, nonce, 1550 display, prompt, maxAge, uiLocales, claimsLocales, 1551 idTokenHint, loginHint, acrValues, claims, requestObject, requestURI, 1552 ar.getCodeChallenge(), ar.getCodeChallengeMethod(), 1553 customParams); 1554 } 1555 1556 1557 /** 1558 * Parses an OpenID Connect authentication request from the specified 1559 * URI query string. 1560 * 1561 * <p>Example URI query string: 1562 * 1563 * <pre> 1564 * response_type=token%20id_token 1565 * &client_id=s6BhdRkqt3 1566 * &redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb 1567 * &scope=openid%20profile 1568 * &state=af0ifjsldkj 1569 * &nonce=n-0S6_WzA2Mj 1570 * </pre> 1571 * 1572 * @param query The URI query string. Must not be {@code null}. 1573 * 1574 * @return The OpenID Connect authentication request. 1575 * 1576 * @throws ParseException If the query string couldn't be parsed to an 1577 * OpenID Connect authentication request. 1578 */ 1579 public static AuthenticationRequest parse(final String query) 1580 throws ParseException { 1581 1582 return parse(null, URLUtils.parseParameters(query)); 1583 } 1584 1585 1586 /** 1587 * Parses an OpenID Connect authentication request from the specified 1588 * URI query string. 1589 * 1590 * <p>Example URI query string: 1591 * 1592 * <pre> 1593 * response_type=token%20id_token 1594 * &client_id=s6BhdRkqt3 1595 * &redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb 1596 * &scope=openid%20profile 1597 * &state=af0ifjsldkj 1598 * &nonce=n-0S6_WzA2Mj 1599 * </pre> 1600 * 1601 * @param uri The URI of the OAuth 2.0 authorisation endpoint. May be 1602 * {@code null} if the {@link #toHTTPRequest} method will 1603 * not be used. 1604 * @param query The URI query string. Must not be {@code null}. 1605 * 1606 * @return The OpenID Connect authentication request. 1607 * 1608 * @throws ParseException If the query string couldn't be parsed to an 1609 * OpenID Connect authentication request. 1610 */ 1611 public static AuthenticationRequest parse(final URI uri, final String query) 1612 throws ParseException { 1613 1614 return parse(uri, URLUtils.parseParameters(query)); 1615 } 1616 1617 1618 /** 1619 * Parses an OpenID Connect authentication request from the specified 1620 * URI. 1621 * 1622 * <p>Example URI: 1623 * 1624 * <pre> 1625 * https://server.example.com/authorize? 1626 * response_type=token%20id_token 1627 * &client_id=s6BhdRkqt3 1628 * &redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb 1629 * &scope=openid%20profile 1630 * &state=af0ifjsldkj 1631 * &nonce=n-0S6_WzA2Mj 1632 * </pre> 1633 * 1634 * @param uri The URI. Must not be {@code null}. 1635 * 1636 * @return The OpenID Connect authentication request. 1637 * 1638 * @throws ParseException If the query string couldn't be parsed to an 1639 * OpenID Connect authentication request. 1640 */ 1641 public static AuthenticationRequest parse(final URI uri) 1642 throws ParseException { 1643 1644 return parse(URIUtils.getBaseURI(uri), URLUtils.parseParameters(uri.getRawQuery())); 1645 } 1646 1647 1648 /** 1649 * Parses an authentication request from the specified HTTP GET or HTTP 1650 * POST request. 1651 * 1652 * <p>Example HTTP request (GET): 1653 * 1654 * <pre> 1655 * https://server.example.com/op/authorize? 1656 * response_type=code%20id_token 1657 * &client_id=s6BhdRkqt3 1658 * &redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb 1659 * &scope=openid 1660 * &nonce=n-0S6_WzA2Mj 1661 * &state=af0ifjsldkj 1662 * </pre> 1663 * 1664 * @param httpRequest The HTTP request. Must not be {@code null}. 1665 * 1666 * @return The OpenID Connect authentication request. 1667 * 1668 * @throws ParseException If the HTTP request couldn't be parsed to an 1669 * OpenID Connect authentication request. 1670 */ 1671 public static AuthenticationRequest parse(final HTTPRequest httpRequest) 1672 throws ParseException { 1673 1674 String query = httpRequest.getQuery(); 1675 1676 if (query == null) 1677 throw new ParseException("Missing URI query string"); 1678 1679 URI endpointURI; 1680 1681 try { 1682 endpointURI = httpRequest.getURL().toURI(); 1683 1684 } catch (URISyntaxException e) { 1685 1686 throw new ParseException(e.getMessage(), e); 1687 } 1688 1689 return parse(endpointURI, query); 1690 } 1691}