001/*
002 * oauth2-oidc-sdk
003 *
004 * Copyright 2012-2016, Connect2id Ltd and contributors.
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
007 * this file except in compliance with the License. You may obtain a copy of the
008 * License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software distributed
013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
015 * specific language governing permissions and limitations under the License.
016 */
017
018package com.nimbusds.oauth2.sdk;
019
020
021import java.net.MalformedURLException;
022import java.net.URI;
023import java.net.URL;
024
025import net.jcip.annotations.Immutable;
026import net.minidev.json.JSONObject;
027
028import com.nimbusds.common.contenttype.ContentType;
029import com.nimbusds.jwt.JWT;
030import com.nimbusds.jwt.JWTParser;
031import com.nimbusds.jwt.PlainJWT;
032import com.nimbusds.oauth2.sdk.auth.PKITLSClientAuthentication;
033import com.nimbusds.oauth2.sdk.auth.SelfSignedTLSClientAuthentication;
034import com.nimbusds.oauth2.sdk.auth.TLSClientAuthentication;
035import com.nimbusds.oauth2.sdk.http.HTTPRequest;
036import com.nimbusds.oauth2.sdk.id.ClientID;
037import com.nimbusds.oauth2.sdk.util.JSONObjectUtils;
038
039
040/**
041 * Request object POST request.
042 *
043 * <p>Example request object POST request:
044 *
045 * <pre>
046 * POST /requests HTTP/1.1
047 * Host: c2id.com
048 * Content-Type: application/jws
049 * Content-Length: 1288
050 *
051 * eyJhbGciOiJSUzI1NiIsImtpZCI6ImsyYmRjIn0.ew0KICJpc3MiOiA
052 * (... abbreviated for brevity ...)
053 * zCYIb_NMXvtTIVc1jpspnTSD7xMbpL-2QgwUsAlMGzw
054 * </pre>
055 *
056 * <p>Related specifications:
057 *
058 * <ul>
059 *     <li>Financial-grade API - Part 2: Read and Write API Security Profile,
060 *         section 7.
061 *     <li>The OAuth 2.0 Authorization Framework: JWT Secured Authorization
062 *         Request (JAR) (draft-ietf-oauth-jwsreq-17).
063 * </ul>
064 */
065@Deprecated
066@Immutable
067public final class RequestObjectPOSTRequest extends AbstractOptionallyAuthenticatedRequest {
068        
069        
070        /**
071         * The request object as JWT, {@code null} for a
072         * {@link #requestJSONObject plain JSON object}.
073         */
074        private final JWT requestObject;
075        
076        
077        /**
078         * The request parameters as plain JSON object, {@code null} for
079         * {@link #requestObject JWT}.
080         */
081        private final JSONObject requestJSONObject;
082        
083        
084        /**
085         * Creates a new request object POST request.
086         *
087         * @param uri           The URI of the request object endpoint. May be
088         *                      {@code null} if the {@link #toHTTPRequest}
089         *                      method will not be used.
090         * @param requestObject The request object. Must not be {@code null}.
091         */
092        public RequestObjectPOSTRequest(final URI uri,
093                                        final JWT requestObject) {
094                
095                super(uri, null);
096                
097                if (requestObject == null) {
098                        throw new IllegalArgumentException("The request object must not be null");
099                }
100                
101                if (requestObject instanceof PlainJWT) {
102                        throw new IllegalArgumentException("The request object must not be an unsecured JWT (alg=none)");
103                }
104                
105                this.requestObject = requestObject;
106                
107                requestJSONObject = null;
108        }
109        
110        
111        /**
112         * Creates a new request object POST request where the parameters are
113         * submitted as plain JSON object, and the client authenticates by
114         * means of mutual TLS. TLS also ensures the integrity and
115         * confidentiality of the request parameters. This method is not
116         * standard.
117         *
118         * @param uri               The URI of the request object endpoint. May
119         *                          be {@code null} if the
120         *                          {@link #toHTTPRequest} method will not be
121         *                          used.
122         * @param tlsClientAuth     The mutual TLS client authentication. Must
123         *                          not be {@code null}.
124         * @param requestJSONObject The request parameters as plain JSON
125         *                          object. Must not be {@code null}.
126         */
127        public RequestObjectPOSTRequest(final URI uri,
128                                        final TLSClientAuthentication tlsClientAuth,
129                                        final JSONObject requestJSONObject) {
130                
131                super(uri, tlsClientAuth);
132                
133                if (tlsClientAuth == null) {
134                        throw new IllegalArgumentException("The mutual TLS client authentication must not be null");
135                }
136                
137                if (requestJSONObject == null) {
138                        throw new IllegalArgumentException("The request JSON object must not be null");
139                }
140                
141                this.requestJSONObject = requestJSONObject;
142                
143                requestObject = null;
144        }
145        
146        
147        /**
148         * Returns the request object as JWT.
149         *
150         * @return The request object as JWT, {@code null} if the request
151         *         parameters are specified as {@link #getRequestJSONObject()
152         *         plain JSON object} instead.
153         */
154        public JWT getRequestObject() {
155                
156                return requestObject;
157        }
158        
159        
160        /**
161         * Returns the request object as plain JSON object.
162         *
163         * @return The request parameters as plain JSON object, {@code null}
164         *         if the request object is specified as a
165         *         {@link #getRequestObject() JWT}.
166         */
167        public JSONObject getRequestJSONObject() {
168                
169                return requestJSONObject;
170        }
171        
172        
173        /**
174         * Returns the mutual TLS client authentication.
175         *
176         * @return The mutual TLS client authentication.
177         */
178        public TLSClientAuthentication getTLSClientAuthentication() {
179                
180                return (TLSClientAuthentication) getClientAuthentication();
181        }
182        
183        
184        @Override
185        public HTTPRequest toHTTPRequest() {
186                
187                if (getEndpointURI() == null)
188                        throw new SerializeException("The endpoint URI is not specified");
189                
190                URL url;
191                try {
192                        url = getEndpointURI().toURL();
193                } catch (MalformedURLException e) {
194                        throw new SerializeException(e.getMessage(), e);
195                }
196                
197                HTTPRequest httpRequest = new HTTPRequest(HTTPRequest.Method.POST, url);
198                
199                if (getRequestObject() != null) {
200                        httpRequest.setEntityContentType(ContentType.APPLICATION_JWT);
201                        httpRequest.setQuery(getRequestObject().serialize());
202                } else if (getRequestJSONObject() != null) {
203                        httpRequest.setEntityContentType(ContentType.APPLICATION_JSON);
204                        httpRequest.setQuery(getRequestJSONObject().toJSONString());
205                        getTLSClientAuthentication().applyTo(httpRequest);
206                }
207                
208                return httpRequest;
209        }
210        
211        
212        /**
213         * Parses a request object POST request from the specified HTTP
214         * request.
215         *
216         * @param httpRequest The HTTP request. Must not be {@code null}.
217         *
218         * @return The request object POST request.
219         *
220         * @throws ParseException If the HTTP request couldn't be parsed to a
221         *                        request object POST request.
222         */
223        public static RequestObjectPOSTRequest parse(final HTTPRequest httpRequest)
224                throws ParseException {
225                
226                // Only HTTP POST accepted
227                httpRequest.ensureMethod(HTTPRequest.Method.POST);
228                
229                if (httpRequest.getEntityContentType() == null) {
230                        throw new ParseException("Missing Content-Type");
231                }
232                
233                if (
234                        ContentType.APPLICATION_JOSE.matches(httpRequest.getEntityContentType()) ||
235                        ContentType.APPLICATION_JWT.matches(httpRequest.getEntityContentType())) {
236                        
237                        // Signed or signed and encrypted request object
238                        
239                        JWT requestObject;
240                        try {
241                                requestObject = JWTParser.parse(httpRequest.getQuery());
242                        } catch (java.text.ParseException e) {
243                                throw new ParseException("Invalid request object JWT: " + e.getMessage());
244                        }
245                        
246                        if (requestObject instanceof PlainJWT) {
247                                throw new ParseException("The request object is an unsecured JWT (alg=none)");
248                        }
249                        
250                        return new RequestObjectPOSTRequest(httpRequest.getURI(), requestObject);
251                        
252                } else if (ContentType.APPLICATION_JSON.matches(httpRequest.getEntityContentType())) {
253                        
254                        JSONObject jsonObject = httpRequest.getQueryAsJSONObject();
255                        
256                        if (jsonObject.get("client_id") == null) {
257                                throw new ParseException("Missing client_id in JSON object");
258                        }
259                        
260                        ClientID clientID = new ClientID(JSONObjectUtils.getString(jsonObject, "client_id"));
261                        
262                        // TODO
263                        TLSClientAuthentication tlsClientAuth;
264                        if (httpRequest.getClientX509Certificate() != null && httpRequest.getClientX509CertificateSubjectDN() != null &&
265                                        httpRequest.getClientX509CertificateSubjectDN().equals(httpRequest.getClientX509CertificateRootDN())) {
266                                tlsClientAuth = new SelfSignedTLSClientAuthentication(clientID, httpRequest.getClientX509Certificate());
267                        } else if (httpRequest.getClientX509Certificate() != null) {
268                                tlsClientAuth = new PKITLSClientAuthentication(clientID, httpRequest.getClientX509Certificate());
269                        } else {
270                                throw new ParseException("Missing mutual TLS client authentication");
271                        }
272                        
273                        return new RequestObjectPOSTRequest(httpRequest.getURI(), tlsClientAuth, jsonObject);
274                        
275                } else {
276                        
277                        throw new ParseException("Unexpected Content-Type: " + httpRequest.getEntityContentType());
278                }
279        }
280}