Skip to content

Commit

Permalink
Merge branch 'feat/oauth/pass-thru-apis' into feat/oauth/revoke-apis
Browse files Browse the repository at this point in the history
  • Loading branch information
sattvikc committed Sep 18, 2024
2 parents 59a13cd + b791bf4 commit 110f2f7
Show file tree
Hide file tree
Showing 22 changed files with 146 additions and 74 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,13 @@
import java.util.Map;
import java.util.stream.Collectors;

public class HttpRequest {
public class HttpRequestForOry {
// This is a helper class to make HTTP requests to the hydra server specifically.
// Although this is similar to HttpRequest, this is slightly modified to be able to work with
// form data, headers in request and responses, query params in non-get requests, reading responses in
// case of errors, etc.
// Left the original HttpRequest as is to avoid any issues with existing code.

private static final int CONNECTION_TIMEOUT = 5000;
private static final int READ_TIMEOUT = 5000;

Expand Down
48 changes: 21 additions & 27 deletions src/main/java/io/supertokens/oauth/OAuth.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,23 +63,21 @@ private static void checkForOauthFeature(AppIdentifier appIdentifier, Main main)
"feature.");
}

public static HttpRequest.Response doOAuthProxyGET(Main main, AppIdentifier appIdentifier, Storage storage, String path, boolean proxyToAdmin, boolean camelToSnakeCaseConversion, Map<String, String> queryParams, Map<String, String> headers) throws StorageQueryException, OAuthClientNotFoundException, TenantOrAppNotFoundException, FeatureNotEnabledException, InvalidConfigException, IOException, OAuthAPIException {
public static HttpRequestForOry.Response doOAuthProxyGET(Main main, AppIdentifier appIdentifier, Storage storage, String clientIdToCheck, String path, boolean proxyToAdmin, boolean camelToSnakeCaseConversion, Map<String, String> queryParams, Map<String, String> headers) throws StorageQueryException, OAuthClientNotFoundException, TenantOrAppNotFoundException, FeatureNotEnabledException, InvalidConfigException, IOException, OAuthAPIException {
checkForOauthFeature(appIdentifier, main);
OAuthStorage oauthStorage = StorageUtils.getOAuthStorage(storage);

if (camelToSnakeCaseConversion) {
queryParams = convertCamelToSnakeCase(queryParams);
}

if (queryParams != null && queryParams.containsKey("client_id")) {
String clientId = queryParams.get("client_id");
if (!oauthStorage.doesClientIdExistForThisApp(appIdentifier, clientId)) {
if (clientIdToCheck != null) {
if (!oauthStorage.doesClientIdExistForThisApp(appIdentifier, clientIdToCheck)) {
throw new OAuthClientNotFoundException();
}
}

// Request transformations
queryParams = Transformations.transformQueryParamsForHydra(queryParams);
headers = Transformations.transformRequestHeadersForHydra(headers);

String baseURL;
Expand All @@ -90,7 +88,7 @@ public static HttpRequest.Response doOAuthProxyGET(Main main, AppIdentifier appI
}
String fullUrl = baseURL + path;

HttpRequest.Response response = HttpRequest.doGet(fullUrl, headers, queryParams);
HttpRequestForOry.Response response = HttpRequestForOry.doGet(fullUrl, headers, queryParams);

// Response transformations
response.jsonResponse = Transformations.transformJsonResponseFromHydra(main, appIdentifier, response.jsonResponse);
Expand All @@ -106,17 +104,16 @@ public static HttpRequest.Response doOAuthProxyGET(Main main, AppIdentifier appI
return response;
}

public static HttpRequest.Response doOAuthProxyFormPOST(Main main, AppIdentifier appIdentifier, Storage storage, String path, boolean proxyToAdmin, boolean camelToSnakeCaseConversion, Map<String, String> formFields, Map<String, String> headers) throws StorageQueryException, OAuthClientNotFoundException, TenantOrAppNotFoundException, FeatureNotEnabledException, InvalidConfigException, IOException, OAuthAPIException {
public static HttpRequestForOry.Response doOAuthProxyFormPOST(Main main, AppIdentifier appIdentifier, Storage storage, String clientIdToCheck, String path, boolean proxyToAdmin, boolean camelToSnakeCaseConversion, Map<String, String> formFields, Map<String, String> headers) throws StorageQueryException, OAuthClientNotFoundException, TenantOrAppNotFoundException, FeatureNotEnabledException, InvalidConfigException, IOException, OAuthAPIException {
checkForOauthFeature(appIdentifier, main);
OAuthStorage oauthStorage = StorageUtils.getOAuthStorage(storage);

if (camelToSnakeCaseConversion) {
formFields = OAuth.convertCamelToSnakeCase(formFields);
}

if (formFields.containsKey("client_id")) {
String clientId = formFields.get("client_id");
if (!oauthStorage.doesClientIdExistForThisApp(appIdentifier, clientId)) {
if (clientIdToCheck != null) {
if (!oauthStorage.doesClientIdExistForThisApp(appIdentifier, clientIdToCheck)) {
throw new OAuthClientNotFoundException();
}
}
Expand All @@ -133,7 +130,7 @@ public static HttpRequest.Response doOAuthProxyFormPOST(Main main, AppIdentifier
}
String fullUrl = baseURL + path;

HttpRequest.Response response = HttpRequest.doFormPost(fullUrl, headers, formFields);
HttpRequestForOry.Response response = HttpRequestForOry.doFormPost(fullUrl, headers, formFields);

// Response transformations
response.jsonResponse = Transformations.transformJsonResponseFromHydra(main, appIdentifier, response.jsonResponse);
Expand All @@ -148,17 +145,16 @@ public static HttpRequest.Response doOAuthProxyFormPOST(Main main, AppIdentifier
return response;
}

public static HttpRequest.Response doOAuthProxyJsonPOST(Main main, AppIdentifier appIdentifier, Storage storage, String path, boolean proxyToAdmin, boolean camelToSnakeCaseConversion, JsonObject jsonInput, Map<String, String> headers) throws StorageQueryException, OAuthClientNotFoundException, TenantOrAppNotFoundException, FeatureNotEnabledException, InvalidConfigException, IOException, OAuthAPIException {
public static HttpRequestForOry.Response doOAuthProxyJsonPOST(Main main, AppIdentifier appIdentifier, Storage storage, String clientIdToCheck, String path, boolean proxyToAdmin, boolean camelToSnakeCaseConversion, JsonObject jsonInput, Map<String, String> headers) throws StorageQueryException, OAuthClientNotFoundException, TenantOrAppNotFoundException, FeatureNotEnabledException, InvalidConfigException, IOException, OAuthAPIException {
checkForOauthFeature(appIdentifier, main);
OAuthStorage oauthStorage = StorageUtils.getOAuthStorage(storage);

if (camelToSnakeCaseConversion) {
jsonInput = convertCamelToSnakeCase(jsonInput);
}

if (jsonInput.has("client_id")) {
String clientId = jsonInput.get("client_id").getAsString();
if (!oauthStorage.doesClientIdExistForThisApp(appIdentifier, clientId)) {
if (clientIdToCheck != null) {
if (!oauthStorage.doesClientIdExistForThisApp(appIdentifier, clientIdToCheck)) {
throw new OAuthClientNotFoundException();
}
}
Expand All @@ -175,7 +171,7 @@ public static HttpRequest.Response doOAuthProxyJsonPOST(Main main, AppIdentifier
}
String fullUrl = baseURL + path;

HttpRequest.Response response = HttpRequest.doJsonPost(fullUrl, headers, jsonInput);
HttpRequestForOry.Response response = HttpRequestForOry.doJsonPost(fullUrl, headers, jsonInput);

// Response transformations
response.jsonResponse = Transformations.transformJsonResponseFromHydra(main, appIdentifier, response.jsonResponse);
Expand All @@ -190,7 +186,7 @@ public static HttpRequest.Response doOAuthProxyJsonPOST(Main main, AppIdentifier
return response;
}

public static HttpRequest.Response doOAuthProxyJsonPUT(Main main, AppIdentifier appIdentifier, Storage storage, String path, boolean proxyToAdmin, boolean camelToSnakeCaseConversion, Map<String, String> queryParams, JsonObject jsonInput, Map<String, String> headers) throws StorageQueryException, OAuthClientNotFoundException, TenantOrAppNotFoundException, FeatureNotEnabledException, InvalidConfigException, IOException, OAuthAPIException {
public static HttpRequestForOry.Response doOAuthProxyJsonPUT(Main main, AppIdentifier appIdentifier, Storage storage, String clientIdToCheck, String path, boolean proxyToAdmin, boolean camelToSnakeCaseConversion, Map<String, String> queryParams, JsonObject jsonInput, Map<String, String> headers) throws StorageQueryException, OAuthClientNotFoundException, TenantOrAppNotFoundException, FeatureNotEnabledException, InvalidConfigException, IOException, OAuthAPIException {
checkForOauthFeature(appIdentifier, main);
OAuthStorage oauthStorage = StorageUtils.getOAuthStorage(storage);

Expand All @@ -199,9 +195,8 @@ public static HttpRequest.Response doOAuthProxyJsonPUT(Main main, AppIdentifier
jsonInput = convertCamelToSnakeCase(jsonInput);
}

if (jsonInput.has("client_id")) {
String clientId = jsonInput.get("client_id").getAsString();
if (!oauthStorage.doesClientIdExistForThisApp(appIdentifier, clientId)) {
if (clientIdToCheck != null) {
if (!oauthStorage.doesClientIdExistForThisApp(appIdentifier, clientIdToCheck)) {
throw new OAuthClientNotFoundException();
}
}
Expand All @@ -218,7 +213,7 @@ public static HttpRequest.Response doOAuthProxyJsonPUT(Main main, AppIdentifier
}
String fullUrl = baseURL + path;

HttpRequest.Response response = HttpRequest.doJsonPut(fullUrl, queryParams, headers, jsonInput);
HttpRequestForOry.Response response = HttpRequestForOry.doJsonPut(fullUrl, queryParams, headers, jsonInput);

// Response transformations
response.jsonResponse = Transformations.transformJsonResponseFromHydra(main, appIdentifier, response.jsonResponse);
Expand All @@ -233,17 +228,16 @@ public static HttpRequest.Response doOAuthProxyJsonPUT(Main main, AppIdentifier
return response;
}

public static HttpRequest.Response doOAuthProxyJsonDELETE(Main main, AppIdentifier appIdentifier, Storage storage, String path, boolean proxyToAdmin, boolean camelToSnakeCaseConversion, Map<String, String> queryParams, JsonObject jsonInput, Map<String, String> headers) throws StorageQueryException, OAuthClientNotFoundException, TenantOrAppNotFoundException, FeatureNotEnabledException, InvalidConfigException, IOException, OAuthAPIException {
public static HttpRequestForOry.Response doOAuthProxyJsonDELETE(Main main, AppIdentifier appIdentifier, Storage storage, String clientIdToCheck, String path, boolean proxyToAdmin, boolean camelToSnakeCaseConversion, Map<String, String> queryParams, JsonObject jsonInput, Map<String, String> headers) throws StorageQueryException, OAuthClientNotFoundException, TenantOrAppNotFoundException, FeatureNotEnabledException, InvalidConfigException, IOException, OAuthAPIException {
checkForOauthFeature(appIdentifier, main);
OAuthStorage oauthStorage = StorageUtils.getOAuthStorage(storage);

if (camelToSnakeCaseConversion) {
jsonInput = OAuth.convertCamelToSnakeCase(jsonInput);
}

if (jsonInput.has("client_id")) {
String clientId = jsonInput.get("client_id").getAsString();
if (!oauthStorage.doesClientIdExistForThisApp(appIdentifier, clientId)) {
if (clientIdToCheck != null) {
if (!oauthStorage.doesClientIdExistForThisApp(appIdentifier, clientIdToCheck)) {
throw new OAuthClientNotFoundException();
}
}
Expand All @@ -260,7 +254,7 @@ public static HttpRequest.Response doOAuthProxyJsonDELETE(Main main, AppIdentifi
}
String fullUrl = baseURL + path;

HttpRequest.Response response = HttpRequest.doJsonDelete(fullUrl, headers, queryParams, jsonInput);
HttpRequestForOry.Response response = HttpRequestForOry.doJsonDelete(fullUrl, queryParams, headers, jsonInput);

// Response transformations
response.jsonResponse = Transformations.transformJsonResponseFromHydra(main, appIdentifier, response.jsonResponse);
Expand All @@ -275,7 +269,7 @@ public static HttpRequest.Response doOAuthProxyJsonDELETE(Main main, AppIdentifi
return response;
}

private static void checkNonSuccessResponse(HttpRequest.Response response) throws OAuthAPIException, OAuthClientNotFoundException {
private static void checkNonSuccessResponse(HttpRequestForOry.Response response) throws OAuthAPIException, OAuthClientNotFoundException {
if (response.statusCode == 404) {
throw new OAuthClientNotFoundException();
}
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/io/supertokens/oauth/OAuthToken.java
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ public static String reSignToken(AppIdentifier appIdentifier, Main main, String
payload.addProperty("iss", iss);
payload.addProperty("stt", tokenType.getValue());

if (tokenType == TokenType.ACCESS_TOKEN) {
// we need to move rsub, tId and sessionHandle from ext to root
Transformations.transformExt(payload);
}

if (payloadUpdate != null) {
for (Map.Entry<String, JsonElement> entry : payloadUpdate.entrySet()) {
if (!NON_OVERRIDABLE_TOKEN_PROPS.contains(entry.getKey())) {
Expand Down
22 changes: 18 additions & 4 deletions src/main/java/io/supertokens/oauth/Transformations.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
Expand All @@ -21,10 +22,7 @@
import io.supertokens.utils.Utils;

public class Transformations {

public static Map<String, String> transformQueryParamsForHydra(Map<String, String> queryParams) {
return queryParams;
}
private static Set<String> EXT_PROPS = Set.of("rsub", "tId", "sessionHandle");

public static Map<String, String> transformRequestHeadersForHydra(Map<String, String> requestHeaders) {
if (requestHeaders == null) {
Expand Down Expand Up @@ -208,4 +206,20 @@ public static JsonObject transformJsonForHydra(JsonObject jsonInput) {
}
return transformedJsonInput;
}

public static void transformExt(JsonObject payload) {
if (payload.has("ext")) {
JsonObject ext = payload.get("ext").getAsJsonObject();
for (String prop : EXT_PROPS) {
if (ext.has(prop)) {
payload.addProperty(prop, ext.get(prop).getAsString());
ext.remove(prop);
}
}

if (ext.entrySet().size() == 0) {
payload.remove("ext");
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,15 @@
import io.supertokens.Main;
import io.supertokens.featureflag.exceptions.FeatureNotEnabledException;
import io.supertokens.multitenancy.exception.BadPermissionException;
import io.supertokens.oauth.HttpRequest;
import io.supertokens.oauth.HttpRequestForOry;
import io.supertokens.oauth.OAuth;
import io.supertokens.oauth.exceptions.OAuthAPIException;
import io.supertokens.oauth.exceptions.OAuthClientNotFoundException;
import io.supertokens.pluginInterface.RECIPE_ID;
import io.supertokens.pluginInterface.Storage;
import io.supertokens.pluginInterface.exceptions.InvalidConfigException;
import io.supertokens.pluginInterface.exceptions.StorageQueryException;
import io.supertokens.pluginInterface.multitenancy.AppIdentifier;
import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException;
import io.supertokens.pluginInterface.oauth.exceptions.OAuth2ClientAlreadyExistsForAppException;
import io.supertokens.webserver.InputParser;
Expand Down Expand Up @@ -60,12 +62,13 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IO
main, req, resp,
getAppIdentifier(req),
enforcePublicTenantAndGetPublicTenantStorage(req),
clientId, // clientIdToCheck
"/admin/clients/" + clientId, // proxyPath
true, // proxyToAdmin
true, // camelToSnakeCaseConversion
OAuthProxyHelper.defaultGetQueryParamsFromRequest(req),
new HashMap<>(), // getHeadersForProxy
(statusCode, headers, rawBody, jsonBody) -> { // handleResponse
(statusCode, headers, rawBody, jsonBody) -> { // getJsonResponse
return jsonBody.getAsJsonObject();
}
);
Expand All @@ -84,16 +87,22 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws I
input.addProperty("subjectType", "public");

try {
AppIdentifier appIdentifier = getAppIdentifier(req);
Storage storage = enforcePublicTenantAndGetPublicTenantStorage(req);

input.addProperty("owner", appIdentifier.getAppId());

OAuthProxyHelper.proxyJsonPOST(
main, req, resp,
getAppIdentifier(req),
enforcePublicTenantAndGetPublicTenantStorage(req),
appIdentifier,
storage,
null, // clientIdToCheck
"/admin/clients", // proxyPath
true, // proxyToAdmin
true, // camelToSnakeCaseConversion
input, // jsonBody
new HashMap<>(), // headers
(statusCode, headers, rawBody, jsonBody) -> { // handleResponse
(statusCode, headers, rawBody, jsonBody) -> { // getJsonResponse
String clientId = jsonBody.getAsJsonObject().get("clientId").getAsString();

try {
Expand All @@ -120,10 +129,11 @@ protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws IO
try {
Map<String, String> queryParams = new HashMap<>();
queryParams.put("clientId", clientId);
HttpRequest.Response response = OAuth.doOAuthProxyGET(
HttpRequestForOry.Response response = OAuth.doOAuthProxyGET(
main,
getAppIdentifier(req),
enforcePublicTenantAndGetPublicTenantStorage(req),
clientId,
"/admin/clients/" + clientId,
true, true, queryParams, null);

Expand All @@ -145,13 +155,14 @@ protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws IO
main, req, resp,
getAppIdentifier(req),
enforcePublicTenantAndGetPublicTenantStorage(req),
clientId, // clientIdToCheck
"/admin/clients/" + clientId,
true, // proxyToAdmin
true, // camelToSnakeCaseConversion
new HashMap<>(), // queryParams
input, // jsonBody
new HashMap<>(), // headers
(statusCode, headers, rawBody, jsonBody) -> { // handleResponse
(statusCode, headers, rawBody, jsonBody) -> { // getJsonResponse
return jsonBody.getAsJsonObject();
}
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,14 @@ protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws IO
main, req, resp,
getAppIdentifier(req),
enforcePublicTenantAndGetPublicTenantStorage(req),
null, // clientIdToCheck
"/admin/oauth2/auth/requests/consent/accept", // proxyPath
true, // proxyToAdmin
true, // camelToSnakeCaseConversion
OAuthProxyHelper.defaultGetQueryParamsFromRequest(req),
input, // jsonBody
new HashMap<>(), // headers
(statusCode, headers, rawBody, jsonBody) -> { // handleResponse
(statusCode, headers, rawBody, jsonBody) -> { // getJsonResponse
JsonObject response = jsonBody.getAsJsonObject();
response.addProperty("status", "OK");
return response;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws IO
main, req, resp,
getAppIdentifier(req),
enforcePublicTenantAndGetPublicTenantStorage(req),
null, // clientIdToCheck
"/admin/oauth2/auth/requests/login/accept",
true,
true,
Expand Down
Loading

0 comments on commit 110f2f7

Please sign in to comment.