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}