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