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