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