Skip to content

Commit 8d1b17a

Browse files
committed
Added OIDC discovery support
1 parent 1e34d07 commit 8d1b17a

File tree

2 files changed

+89
-1
lines changed

2 files changed

+89
-1
lines changed

capabilities-security/src/main/java/ai/wanaku/capabilities/sdk/security/ServiceAuthenticator.java

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.nimbusds.oauth2.sdk.AccessTokenResponse;
66
import com.nimbusds.oauth2.sdk.AuthorizationGrant;
77
import com.nimbusds.oauth2.sdk.ClientCredentialsGrant;
8+
import com.nimbusds.oauth2.sdk.GeneralException;
89
import com.nimbusds.oauth2.sdk.ParseException;
910
import com.nimbusds.oauth2.sdk.RefreshTokenGrant;
1011
import com.nimbusds.oauth2.sdk.TokenErrorResponse;
@@ -13,13 +14,19 @@
1314
import com.nimbusds.oauth2.sdk.auth.ClientAuthentication;
1415
import com.nimbusds.oauth2.sdk.auth.ClientSecretBasic;
1516
import com.nimbusds.oauth2.sdk.auth.Secret;
17+
import com.nimbusds.oauth2.sdk.http.HTTPRequest;
18+
import com.nimbusds.oauth2.sdk.http.HTTPResponse;
1619
import com.nimbusds.oauth2.sdk.id.ClientID;
20+
import com.nimbusds.oauth2.sdk.id.Issuer;
1721
import com.nimbusds.oauth2.sdk.token.AccessToken;
1822
import com.nimbusds.oauth2.sdk.token.RefreshToken;
23+
import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
1924
import java.io.IOException;
2025
import java.net.URI;
26+
import java.net.URL;
2127
import java.time.Duration;
2228
import java.time.Instant;
29+
import net.minidev.json.JSONObject;
2330
import org.slf4j.Logger;
2431
import org.slf4j.LoggerFactory;
2532

@@ -66,7 +73,7 @@ private void renewToken(SecurityServiceConfig config) {
6673
private TokenRequest createTokenRequest(SecurityServiceConfig config) {
6774
final ClientAuthentication clientAuth = getClientAuthentication(config);
6875

69-
URI tokenEndpoint = URI.create(config.getTokenEndpoint());
76+
final URI tokenEndpoint = resolveTokenEndpointUri(config);
7077

7178
TokenRequest request;
7279
if (refreshToken == null) {
@@ -80,6 +87,59 @@ private TokenRequest createTokenRequest(SecurityServiceConfig config) {
8087
return request;
8188
}
8289

90+
/*
91+
* We cannot use OIDCProviderMetadata.resolve directly, because it validates the provided
92+
* config endpoint with the issuer endpoint. However, because Wanaku typically uses the
93+
* OIDC Proxy, they are not the same (which causes it to throw a GeneralException).
94+
* This mimics the resolve logic, but ignores the validation and other things we don't
95+
* need.
96+
*/
97+
private static Issuer resolveIssuer(SecurityServiceConfig config) {
98+
// The OpenID provider issuer URL
99+
Issuer issuer = new Issuer(config.getTokenEndpoint());
100+
101+
try {
102+
final URL openIdConfigUrl = OIDCProviderMetadata.resolveURL(issuer);
103+
104+
HTTPRequest httpRequest = new HTTPRequest(HTTPRequest.Method.GET, openIdConfigUrl);
105+
HTTPResponse httpResponse = httpRequest.send();
106+
107+
if (httpResponse.getStatusCode() != 200) {
108+
throw new ServiceAuthException("Unable to download OpenID Provider metadata from " + openIdConfigUrl
109+
+ ": Status code " + httpResponse.getStatusCode());
110+
}
111+
112+
JSONObject jsonObject = httpResponse.getBodyAsJSONObject();
113+
114+
OIDCProviderMetadata op = OIDCProviderMetadata.parse(jsonObject);
115+
116+
return op.getIssuer();
117+
} catch (GeneralException e) {
118+
throw new ServiceAuthException("Unable to resolve token endpoint URI: " + e.getMessage(), e);
119+
} catch (IOException e) {
120+
throw new ServiceAuthException("I/O error while resolving token endpoint URI: " + e.getMessage(), e);
121+
}
122+
}
123+
124+
private static URI resolveTokenEndpointUri(SecurityServiceConfig config) {
125+
126+
/* We cannot use Wanaku's base address because it's typically behind the OIDC
127+
* proxy. Therefore, we first need to resolve the issuer address, and then
128+
* use the issuer address to resolve the OIDC metadata.
129+
*/
130+
Issuer issuer = new Issuer(resolveIssuer(config));
131+
132+
try {
133+
final OIDCProviderMetadata resolvedOp = OIDCProviderMetadata.resolve(issuer);
134+
135+
return resolvedOp.getTokenEndpointURI();
136+
} catch (GeneralException e) {
137+
throw new ServiceAuthException("Unable to resolve token endpoint URI: " + e.getMessage(), e);
138+
} catch (IOException e) {
139+
throw new ServiceAuthException("I/O error while resolving token endpoint URI: " + e.getMessage(), e);
140+
}
141+
}
142+
83143
/**
84144
* Creates client authentication for OAuth2 requests.
85145
*

capabilities-security/src/main/java/ai/wanaku/capabilities/sdk/security/TokenEndpoint.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,32 @@ public static String direct(String uri) {
3030
public static String fromBaseUrl(String baseUrl) {
3131
return baseUrl + "/protocol/openid-connect/token";
3232
}
33+
34+
/**
35+
* Constructs a token endpoint URL by appending the discovery OpenID Connect path to a base URL.
36+
*
37+
* @param baseUrl The base URL of the authentication server.
38+
* @return The complete token endpoint URL.
39+
*/
40+
public static String forDiscovery(String baseUrl) {
41+
return baseUrl + "/q/oidc/";
42+
}
43+
44+
/**
45+
* Automatically resolve the best URI to use
46+
* @param registrationUri
47+
* @param tokenEndpointUri
48+
* @return
49+
*/
50+
public static String autoResolve(String registrationUri, String tokenEndpointUri) {
51+
if (tokenEndpointUri != null) {
52+
if (!tokenEndpointUri.isEmpty() && !tokenEndpointUri.endsWith("/realms/wanaku/")) {
53+
return direct(tokenEndpointUri);
54+
}
55+
56+
return fromBaseUrl(tokenEndpointUri);
57+
}
58+
59+
return forDiscovery(registrationUri);
60+
}
3361
}

0 commit comments

Comments
 (0)