Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: hydra integration for auth, token and few more endpoints #1032

Merged
merged 40 commits into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
8438685
fix: auth and token api
sattvikc Aug 20, 2024
48cfb7a
fix: cookie and code transformations
sattvikc Aug 21, 2024
fa44c0c
fix: token re-signing
sattvikc Aug 23, 2024
cb79064
fix: token endpoint
sattvikc Aug 26, 2024
393146b
fix: license check and jwks caching
sattvikc Aug 26, 2024
824cbbd
fix: exceptions
sattvikc Aug 27, 2024
cf408d4
fix: refactor
sattvikc Aug 27, 2024
802bbd3
fix: refactor
sattvikc Aug 28, 2024
7839b69
fix: refactor and client crud APIs
sattvikc Aug 29, 2024
8eb8dc2
fix: token type enum
sattvikc Sep 3, 2024
b9b2d68
fix: process ext only on access token
sattvikc Sep 3, 2024
04e7c76
fix: refactor
sattvikc Sep 4, 2024
874ac8c
fix: bugs and refactor
sattvikc Sep 5, 2024
2721620
fix: oauth clients list api
sattvikc Sep 5, 2024
6f03959
fix: consent get accept and reject
sattvikc Sep 5, 2024
41836b3
fix: login request
sattvikc Sep 6, 2024
3a10264
fix: query param transformation
sattvikc Sep 6, 2024
1eddb69
fix: logout request
sattvikc Sep 9, 2024
84fda3f
fix: refactor
sattvikc Sep 9, 2024
7f413c6
fix: remove error debug and hint
sattvikc Sep 9, 2024
c620cdf
fix: introspect api
sattvikc Sep 10, 2024
3e94443
fix: pr comments
sattvikc Sep 11, 2024
b79ddfa
fix: pr comments
sattvikc Sep 11, 2024
ca95c13
fix: pr comment
sattvikc Sep 11, 2024
621befb
fix: pr comment
sattvikc Sep 11, 2024
12c09a0
fix: pr comments and refactor
sattvikc Sep 12, 2024
b6ab81b
fix: pr comments
sattvikc Sep 12, 2024
d87769a
fix: pr comments
sattvikc Sep 12, 2024
085e5c1
fix: revert original http request
sattvikc Sep 12, 2024
bdcdc29
fix: pr comment refactor
sattvikc Sep 12, 2024
d8a1b87
fix: pr comment
sattvikc Sep 16, 2024
360b266
fix: pr comment
sattvikc Sep 16, 2024
f3c022f
fix: pr comment
sattvikc Sep 18, 2024
095a27f
fix: pr comments
sattvikc Sep 18, 2024
da96bc4
fix: owner and pagination
sattvikc Sep 18, 2024
160dce5
fix: pr comment
sattvikc Sep 18, 2024
bd0a918
fix: client id check
sattvikc Sep 18, 2024
b791bf4
fix: ext related
sattvikc Sep 18, 2024
c568f5c
fix: pr comment
sattvikc Sep 19, 2024
bd86375
fix: revoke APIs (#1041)
sattvikc Sep 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ dependencies {

// https://mvnrepository.com/artifact/com.googlecode.libphonenumber/libphonenumber/
implementation group: 'com.googlecode.libphonenumber', name: 'libphonenumber', version: '8.13.25'
implementation group: 'io.jsonwebtoken', name: 'jjwt-api', version: '0.12.6'
porcellus marked this conversation as resolved.
Show resolved Hide resolved
runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-impl', version: '0.12.6'
runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.12.6'

compileOnly project(":supertokens-plugin-interface")
testImplementation project(":supertokens-plugin-interface")
Expand Down
8 changes: 4 additions & 4 deletions devConfig.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -154,17 +154,17 @@ disable_telemetry: true

# (OPTIONAL | Default: null) string value. If specified, the core uses this URL to connect to the OAuth provider
# public service.
# oauth_provider_public_service_url:
oauth_provider_public_service_url: http://localhost:4444

# (OPTIONAL | Default: null) string value. If specified, the core uses this URL to connect to the OAuth provider admin
# service.
# oauth_provider_admin_service_url:
oauth_provider_admin_service_url: http://localhost:4445


# (OPTIONAL | Default: http://localhost:3000) string value. If specified, the core uses this URL replace the default
# consent and login URLs to {apiDomain}.
# oauth_provider_consent_login_base_url:
oauth_provider_consent_login_base_url: http://localhost:3000

# (OPTIONAL | Default: oauth_provider_public_service_url) If specified, the core uses this URL to parse responses from the oauth provider when
# the oauth provider's internal address differs from the known public provider address.
# oauth_provider_url_configured_in_hydra:
# oauth_provider_url_configured_in_hydra: http://localhost:4001
66 changes: 65 additions & 1 deletion src/main/java/io/supertokens/httpRequest/HttpRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ public static <T> T sendGETRequest(Main main, String requestID, String url, Map<
@SuppressWarnings("unchecked")
public static <T> T sendGETRequestWithResponseHeaders(Main main, String requestID, String url,
Map<String, String> params,
Map<String, String> headers,
int connectionTimeoutMS, int readTimeoutMS, Integer version,
Map<String, List<String>> responseHeaders, boolean followRedirects)
throws IOException, HttpResponseException {
Expand Down Expand Up @@ -152,6 +153,11 @@ public static <T> T sendGETRequestWithResponseHeaders(Main main, String requestI
if (version != null) {
con.setRequestProperty("api-version", version + "");
}
if (headers != null) {
for (Map.Entry<String, String> entry : headers.entrySet()) {
con.setRequestProperty(entry.getKey(), entry.getValue());
}
}
con.setInstanceFollowRedirects(followRedirects);
int responseCode = con.getResponseCode();

Expand Down Expand Up @@ -293,4 +299,62 @@ public static <T> T sendJsonDELETERequest(Main main, String requestID, String ur
"DELETE");
}

}
public static <T> T sendFormPOSTRequest(Main main, String requestID, String url, Map<String, String> formData,
int connectionTimeoutMS, int readTimeoutMS, Integer version)
throws IOException, HttpResponseException {
StringBuilder formDataBuilder = new StringBuilder();
for (Map.Entry<String, String> entry : formData.entrySet()) {
formDataBuilder.append(entry.getKey()).append("=")
.append(URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8)).append("&");
}
String formDataStr = formDataBuilder.toString();
if (!formDataStr.equals("")) {
formDataStr = formDataStr.substring(0, formDataStr.length() - 1);
}

URL obj = getURL(main, requestID, url);
HttpURLConnection con = null;
try {
con = (HttpURLConnection) obj.openConnection();
con.setRequestMethod("POST");
con.setConnectTimeout(connectionTimeoutMS);
con.setReadTimeout(readTimeoutMS);
con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
if (version != null) {
con.setRequestProperty("api-version", version + "");
}

con.setDoOutput(true);
try (OutputStream os = con.getOutputStream()) {
os.write(formDataStr.getBytes(StandardCharsets.UTF_8));
}

int responseCode = con.getResponseCode();
InputStream inputStream = null;
if (responseCode < STATUS_CODE_ERROR_THRESHOLD) {
inputStream = con.getInputStream();
} else {
inputStream = con.getErrorStream();
}

StringBuilder response = new StringBuilder();
try (BufferedReader in = new BufferedReader(new InputStreamReader(inputStream))) {
String inputLine;
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
}
if (responseCode < STATUS_CODE_ERROR_THRESHOLD) {
if (!isJsonValid(response.toString())) {
return (T) response.toString();
}
return (T) (new JsonParser().parse(response.toString()));
}
throw new HttpResponseException(responseCode, response.toString());
} finally {
if (con != null) {
con.disconnect();
}
}
}
}
9 changes: 9 additions & 0 deletions src/main/java/io/supertokens/inmemorydb/Start.java
Original file line number Diff line number Diff line change
Expand Up @@ -3046,4 +3046,13 @@ public boolean removeAppClientAssociation(AppIdentifier appIdentifier, String cl
throw new StorageQueryException(e);
}
}

@Override
public List<String> listClientsForApp(AppIdentifier appIdentifier) throws StorageQueryException {
try {
return OAuthQueries.listClientsForApp(this, appIdentifier);
} catch (SQLException e) {
throw new StorageQueryException(e);
}
}
}
17 changes: 17 additions & 0 deletions src/main/java/io/supertokens/inmemorydb/queries/OAuthQueries.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import static io.supertokens.inmemorydb.QueryExecutorTemplate.execute;
import static io.supertokens.inmemorydb.QueryExecutorTemplate.update;
Expand Down Expand Up @@ -51,6 +53,21 @@ public static boolean isClientIdForAppId(Start start, String clientId, AppIdenti
}, ResultSet::next);
}

public static List<String> listClientsForApp(Start start, AppIdentifier appIdentifier)
throws SQLException, StorageQueryException {
String QUERY = "SELECT client_id FROM " + Config.getConfig(start).getOAuthClientTable() +
" WHERE app_id = ?";
return execute(start, QUERY, pst -> {
pst.setString(1, appIdentifier.getAppId());
}, (result) -> {
List<String> res = new ArrayList<>();
while (result.next()) {
res.add(result.getString("client_id"));
}
return res;
});
}

public static void insertClientIdForAppId(Start start, String clientId, AppIdentifier appIdentifier)
throws SQLException, StorageQueryException {
String INSERT = "INSERT INTO " + Config.getConfig(start).getOAuthClientTable()
Expand Down
187 changes: 187 additions & 0 deletions src/main/java/io/supertokens/oauth/HttpRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
package io.supertokens.oauth;

import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import io.supertokens.oauth.exceptions.OAuthClientNotFoundException;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class HttpRequest {
private static final int CONNECTION_TIMEOUT = 5000;
private static final int READ_TIMEOUT = 5000;

public static Response doGet(String url, Map<String, String> headers, Map<String, String> queryParams) throws IOException {
if (queryParams == null) {
queryParams = new HashMap<>();
}
URL obj = new URL(url + "?" + queryParams.entrySet().stream()
.map(e -> e.getKey() + "=" + URLEncoder.encode(e.getValue(), StandardCharsets.UTF_8))
.collect(Collectors.joining("&")));
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
con.setInstanceFollowRedirects(false); // Do not follow redirect
con.setRequestMethod("GET");
con.setConnectTimeout(CONNECTION_TIMEOUT);
con.setReadTimeout(READ_TIMEOUT);
if (headers != null) {
for (Map.Entry<String, String> entry : headers.entrySet()) {
con.setRequestProperty(entry.getKey(), entry.getValue());
}
}
return getResponse(con);
}

public static Response doFormPost(String url, Map<String, String> headers, Map<String, String> formFields) throws IOException, OAuthClientNotFoundException {
try {
URL obj = new URL(url);
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
con.setRequestMethod("POST");
con.setConnectTimeout(CONNECTION_TIMEOUT);
con.setReadTimeout(READ_TIMEOUT);
con.setDoOutput(true);
con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");

if (headers != null) {
for (Map.Entry<String, String> entry : headers.entrySet()) {
con.setRequestProperty(entry.getKey(), entry.getValue());
}
}

try (DataOutputStream os = new DataOutputStream(con.getOutputStream())) {
os.writeBytes(formFields.entrySet().stream()
.map(e -> e.getKey() + "=" + URLEncoder.encode(e.getValue(), StandardCharsets.UTF_8))
.collect(Collectors.joining("&")));
}
return getResponse(con);
} catch (FileNotFoundException e) {
throw new OAuthClientNotFoundException();
}
}

public static Response doJsonPost(String url, Map<String, String> headers, JsonObject jsonInput) throws IOException, OAuthClientNotFoundException {
try {
URL obj = new URL(url);
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
con.setRequestMethod("POST");
con.setConnectTimeout(CONNECTION_TIMEOUT);
con.setReadTimeout(READ_TIMEOUT);
con.setDoOutput(true);
con.setRequestProperty("Content-Type", "application/json");

if (headers != null) {
for (Map.Entry<String, String> entry : headers.entrySet()) {
con.setRequestProperty(entry.getKey(), entry.getValue());
}
}

try (DataOutputStream os = new DataOutputStream(con.getOutputStream())) {
os.writeBytes(jsonInput.toString());
}
return getResponse(con);
} catch (FileNotFoundException e) {
throw new OAuthClientNotFoundException();
}
}

public static Response doJsonPut(String url, Map<String, String> queryParams, Map<String, String> headers, JsonObject jsonInput) throws IOException, OAuthClientNotFoundException {
try {
if (queryParams == null) {
queryParams = new HashMap<>();
}
URL obj = new URL(url + "?" + queryParams.entrySet().stream()
.map(e -> e.getKey() + "=" + URLEncoder.encode(e.getValue(), StandardCharsets.UTF_8))
.collect(Collectors.joining("&")));
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
con.setRequestMethod("PUT");
con.setConnectTimeout(CONNECTION_TIMEOUT);
con.setReadTimeout(READ_TIMEOUT);
con.setDoOutput(true);
con.setRequestProperty("Content-Type", "application/json");

if (headers != null) {
for (Map.Entry<String, String> entry : headers.entrySet()) {
con.setRequestProperty(entry.getKey(), entry.getValue());
}
}

try (DataOutputStream os = new DataOutputStream(con.getOutputStream())) {
os.writeBytes(jsonInput.toString());
}
return getResponse(con);
} catch (FileNotFoundException e) {
throw new OAuthClientNotFoundException();
}
}

public static Response doJsonDelete(String url, Map<String, String> headers, JsonObject jsonInput) throws IOException, OAuthClientNotFoundException {
try {
URL obj = new URL(url);
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
con.setRequestMethod("DELETE");
con.setConnectTimeout(CONNECTION_TIMEOUT);
con.setReadTimeout(READ_TIMEOUT);
con.setDoOutput(true);
con.setRequestProperty("Content-Type", "application/json");

if (headers != null) {
for (Map.Entry<String, String> entry : headers.entrySet()) {
con.setRequestProperty(entry.getKey(), entry.getValue());
}
}

if (jsonInput != null) {
try (DataOutputStream os = new DataOutputStream(con.getOutputStream())) {
os.writeBytes(jsonInput.toString());
}
}

return getResponse(con);
} catch (FileNotFoundException e) {
throw new OAuthClientNotFoundException();
}
}

private static Response getResponse(HttpURLConnection con) throws IOException {
int responseCode = con.getResponseCode();
BufferedReader in;
if (responseCode < 400) {
in = new BufferedReader(new InputStreamReader(con.getInputStream()));
} else {
in = new BufferedReader(new InputStreamReader(con.getErrorStream()));
porcellus marked this conversation as resolved.
Show resolved Hide resolved
}
String inputLine;
StringBuilder response = new StringBuilder();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
JsonElement jsonResponse = null;
if (con.getContentType() != null && con.getContentType().contains("application/json")) {
Gson gson = new Gson();
jsonResponse = gson.fromJson(response.toString(), JsonElement.class);
}
return new Response(responseCode, response.toString(), jsonResponse, con.getHeaderFields());
}

public static class Response {
public int statusCode;
public String rawResponse;
public JsonElement jsonResponse;
public Map<String, List<String>> headers;

public Response(int statusCode, String rawResponse, JsonElement jsonResponse, Map<String, List<String>> headers) {
this.statusCode = statusCode;
this.rawResponse = rawResponse;
this.jsonResponse = jsonResponse;
this.headers = headers;
}
}
}
Loading
Loading