001package com.nimbusds.common.oauth2; 002 003 004import java.io.IOException; 005import java.nio.charset.StandardCharsets; 006import java.security.MessageDigest; 007import java.security.NoSuchAlgorithmException; 008import javax.servlet.http.HttpServletRequest; 009import javax.servlet.http.HttpServletResponse; 010import javax.ws.rs.WebApplicationException; 011import javax.ws.rs.core.MediaType; 012import javax.ws.rs.core.Response; 013 014import com.nimbusds.oauth2.sdk.token.BearerAccessToken; 015import com.nimbusds.oauth2.sdk.token.BearerTokenError; 016import net.minidev.json.JSONObject; 017import org.apache.logging.log4j.Logger; 018 019 020/** 021 * Master access token validator. Intended for validation of master API access 022 * tokens for the Connect2id server and elsewhere. 023 */ 024public interface MasterAccessTokenValidator { 025 026 027 /** 028 * Error response: Missing OAuth 2.0 Bearer access token. 029 */ 030 ErrorResponse MISSING_BEARER_TOKEN = new ErrorResponse( 031 BearerTokenError.MISSING_TOKEN.getHTTPStatusCode(), 032 BearerTokenError.MISSING_TOKEN.toWWWAuthenticateHeader(), 033 "missing_token", 034 "Unauthorized: Missing Bearer access token"); 035 036 037 /** 038 * Error response: Invalid OAuth 2.0 Bearer access token. 039 */ 040 ErrorResponse INVALID_BEARER_TOKEN = new ErrorResponse( 041 BearerTokenError.INVALID_TOKEN.getHTTPStatusCode(), 042 BearerTokenError.INVALID_TOKEN.toWWWAuthenticateHeader(), 043 BearerTokenError.INVALID_TOKEN.getCode(), 044 "Unauthorized: Invalid Bearer access token"); 045 046 047 /** 048 * Error response: Web API disabled. 049 */ 050 ErrorResponse WEB_API_DISABLED = new ErrorResponse( 051 403, 052 null, 053 "web_api_disabled", 054 "Forbidden: Web API disabled"); 055 056 057 /** 058 * Bearer token error response. 059 */ 060 class ErrorResponse { 061 062 063 /** 064 * The HTTP status code. 065 */ 066 private final int statusCode; 067 068 069 /** 070 * Optional HTTP response {@code WWW-Authenticate} header. 071 */ 072 private final String wwwAuthHeader; 073 074 075 /** 076 * The HTTP body. 077 */ 078 private final String body; 079 080 081 /** 082 * Creates a new bearer token error response. 083 * 084 * @param statusCode The HTTP status code. 085 * @param wwwAuthHeader The HTTP response 086 * {@code WWW-Authenticate} header, 087 * {@code null} if none. 088 * @param bodyErrorCode The error code for the body JSON 089 * object. 090 * @param bodyErrorDesc The error description for the body JSON 091 * object. 092 */ 093 public ErrorResponse(final int statusCode, 094 final String wwwAuthHeader, 095 final String bodyErrorCode, 096 final String bodyErrorDesc) { 097 098 this.statusCode = statusCode; 099 this.wwwAuthHeader = wwwAuthHeader; 100 JSONObject bodyErrorObj = new JSONObject(); 101 bodyErrorObj.put("error", bodyErrorCode); 102 bodyErrorObj.put("error_description", bodyErrorDesc); 103 this.body = bodyErrorObj.toJSONString(); 104 } 105 106 107 /** 108 * Returns a web application exception for this error response. 109 * 110 * @return The web application exception. 111 */ 112 public WebApplicationException toWebAppException() { 113 114 Response.ResponseBuilder builder = Response.status(statusCode); 115 116 if (wwwAuthHeader != null) { 117 builder.header("WWW-Authenticate", wwwAuthHeader); 118 } 119 120 return new WebApplicationException( 121 builder.entity(body).type(MediaType.APPLICATION_JSON).build()); 122 } 123 124 125 /** 126 * Applies this error response to the specified HTTP servlet 127 * response. 128 * 129 * @param servletResponse The HTTP servlet response. Must not 130 * be {@code null}. 131 * 132 * @throws IOException If the error response couldn't be 133 * written. 134 */ 135 public void apply(final HttpServletResponse servletResponse) 136 throws IOException { 137 138 servletResponse.setStatus(statusCode); 139 140 if (wwwAuthHeader != null) { 141 servletResponse.setHeader("WWW-Authenticate", wwwAuthHeader); 142 } 143 144 if (body != null) { 145 servletResponse.setContentType("application/json"); 146 servletResponse.getWriter().print(body); 147 } 148 } 149 } 150 151 152 /** 153 * Computes the SHA-256 hash of the specified Bearer access token. 154 * 155 * @param token The Bearer access token. Must not be {@code null}. 156 * @param salt Optional salt to use, {@code null} if none. 157 * 158 * @return The computed SHA-256 hash. 159 */ 160 static byte[] computeSHA256(final BearerAccessToken token, final byte[] salt) { 161 162 try { 163 MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); 164 if (salt != null) { 165 sha256.update(salt); 166 } 167 return sha256.digest(token.getValue().getBytes(StandardCharsets.UTF_8)); 168 } catch (NoSuchAlgorithmException e) { 169 throw new RuntimeException(e); 170 } 171 } 172 173 174 /** 175 * Returns {@code true} if access is disabled (no access token 176 * configured). 177 * 178 * @return {@code true} if access is disabled, else {@code false}. 179 */ 180 boolean accessIsDisabled(); 181 182 183 /** 184 * Gets the optional logger. 185 * 186 * @return The logger, {@code null} if not specified. 187 */ 188 Logger getLogger(); 189 190 191 /** 192 * Sets the optional logger. 193 * 194 * @param log The logger, {@code null} if not specified. 195 */ 196 void setLogger(final Logger log); 197 198 199 /** 200 * Returns {@code true} if the specified bearer access token is valid. 201 * 202 * @param accessToken The bearer access token to check, {@code null} if 203 * not specified. 204 * 205 * @return {@code true} if the specified bearer access token is valid, 206 * else {@code false}. 207 */ 208 boolean isValid(final BearerAccessToken accessToken); 209 210 211 /** 212 * Validates a bearer access token passed in the specified HTTP 213 * Authorization header value. 214 * 215 * @param authzHeader The HTTP Authorization header value, {@code null} 216 * if not specified. 217 * 218 * @throws WebApplicationException If the header value is {@code null}, 219 * the web API is disabled, or the 220 * Bearer access token is missing or 221 * invalid. 222 */ 223 void validateBearerAccessToken(final String authzHeader) 224 throws WebApplicationException; 225 226 227 /** 228 * Validates a bearer access token passed in the specified HTTP servlet 229 * request. 230 * 231 * @param servletRequest The HTTP servlet request. Must not be 232 * {@code null}. 233 * @param servletResponse The HTTP servlet response. Must not be 234 * {@code null}. 235 * 236 * @return {@code true} if the bearer access token was successfully 237 * validated, {@code false}. 238 * 239 * @throws IOException If the response couldn't be written. 240 */ 241 boolean validateBearerAccessToken(final HttpServletRequest servletRequest, 242 final HttpServletResponse servletResponse) 243 throws IOException; 244}