001package com.nimbusds.oauth2.sdk; 002 003 004import java.net.MalformedURLException; 005import java.net.URI; 006import java.net.URISyntaxException; 007import java.net.URL; 008import java.util.LinkedHashMap; 009import java.util.Map; 010 011import com.nimbusds.oauth2.sdk.http.HTTPRequest; 012import com.nimbusds.oauth2.sdk.id.ClientID; 013import com.nimbusds.oauth2.sdk.id.State; 014import com.nimbusds.oauth2.sdk.pkce.CodeChallenge; 015import com.nimbusds.oauth2.sdk.pkce.CodeChallengeMethod; 016import com.nimbusds.oauth2.sdk.util.URIUtils; 017import com.nimbusds.oauth2.sdk.util.URLUtils; 018import net.jcip.annotations.Immutable; 019import org.apache.commons.lang3.StringUtils; 020 021 022/** 023 * Authorisation request. Used to authenticate an end-user and request the 024 * end-user's consent to grant the client access to a protected resource. 025 * 026 * <p>Extending classes may define additional request parameters as well as 027 * enforce tighter requirements on the base parameters. 028 * 029 * <p>Example HTTP request: 030 * 031 * <pre> 032 * https://server.example.com/authorize? 033 * response_type=code 034 * &client_id=s6BhdRkqt3 035 * &state=xyz 036 * &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb 037 * </pre> 038 * 039 * <p>Related specifications: 040 * 041 * <ul> 042 * <li>OAuth 2.0 (RFC 6749), sections 4.1.1 and 4.2.1. 043 * <li>OAuth 2.0 Multiple Response Type Encoding Practices 1.0. 044 * <li>OAuth 2.0 Form Post Response Mode 1.0. 045 * <li>Proof Key for Code Exchange by OAuth Public Clients (RFC 7636). 046 * </ul> 047 */ 048@Immutable 049public class AuthorizationRequest extends AbstractRequest { 050 051 052 /** 053 * The response type (required). 054 */ 055 private final ResponseType rt; 056 057 058 /** 059 * The client identifier (required). 060 */ 061 private final ClientID clientID; 062 063 064 /** 065 * The redirection URI where the response will be sent (optional). 066 */ 067 private final URI redirectURI; 068 069 070 /** 071 * The scope (optional). 072 */ 073 private final Scope scope; 074 075 076 /** 077 * The opaque value to maintain state between the request and the 078 * callback (recommended). 079 */ 080 private final State state; 081 082 083 /** 084 * The response mode (optional). 085 */ 086 private final ResponseMode rm; 087 088 089 /** 090 * The authorisation code challenge for PKCE (optional). 091 */ 092 private final CodeChallenge codeChallenge; 093 094 095 /** 096 * The authorisation code challenge method for PKCE (optional). 097 */ 098 private final CodeChallengeMethod codeChallengeMethod; 099 100 101 /** 102 * Builder for constructing authorisation requests. 103 */ 104 public static class Builder { 105 106 107 /** 108 * The endpoint URI (optional). 109 */ 110 private URI uri; 111 112 113 /** 114 * The response type (required). 115 */ 116 private final ResponseType rt; 117 118 119 /** 120 * The client identifier (required). 121 */ 122 private final ClientID clientID; 123 124 125 /** 126 * The redirection URI where the response will be sent 127 * (optional). 128 */ 129 private URI redirectURI; 130 131 132 /** 133 * The scope (optional). 134 */ 135 private Scope scope; 136 137 138 /** 139 * The opaque value to maintain state between the request and 140 * the callback (recommended). 141 */ 142 private State state; 143 144 145 /** 146 * The response mode (optional). 147 */ 148 private ResponseMode rm; 149 150 151 /** 152 * The authorisation code challenge for PKCE (optional). 153 */ 154 private CodeChallenge codeChallenge; 155 156 157 /** 158 * The authorisation code challenge method for PKCE (optional). 159 */ 160 private CodeChallengeMethod codeChallengeMethod; 161 162 163 /** 164 * Creates a new authorisation request builder. 165 * 166 * @param rt The response type. Corresponds to the 167 * {@code response_type} parameter. Must not be 168 * {@code null}. 169 * @param clientID The client identifier. Corresponds to the 170 * {@code client_id} parameter. Must not be 171 * {@code null}. 172 */ 173 public Builder(final ResponseType rt, final ClientID clientID) { 174 175 if (rt == null) 176 throw new IllegalArgumentException("The response type must not be null"); 177 178 this.rt = rt; 179 180 181 if (clientID == null) 182 throw new IllegalArgumentException("The client ID must not be null"); 183 184 this.clientID = clientID; 185 } 186 187 188 /** 189 * Sets the redirection URI. Corresponds to the optional 190 * {@code redirection_uri} parameter. 191 * 192 * @param redirectURI The redirection URI, {@code null} if not 193 * specified. 194 * 195 * @return This builder. 196 */ 197 public Builder redirectionURI(final URI redirectURI) { 198 199 this.redirectURI = redirectURI; 200 return this; 201 } 202 203 204 /** 205 * Sets the scope. Corresponds to the optional {@code scope} 206 * parameter. 207 * 208 * @param scope The scope, {@code null} if not specified. 209 * 210 * @return This builder. 211 */ 212 public Builder scope(final Scope scope) { 213 214 this.scope = scope; 215 return this; 216 } 217 218 219 /** 220 * Sets the state. Corresponds to the recommended {@code state} 221 * parameter. 222 * 223 * @param state The state, {@code null} if not specified. 224 * 225 * @return This builder. 226 */ 227 public Builder state(final State state) { 228 229 this.state = state; 230 return this; 231 } 232 233 234 /** 235 * Sets the response mode. Corresponds to the optional 236 * {@code response_mode} parameter. Use of this parameter is 237 * not recommended unless a non-default response mode is 238 * requested (e.g. form_post). 239 * 240 * @param rm The response mode, {@code null} if not specified. 241 * 242 * @return This builder. 243 */ 244 public Builder responseMode(final ResponseMode rm) { 245 246 this.rm = rm; 247 return this; 248 } 249 250 251 /** 252 * Sets the code challenge for Proof Key for Code Exchange 253 * (PKCE) by public OAuth clients. 254 * 255 * @param codeChallenge The code challenge, {@code null} 256 * if not specified. 257 * @param codeChallengeMethod The code challenge method, 258 * {@code null} if not specified. 259 * 260 * @return This builder. 261 */ 262 public Builder codeChallenge(final CodeChallenge codeChallenge, final CodeChallengeMethod codeChallengeMethod) { 263 264 this.codeChallenge = codeChallenge; 265 this.codeChallengeMethod = codeChallengeMethod; 266 return this; 267 } 268 269 270 /** 271 * Sets the URI of the endpoint (HTTP or HTTPS) for which the 272 * request is intended. 273 * 274 * @param uri The endpoint URI, {@code null} if not specified. 275 * 276 * @return This builder. 277 */ 278 public Builder endpointURI(final URI uri) { 279 280 this.uri = uri; 281 return this; 282 } 283 284 285 /** 286 * Builds a new authorisation request. 287 * 288 * @return The authorisation request. 289 */ 290 public AuthorizationRequest build() { 291 292 return new AuthorizationRequest(uri, rt, rm, clientID, redirectURI, scope, state, codeChallenge, codeChallengeMethod); 293 } 294 } 295 296 297 /** 298 * Creates a new minimal authorisation request. 299 * 300 * @param uri The URI of the authorisation endpoint. May be 301 * {@code null} if the {@link #toHTTPRequest} method 302 * will not be used. 303 * @param rt The response type. Corresponds to the 304 * {@code response_type} parameter. Must not be 305 * {@code null}. 306 * @param clientID The client identifier. Corresponds to the 307 * {@code client_id} parameter. Must not be 308 * {@code null}. 309 */ 310 public AuthorizationRequest(final URI uri, 311 final ResponseType rt, 312 final ClientID clientID) { 313 314 this(uri, rt, null, clientID, null, null, null, null, null); 315 } 316 317 318 /** 319 * Creates a new authorisation request. 320 * 321 * @param uri The URI of the authorisation endpoint. 322 * May be {@code null} if the 323 * {@link #toHTTPRequest} method will not be 324 * used. 325 * @param rt The response type. Corresponds to the 326 * {@code response_type} parameter. Must not 327 * be {@code null}. 328 * @param rm The response mode. Corresponds to the 329 * optional {@code response_mode} parameter. 330 * Use of this parameter is not recommended 331 * unless a non-default response mode is 332 * requested (e.g. form_post). 333 * @param clientID The client identifier. Corresponds to the 334 * {@code client_id} parameter. Must not be 335 * {@code null}. 336 * @param redirectURI The redirection URI. Corresponds to the 337 * optional {@code redirect_uri} parameter. 338 * {@code null} if not specified. 339 * @param scope The request scope. Corresponds to the 340 * optional {@code scope} parameter. 341 * {@code null} if not specified. 342 * @param state The state. Corresponds to the recommended 343 * {@code state} parameter. {@code null} if 344 * not specified. 345 */ 346 public AuthorizationRequest(final URI uri, 347 final ResponseType rt, 348 final ResponseMode rm, 349 final ClientID clientID, 350 final URI redirectURI, 351 final Scope scope, 352 final State state) { 353 354 this(uri, rt, rm, clientID, redirectURI, scope, state, null, null); 355 } 356 357 358 /** 359 * Creates a new authorisation request with PKCE support. 360 * 361 * @param uri The URI of the authorisation endpoint. 362 * May be {@code null} if the 363 * {@link #toHTTPRequest} method will not be 364 * used. 365 * @param rt The response type. Corresponds to the 366 * {@code response_type} parameter. Must not 367 * be {@code null}. 368 * @param rm The response mode. Corresponds to the 369 * optional {@code response_mode} parameter. 370 * Use of this parameter is not recommended 371 * unless a non-default response mode is 372 * requested (e.g. form_post). 373 * @param clientID The client identifier. Corresponds to the 374 * {@code client_id} parameter. Must not be 375 * {@code null}. 376 * @param redirectURI The redirection URI. Corresponds to the 377 * optional {@code redirect_uri} parameter. 378 * {@code null} if not specified. 379 * @param scope The request scope. Corresponds to the 380 * optional {@code scope} parameter. 381 * {@code null} if not specified. 382 * @param state The state. Corresponds to the recommended 383 * {@code state} parameter. {@code null} if 384 * not specified. 385 * @param codeChallenge The code challenge for PKCE, {@code null} 386 * if not specified. 387 * @param codeChallengeMethod The code challenge method for PKCE, 388 * {@code null} if not specified. 389 */ 390 public AuthorizationRequest(final URI uri, 391 final ResponseType rt, 392 final ResponseMode rm, 393 final ClientID clientID, 394 final URI redirectURI, 395 final Scope scope, 396 final State state, 397 final CodeChallenge codeChallenge, 398 final CodeChallengeMethod codeChallengeMethod) { 399 400 super(uri); 401 402 if (rt == null) 403 throw new IllegalArgumentException("The response type must not be null"); 404 405 this.rt = rt; 406 407 this.rm = rm; 408 409 410 if (clientID == null) 411 throw new IllegalArgumentException("The client ID must not be null"); 412 413 this.clientID = clientID; 414 415 416 this.redirectURI = redirectURI; 417 this.scope = scope; 418 this.state = state; 419 420 this.codeChallenge = codeChallenge; 421 this.codeChallengeMethod = codeChallengeMethod; 422 } 423 424 425 /** 426 * Gets the response type. Corresponds to the {@code response_type} 427 * parameter. 428 * 429 * @return The response type. 430 */ 431 public ResponseType getResponseType() { 432 433 return rt; 434 } 435 436 437 /** 438 * Gets the optional response mode. Corresponds to the optional 439 * {@code response_mode} parameter. 440 * 441 * @return The response mode, {@code null} if not specified. 442 */ 443 public ResponseMode getResponseMode() { 444 445 return rm; 446 } 447 448 449 /** 450 * Returns the implied response mode, determined by the optional 451 * {@code response_mode} parameter, and if that isn't specified, by 452 * the {@code response_type}. 453 * 454 * @return The implied response mode. 455 */ 456 public ResponseMode impliedResponseMode() { 457 458 if (rm != null) { 459 return rm; 460 } else if (rt.impliesImplicitFlow()) { 461 return ResponseMode.FRAGMENT; 462 } else { 463 return ResponseMode.QUERY; 464 } 465 } 466 467 468 /** 469 * Gets the client identifier. Corresponds to the {@code client_id} 470 * parameter. 471 * 472 * @return The client identifier. 473 */ 474 public ClientID getClientID() { 475 476 return clientID; 477 } 478 479 480 /** 481 * Gets the redirection URI. Corresponds to the optional 482 * {@code redirection_uri} parameter. 483 * 484 * @return The redirection URI, {@code null} if not specified. 485 */ 486 public URI getRedirectionURI() { 487 488 return redirectURI; 489 } 490 491 492 /** 493 * Gets the scope. Corresponds to the optional {@code scope} parameter. 494 * 495 * @return The scope, {@code null} if not specified. 496 */ 497 public Scope getScope() { 498 499 return scope; 500 } 501 502 503 /** 504 * Gets the state. Corresponds to the recommended {@code state} 505 * parameter. 506 * 507 * @return The state, {@code null} if not specified. 508 */ 509 public State getState() { 510 511 return state; 512 } 513 514 515 /** 516 * Returns the code challenge for PKCE. 517 * 518 * @return The code challenge, {@code null} if not specified. 519 */ 520 public CodeChallenge getCodeChallenge() { 521 522 return codeChallenge; 523 } 524 525 526 /** 527 * Returns the code challenge method for PKCE. 528 * 529 * @return The code challenge method, {@code null} if not specified. 530 */ 531 public CodeChallengeMethod getCodeChallengeMethod() { 532 533 return codeChallengeMethod; 534 } 535 536 537 /** 538 * Returns the parameters for this authorisation request. 539 * 540 * <p>Example parameters: 541 * 542 * <pre> 543 * response_type = code 544 * client_id = s6BhdRkqt3 545 * state = xyz 546 * redirect_uri = https://client.example.com/cb 547 * </pre> 548 * 549 * @return The parameters. 550 */ 551 public Map<String,String> toParameters() { 552 553 Map <String,String> params = new LinkedHashMap<>(); 554 555 params.put("response_type", rt.toString()); 556 params.put("client_id", clientID.getValue()); 557 558 if (rm != null) { 559 params.put("response_mode", rm.getValue()); 560 } 561 562 if (redirectURI != null) 563 params.put("redirect_uri", redirectURI.toString()); 564 565 if (scope != null) 566 params.put("scope", scope.toString()); 567 568 if (state != null) 569 params.put("state", state.getValue()); 570 571 if (codeChallenge != null) { 572 params.put("code_challenge", codeChallenge.getValue()); 573 574 if (codeChallengeMethod != null) { 575 params.put("code_challenge_method", codeChallengeMethod.getValue()); 576 } 577 } 578 579 return params; 580 } 581 582 583 /** 584 * Returns the URI query string for this authorisation request. 585 * 586 * <p>Note that the '?' character preceding the query string in an URI 587 * is not included in the returned string. 588 * 589 * <p>Example URI query string: 590 * 591 * <pre> 592 * response_type=code 593 * &client_id=s6BhdRkqt3 594 * &state=xyz 595 * &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb 596 * </pre> 597 * 598 * @return The URI query string. 599 */ 600 public String toQueryString() { 601 602 return URLUtils.serializeParameters(toParameters()); 603 } 604 605 606 /** 607 * Returns the complete URI representation for this authorisation 608 * request, consisting of the {@link #getEndpointURI authorization 609 * endpoint URI} with the {@link #toQueryString query string} appended. 610 * 611 * <p>Example URI: 612 * 613 * <pre> 614 * https://server.example.com/authorize? 615 * response_type=code 616 * &client_id=s6BhdRkqt3 617 * &state=xyz 618 * &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb 619 * </pre> 620 * 621 * @return The URI representation. 622 */ 623 public URI toURI() { 624 625 if (getEndpointURI() == null) 626 throw new SerializeException("The authorization endpoint URI is not specified"); 627 628 StringBuilder sb = new StringBuilder(getEndpointURI().toString()); 629 sb.append('?'); 630 sb.append(toQueryString()); 631 try { 632 return new URI(sb.toString()); 633 } catch (URISyntaxException e) { 634 throw new SerializeException("Couldn't append query string: " + e.getMessage(), e); 635 } 636 } 637 638 639 /** 640 * Returns the matching HTTP request. 641 * 642 * @param method The HTTP request method which can be GET or POST. Must 643 * not be {@code null}. 644 * 645 * @return The HTTP request. 646 */ 647 public HTTPRequest toHTTPRequest(final HTTPRequest.Method method) { 648 649 if (getEndpointURI() == null) 650 throw new SerializeException("The endpoint URI is not specified"); 651 652 HTTPRequest httpRequest; 653 654 URL endpointURL; 655 656 try { 657 endpointURL = getEndpointURI().toURL(); 658 659 } catch (MalformedURLException e) { 660 661 throw new SerializeException(e.getMessage(), e); 662 } 663 664 if (method.equals(HTTPRequest.Method.GET)) { 665 666 httpRequest = new HTTPRequest(HTTPRequest.Method.GET, endpointURL); 667 668 } else if (method.equals(HTTPRequest.Method.POST)) { 669 670 httpRequest = new HTTPRequest(HTTPRequest.Method.POST, endpointURL); 671 672 } else { 673 674 throw new IllegalArgumentException("The HTTP request method must be GET or POST"); 675 } 676 677 httpRequest.setQuery(toQueryString()); 678 679 return httpRequest; 680 } 681 682 683 @Override 684 public HTTPRequest toHTTPRequest() { 685 686 return toHTTPRequest(HTTPRequest.Method.GET); 687 } 688 689 690 /** 691 * Parses an authorisation request from the specified parameters. 692 * 693 * <p>Example parameters: 694 * 695 * <pre> 696 * response_type = code 697 * client_id = s6BhdRkqt3 698 * state = xyz 699 * redirect_uri = https://client.example.com/cb 700 * </pre> 701 * 702 * @param params The parameters. Must not be {@code null}. 703 * 704 * @return The authorisation request. 705 * 706 * @throws ParseException If the parameters couldn't be parsed to an 707 * authorisation request. 708 */ 709 public static AuthorizationRequest parse(final Map<String,String> params) 710 throws ParseException { 711 712 return parse(null, params); 713 } 714 715 716 /** 717 * Parses an authorisation request from the specified parameters. 718 * 719 * <p>Example parameters: 720 * 721 * <pre> 722 * response_type = code 723 * client_id = s6BhdRkqt3 724 * state = xyz 725 * redirect_uri = https://client.example.com/cb 726 * </pre> 727 * 728 * @param uri The URI of the authorisation endpoint. May be 729 * {@code null} if the {@link #toHTTPRequest()} method 730 * will not be used. 731 * @param params The parameters. Must not be {@code null}. 732 * 733 * @return The authorisation request. 734 * 735 * @throws ParseException If the parameters couldn't be parsed to an 736 * authorisation request. 737 */ 738 public static AuthorizationRequest parse(final URI uri, final Map<String,String> params) 739 throws ParseException { 740 741 // Parse mandatory client ID first 742 String v = params.get("client_id"); 743 744 if (StringUtils.isBlank(v)) { 745 String msg = "Missing \"client_id\" parameter"; 746 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg)); 747 } 748 749 ClientID clientID = new ClientID(v); 750 751 752 // Parse optional redirection URI second 753 v = params.get("redirect_uri"); 754 755 URI redirectURI = null; 756 757 if (StringUtils.isNotBlank(v)) { 758 759 try { 760 redirectURI = new URI(v); 761 762 } catch (URISyntaxException e) { 763 String msg = "Invalid \"redirect_uri\" parameter: " + e.getMessage(); 764 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 765 clientID, null, null, null, e); 766 } 767 } 768 769 770 // Parse optional state third 771 State state = State.parse(params.get("state")); 772 773 774 // Parse mandatory response type 775 v = params.get("response_type"); 776 777 ResponseType rt; 778 779 try { 780 rt = ResponseType.parse(v); 781 782 } catch (ParseException e) { 783 // Only cause 784 String msg = "Missing \"response_type\" parameter"; 785 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 786 clientID, redirectURI, null, state, e); 787 } 788 789 790 // Parse the optional response mode 791 v = params.get("response_mode"); 792 793 ResponseMode rm = null; 794 795 if (StringUtils.isNotBlank(v)) { 796 rm = new ResponseMode(v); 797 } 798 799 800 // Parse optional scope 801 v = params.get("scope"); 802 803 Scope scope = null; 804 805 if (StringUtils.isNotBlank(v)) 806 scope = Scope.parse(v); 807 808 809 // Parse optional code challenge and method for PKCE 810 CodeChallenge codeChallenge = null; 811 CodeChallengeMethod codeChallengeMethod = null; 812 813 v = params.get("code_challenge"); 814 815 if (StringUtils.isNotBlank(v)) 816 codeChallenge = new CodeChallenge(v); 817 818 if (codeChallenge != null) { 819 820 v = params.get("code_challenge_method"); 821 822 if (StringUtils.isNotBlank(v)) 823 codeChallengeMethod = CodeChallengeMethod.parse(v); 824 } 825 826 827 return new AuthorizationRequest(uri, rt, rm, clientID, redirectURI, scope, state, codeChallenge, codeChallengeMethod); 828 } 829 830 831 /** 832 * Parses an authorisation request from the specified URI query string. 833 * 834 * <p>Example URI query string: 835 * 836 * <pre> 837 * response_type=code 838 * &client_id=s6BhdRkqt3 839 * &state=xyz 840 * &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb 841 * </pre> 842 * 843 * @param query The URI query string. Must not be {@code null}. 844 * 845 * @return The authorisation request. 846 * 847 * @throws ParseException If the query string couldn't be parsed to an 848 * authorisation request. 849 */ 850 public static AuthorizationRequest parse(final String query) 851 throws ParseException { 852 853 return parse(null, URLUtils.parseParameters(query)); 854 } 855 856 857 /** 858 * Parses an authorisation request from the specified URI query string. 859 * 860 * <p>Example URI query string: 861 * 862 * <pre> 863 * response_type=code 864 * &client_id=s6BhdRkqt3 865 * &state=xyz 866 * &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb 867 * </pre> 868 * 869 * @param uri The URI of the authorisation endpoint. May be 870 * {@code null} if the {@link #toHTTPRequest()} method 871 * will not be used. 872 * @param query The URI query string. Must not be {@code null}. 873 * 874 * @return The authorisation request. 875 * 876 * @throws ParseException If the query string couldn't be parsed to an 877 * authorisation request. 878 */ 879 public static AuthorizationRequest parse(final URI uri, final String query) 880 throws ParseException { 881 882 return parse(uri, URLUtils.parseParameters(query)); 883 } 884 885 886 /** 887 * Parses an authorisation request from the specified URI. 888 * 889 * <p>Example URI: 890 * 891 * <pre> 892 * https://server.example.com/authorize? 893 * response_type=code 894 * &client_id=s6BhdRkqt3 895 * &state=xyz 896 * &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb 897 * </pre> 898 * 899 * @param uri The URI. Must not be {@code null}. 900 * 901 * @return The authorisation request. 902 * 903 * @throws ParseException If the URI couldn't be parsed to an 904 * authorisation request. 905 */ 906 public static AuthorizationRequest parse(final URI uri) 907 throws ParseException { 908 909 return parse(URIUtils.getBaseURI(uri), URLUtils.parseParameters(uri.getRawQuery())); 910 } 911 912 913 /** 914 * Parses an authorisation request from the specified HTTP request. 915 * 916 * <p>Example HTTP request (GET): 917 * 918 * <pre> 919 * https://server.example.com/authorize? 920 * response_type=code 921 * &client_id=s6BhdRkqt3 922 * &state=xyz 923 * &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb 924 * </pre> 925 * 926 * @param httpRequest The HTTP request. Must not be {@code null}. 927 * 928 * @return The authorisation request. 929 * 930 * @throws ParseException If the HTTP request couldn't be parsed to an 931 * authorisation request. 932 */ 933 public static AuthorizationRequest parse(final HTTPRequest httpRequest) 934 throws ParseException { 935 936 String query = httpRequest.getQuery(); 937 938 if (query == null) 939 throw new ParseException("Missing URI query string"); 940 941 try { 942 return parse(URIUtils.getBaseURI(httpRequest.getURL().toURI()), query); 943 944 } catch (URISyntaxException e) { 945 946 throw new ParseException(e.getMessage(), e); 947 } 948 } 949}