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.Map;
009
010import net.jcip.annotations.Immutable;
011
012import com.nimbusds.oauth2.sdk.auth.ClientAuthentication;
013import com.nimbusds.oauth2.sdk.http.CommonContentTypes;
014import com.nimbusds.oauth2.sdk.http.HTTPRequest;
015import com.nimbusds.oauth2.sdk.util.URLUtils;
016
017
018/**
019 * Token request. Used to obtain an
020 * {@link com.nimbusds.oauth2.sdk.token.AccessToken access token} and an
021 * optional {@link com.nimbusds.oauth2.sdk.token.RefreshToken refresh token}
022 * at the Token endpoint of the authorisation server.
023 *
024 * <p>Example token request with an authorisation code grant:
025 *
026 * <pre>
027 * POST /token HTTP/1.1
028 * Host: server.example.com
029 * Content-Type: application/x-www-form-URIencoded
030 * Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
031 * 
032 * grant_type=authorization_code
033 * &amp;code=SplxlOBeZQQYbYS6WxSbIA
034 * &amp;redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb
035 * </pre>
036 *
037 * <p>Related specifications:
038 *
039 * <ul>
040 *     <li>OAuth 2.0 (RFC 6749), sections 4.1.3, 4.3.2, 4.4.2 and 6.
041 * </ul>
042 */
043@Immutable
044public class TokenRequest extends AbstractRequest {
045
046
047        /**
048         * The client authentication, {@code null} if none.
049         */
050        private final ClientAuthentication clientAuth;
051
052
053        /**
054         * The authorisation grant.
055         */
056        private final AuthorizationGrant authzGrant;
057        
058        
059        /**
060         * Creates a new token request.
061         *
062         * @param uri        The URI of the token endpoint. May be 
063         *                   {@code null} if the {@link #toHTTPRequest} method
064         *                   will not be used.
065         * @param clientAuth The client authentication, {@code null} if none.
066         * @param authzGrant The authorisation grant. Must not be {@code null}.
067         */
068        public TokenRequest(final URI uri,
069                            final ClientAuthentication clientAuth,
070                            final AuthorizationGrant authzGrant) {
071        
072                super(uri);
073                
074                this.clientAuth = clientAuth;
075
076                if (authzGrant == null)
077                        throw new IllegalArgumentException("The authorization grant must not be null");
078
079                this.authzGrant = authzGrant;
080        }
081        
082        
083        /**
084         * Gets the client authentication.
085         *
086         * @return The client authentication, {@code null} if none.
087         */
088        public ClientAuthentication getClientAuthentication() {
089        
090                return clientAuth;
091        }
092
093
094        /**
095         * Gets the authorisation grant.
096         *
097         * @return The authorisation grant.
098         */
099        public AuthorizationGrant getAuthorizationGrant() {
100
101                return authzGrant;
102        }
103
104
105        @Override
106        public HTTPRequest toHTTPRequest()
107                throws SerializeException {
108
109                if (getEndpointURI() == null)
110                        throw new SerializeException("The endpoint URI is not specified");
111
112                URL url;
113
114                try {
115                        url = getEndpointURI().toURL();
116
117                } catch (MalformedURLException e) {
118
119                        throw new SerializeException(e.getMessage(), e);
120                }
121
122                HTTPRequest httpRequest = new HTTPRequest(HTTPRequest.Method.POST, url);
123                httpRequest.setContentType(CommonContentTypes.APPLICATION_URLENCODED);
124
125                Map<String,String> params = authzGrant.toParameters();
126
127                httpRequest.setQuery(URLUtils.serializeParameters(params));
128
129                if (getClientAuthentication() != null)
130                        getClientAuthentication().applyTo(httpRequest);
131
132                return httpRequest;
133        }
134        
135        
136        /**
137         * Parses the specified HTTP request for a token request.
138         *
139         * @param httpRequest The HTTP request. Must not be {@code null}.
140         *
141         * @return The token request.
142         *
143         * @throws ParseException If the HTTP request couldn't be parsed to a 
144         *                        token request.
145         */
146        public static TokenRequest parse(final HTTPRequest httpRequest)
147                throws ParseException {
148                
149                // Only HTTP POST accepted
150                httpRequest.ensureMethod(HTTPRequest.Method.POST);
151                httpRequest.ensureContentType(CommonContentTypes.APPLICATION_URLENCODED);
152                
153                // No fragment!
154                // May use query component!
155                Map<String,String> params = httpRequest.getQueryParameters();
156
157                // Parse grant
158                AuthorizationGrant authzGrant = AuthorizationGrant.parse(params);
159
160                // Parse client auth
161                ClientAuthentication clientAuth = ClientAuthentication.parse(httpRequest);
162
163                URI uri;
164
165                try {
166                        uri = httpRequest.getURL().toURI();
167
168                } catch (URISyntaxException e) {
169
170                        throw new ParseException(e.getMessage(), e);
171                }
172
173                return new TokenRequest(uri, clientAuth, authzGrant);
174        }
175}