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}