001 package com.nimbusds.oauth2.sdk.client; 002 003 004 import java.net.URL; 005 006 import org.apache.commons.lang3.StringUtils; 007 008 import net.jcip.annotations.Immutable; 009 010 import net.minidev.json.JSONObject; 011 012 import com.nimbusds.oauth2.sdk.ParseException; 013 import com.nimbusds.oauth2.sdk.ProtectedResourceRequest; 014 import com.nimbusds.oauth2.sdk.SerializeException; 015 import com.nimbusds.oauth2.sdk.auth.Secret; 016 import com.nimbusds.oauth2.sdk.http.CommonContentTypes; 017 import com.nimbusds.oauth2.sdk.http.HTTPRequest; 018 import com.nimbusds.oauth2.sdk.id.ClientID; 019 import com.nimbusds.oauth2.sdk.token.BearerAccessToken; 020 import com.nimbusds.oauth2.sdk.util.JSONObjectUtils; 021 022 023 /** 024 * Client registration request. This class is immutable. 025 * 026 * <p>Example HTTP request: 027 * 028 * <pre> 029 * PUT /register/s6BhdRkqt3 HTTP/1.1 030 * Accept: application/json 031 * Host: server.example.com 032 * Authorization: Bearer reg-23410913-abewfq.123483 033 * 034 * { 035 * "client_id" :"s6BhdRkqt3", 036 * "client_secret" : "cf136dc3c1fc93f31185e5885805d", 037 * "redirect_uris" : ["https://client.example.org/callback", "https://client.example.org/alt"], 038 * "scope" : "read write dolphin", 039 * "grant_types" : ["authorization_code", "refresh_token"] 040 * "token_endpoint_auth_method" : "client_secret_basic", 041 * "jwks_uri" : "https://client.example.org/my_public_keys.jwks" 042 * "client_name" : "My New Example", 043 * "client_name#fr" : "Mon Nouvel Exemple", 044 * "logo_uri" : "https://client.example.org/newlogo.png" 045 * "logo_uri#fr" : "https://client.example.org/fr/newlogo.png" 046 * } 047 * 048 * </pre> 049 * 050 * <p>Related specifications: 051 * 052 * <ul> 053 * <li>OAuth 2.0 Dynamic Client Registration Protocol 054 * (draft-ietf-oauth-dyn-reg-12), section 4.3. 055 * </ul> 056 * 057 * @author Vladimir Dzhuvinov 058 */ 059 @Immutable 060 public class ClientUpdateRequest extends ProtectedResourceRequest { 061 062 063 /** 064 * The registered client ID. 065 */ 066 private final ClientID id; 067 068 069 /** 070 * The client metadata. 071 */ 072 private final ClientMetadata metadata; 073 074 075 /** 076 * The optional client secret. 077 */ 078 private final Secret secret; 079 080 081 /** 082 * Creates a new client update request. 083 * 084 * @param uri The URI of the client update endpoint. May be 085 * {@code null} if the {@link #toHTTPRequest()} 086 * method will not be used. 087 * @param accessToken The client registration access token. Must not be 088 * {@code null}. 089 * @param metadata The client metadata. Must not be {@code null} and 090 * must specify one or more redirect URIs. 091 * @param secret The optional client secret, {@code null} if not 092 * specified. 093 */ 094 public ClientUpdateRequest(final URL uri, 095 final ClientID id, 096 final BearerAccessToken accessToken, 097 final ClientMetadata metadata, 098 final Secret secret) { 099 100 super(uri, accessToken); 101 102 if (id == null) 103 throw new IllegalArgumentException("The client identifier must not be null"); 104 105 this.id = id; 106 107 if (metadata == null) 108 throw new IllegalArgumentException("The client metadata must not be null"); 109 110 this.metadata = metadata; 111 112 this.secret = secret; 113 } 114 115 116 /** 117 * Gets the client ID. Corresponds to the {@code client_id} client 118 * registration parameter. 119 * 120 * @return The client ID, {@code null} if not specified. 121 */ 122 public ClientID getClientID() { 123 124 return id; 125 } 126 127 128 /** 129 * Gets the associated client metadata. 130 * 131 * @return The client metadata. 132 */ 133 public ClientMetadata getClientMetadata() { 134 135 return metadata; 136 } 137 138 139 /** 140 * Gets the client secret. Corresponds to the {@code client_secret} 141 * registration parameters. 142 * 143 * @return The client secret, {@code null} if not specified. 144 */ 145 public Secret getClientSecret() { 146 147 return secret; 148 } 149 150 151 @Override 152 public HTTPRequest toHTTPRequest() 153 throws SerializeException{ 154 155 if (getURI() == null) 156 throw new SerializeException("The endpoint URI is not specified"); 157 158 HTTPRequest httpRequest = new HTTPRequest(HTTPRequest.Method.PUT, getURI()); 159 160 httpRequest.setAuthorization(getAccessToken().toAuthorizationHeader()); 161 162 httpRequest.setContentType(CommonContentTypes.APPLICATION_JSON); 163 164 JSONObject jsonObject = metadata.toJSONObject(); 165 166 jsonObject.put("client_id", id.getValue()); 167 168 if (secret != null) 169 jsonObject.put("client_secret", secret.getValue()); 170 171 httpRequest.setQuery(jsonObject.toString()); 172 173 return httpRequest; 174 } 175 176 177 /** 178 * Parses a client update request from the specified HTTP PUT request. 179 * 180 * @param httpRequest The HTTP request. Must not be {@code null}. 181 * 182 * @return The client update request. 183 * 184 * @throws ParseException If the HTTP request couldn't be parsed to a 185 * client update request. 186 */ 187 public static ClientUpdateRequest parse(final HTTPRequest httpRequest) 188 throws ParseException { 189 190 httpRequest.ensureMethod(HTTPRequest.Method.PUT); 191 192 String authzHeaderValue = httpRequest.getAuthorization(); 193 194 if (StringUtils.isBlank(authzHeaderValue)) 195 throw new ParseException("Missing HTTP Authorization header"); 196 197 BearerAccessToken accessToken = BearerAccessToken.parse(authzHeaderValue); 198 199 JSONObject jsonObject = httpRequest.getQueryAsJSONObject(); 200 201 ClientID id = new ClientID(JSONObjectUtils.getString(jsonObject, "client_id")); 202 203 ClientMetadata metadata = ClientMetadata.parse(jsonObject); 204 205 Secret clientSecret = null; 206 207 if (jsonObject.get("client_secret") != null) 208 clientSecret = new Secret(JSONObjectUtils.getString(jsonObject, "client_secret")); 209 210 211 return new ClientUpdateRequest(httpRequest.getURL(), id, accessToken, metadata, clientSecret); 212 } 213 }