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.jwt.JWT; 029import com.nimbusds.jwt.JWTParser; 030import com.nimbusds.jwt.PlainJWT; 031import com.nimbusds.oauth2.sdk.auth.PKITLSClientAuthentication; 032import com.nimbusds.oauth2.sdk.auth.SelfSignedTLSClientAuthentication; 033import com.nimbusds.oauth2.sdk.auth.TLSClientAuthentication; 034import com.nimbusds.oauth2.sdk.http.CommonContentTypes; 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.setContentType(CommonContentTypes.APPLICATION_JWT); 201 httpRequest.setQuery(getRequestObject().serialize()); 202 } else if (getRequestJSONObject() != null) { 203 httpRequest.setContentType(CommonContentTypes.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.getContentType() == null) { 230 throw new ParseException("Missing Content-Type"); 231 } 232 233 if ( 234 CommonContentTypes.APPLICATION_JOSE.match(httpRequest.getContentType()) || 235 CommonContentTypes.APPLICATION_JWT.match(httpRequest.getContentType())) { 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 (CommonContentTypes.APPLICATION_JSON.match(httpRequest.getContentType())) { 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.getContentType()); 278 } 279 } 280}