001package com.nimbusds.oauth2.sdk; 002 003 004import java.net.MalformedURLException; 005import java.net.URI; 006import java.net.URISyntaxException; 007import java.net.URL; 008import java.util.Collections; 009import java.util.HashMap; 010import java.util.Map; 011import java.util.Set; 012 013import net.jcip.annotations.Immutable; 014 015import net.minidev.json.JSONObject; 016 017import com.nimbusds.oauth2.sdk.auth.ClientAuthentication; 018import com.nimbusds.oauth2.sdk.http.CommonContentTypes; 019import com.nimbusds.oauth2.sdk.http.HTTPRequest; 020import com.nimbusds.oauth2.sdk.token.AccessToken; 021import com.nimbusds.oauth2.sdk.token.RefreshToken; 022import com.nimbusds.oauth2.sdk.token.Token; 023import com.nimbusds.oauth2.sdk.token.TypelessAccessToken; 024import com.nimbusds.oauth2.sdk.util.URLUtils; 025 026 027/** 028 * Token revocation request. Used to revoke an issued access or refresh token. 029 * 030 * <p>Example token revocation request with client authentication: 031 * 032 * <pre> 033 * POST /revoke HTTP/1.1 034 * Host: server.example.com 035 * Content-Type: application/x-www-form-urlencoded 036 * Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW 037 * 038 * token=45ghiukldjahdnhzdauz&token_type_hint=refresh_token 039 * </pre> 040 * 041 * <p>Related specifications: 042 * 043 * <ul> 044 * <li>OAuth 2.0 Token Revocation (RFC 7009), section 2.1. 045 * </ul> 046 */ 047@Immutable 048public final class TokenRevocationRequest extends AbstractRequest { 049 050 051 /** 052 * The client authentication, {@code null} if none. 053 */ 054 private final ClientAuthentication clientAuth; 055 056 057 /** 058 * The token to revoke. 059 */ 060 private final Token token; 061 062 063 /** 064 * 065 * @param uri The URI of the token revocation endpoint. May be 066 * {@code null} if the {@link #toHTTPRequest} method 067 * will not be used. 068 * @param clientAuth The client authentication, {@code null} if none. 069 * @param token The access or refresh token to revoke. Must not be 070 * {@code null}. 071 */ 072 public TokenRevocationRequest(final URI uri, 073 final ClientAuthentication clientAuth, 074 final Token token) { 075 076 super(uri); 077 078 this.clientAuth = clientAuth; 079 080 if (token == null) 081 throw new IllegalArgumentException("The token must not be null"); 082 083 this.token = token; 084 } 085 086 087 /** 088 * Gets the client authentication. 089 * 090 * @return The client authentication, {@code null} if none. 091 */ 092 public ClientAuthentication getClientAuthentication() { 093 094 return clientAuth; 095 } 096 097 098 /** 099 * Returns the token to revoke. The {@code instanceof} operator can be 100 * used to infer the token type. If it's neither 101 * {@link com.nimbusds.oauth2.sdk.token.AccessToken} nor 102 * {@link com.nimbusds.oauth2.sdk.token.RefreshToken} the 103 * {@code token_type_hint} has not be provided as part of the token 104 * revocation request. 105 * 106 * @return The token. 107 */ 108 public Token getToken() { 109 110 return token; 111 } 112 113 114 @Override 115 public HTTPRequest toHTTPRequest() 116 throws SerializeException { 117 118 if (getEndpointURI() == null) 119 throw new SerializeException("The endpoint URI is not specified"); 120 121 URL url; 122 123 try { 124 url = getEndpointURI().toURL(); 125 126 } catch (MalformedURLException e) { 127 128 throw new SerializeException(e.getMessage(), e); 129 } 130 131 HTTPRequest httpRequest = new HTTPRequest(HTTPRequest.Method.POST, url); 132 httpRequest.setContentType(CommonContentTypes.APPLICATION_URLENCODED); 133 134 Map<String,String> params = new HashMap<>(); 135 params.put("token", token.getValue()); 136 137 if (token instanceof AccessToken) { 138 params.put("token_type_hint", "access_token"); 139 } else if (token instanceof RefreshToken) { 140 params.put("token_type_hint", "refresh_token"); 141 } 142 143 httpRequest.setQuery(URLUtils.serializeParameters(params)); 144 145 if (getClientAuthentication() != null) 146 getClientAuthentication().applyTo(httpRequest); 147 148 return httpRequest; 149 } 150 151 152 /** 153 * Parses a token revocation request from the specified HTTP request. 154 * 155 * @param httpRequest The HTTP request. Must not be {@code null}. 156 * 157 * @return The token revocation request. 158 * 159 * @throws ParseException If the HTTP request couldn't be parsed to a 160 * token revocation request. 161 */ 162 public static TokenRevocationRequest parse(final HTTPRequest httpRequest) 163 throws ParseException { 164 165 // Only HTTP POST accepted 166 httpRequest.ensureMethod(HTTPRequest.Method.POST); 167 httpRequest.ensureContentType(CommonContentTypes.APPLICATION_URLENCODED); 168 169 Map<String,String> params = httpRequest.getQueryParameters(); 170 171 final String tokenValue = params.get("token"); 172 173 if (tokenValue == null || tokenValue.isEmpty()) { 174 throw new ParseException("Missing required token parameter"); 175 } 176 177 // Detect the token type 178 Token token = null; 179 180 final String tokenTypeHint = params.get("token_type_hint"); 181 182 if (tokenTypeHint == null) { 183 184 // Can be both access or refresh token 185 token = new Token() { 186 187 @Override 188 public String getValue() { 189 190 return tokenValue; 191 } 192 193 @Override 194 public Set<String> getParamNames() { 195 196 return Collections.emptySet(); 197 } 198 199 @Override 200 public JSONObject toJSONObject() { 201 202 return new JSONObject(); 203 } 204 205 @Override 206 public boolean equals(final Object other) { 207 208 return other instanceof Token && other.toString().equals(tokenValue); 209 } 210 }; 211 212 } else if (tokenTypeHint.equals("access_token")) { 213 214 token = new TypelessAccessToken(tokenValue); 215 216 } else if (tokenTypeHint.equals("refresh_token")) { 217 218 token = new RefreshToken(tokenValue); 219 } 220 221 222 // Parse client auth 223 ClientAuthentication clientAuth = ClientAuthentication.parse(httpRequest); 224 225 URI uri; 226 227 try { 228 uri = httpRequest.getURL().toURI(); 229 230 } catch (URISyntaxException e) { 231 232 throw new ParseException(e.getMessage(), e); 233 } 234 235 return new TokenRevocationRequest(uri, clientAuth, token); 236 } 237}