Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ public class OAuth2TokenBody {
private String redirectUri;
private String codeVerifier;

private OAuth2TokenBody() {}

public static OAuth2TokenBody refreshTokenBodyBuilder(String refreshToken) {
OAuth2TokenBody r = new OAuth2TokenBody();
r.grantType = "refresh_token";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import javax.annotation.concurrent.GuardedBy;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.List;
import java.util.UUID;

Expand All @@ -54,6 +55,8 @@
import static com.predic8.membrane.core.interceptor.oauth2.OAuth2TokenBody.authorizationCodeBodyBuilder;
import static com.predic8.membrane.core.interceptor.oauth2.OAuth2TokenBody.refreshTokenBodyBuilder;
import static com.predic8.membrane.core.interceptor.oauth2client.rf.JsonUtils.isJson;
import static java.net.URLEncoder.encode;
import static java.nio.charset.StandardCharsets.UTF_8;

public abstract class AuthorizationService {

Expand All @@ -74,6 +77,7 @@ public abstract class AuthorizationService {
private SSLContext sslContext;
private boolean useJWTForClientAuth;
private final LogHelper logHelper = new LogHelper();
private ClientAuthorization clientAuthorization = ClientAuthorization.client_secret_basic;

protected boolean supportsDynamicRegistration = false;

Expand Down Expand Up @@ -224,6 +228,21 @@ public void setUseJWTForClientAuth(boolean useJWTForClientAuth) {
this.useJWTForClientAuth = useJWTForClientAuth;
}

public ClientAuthorization getClientAuthorization() {
return clientAuthorization;
}

/**
* @description Client Authorization method (see <a
* href="https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication">OIDC
* Core 1.0 chapter 9</a>
* @default client_secret_basic
*/
@MCAttribute
public void setClientAuthorization(ClientAuthorization clientAuthorization) {
this.clientAuthorization = clientAuthorization;
}

public JWSSigner getJwtKeyCertHandler() {
return JWSSigner;
}
Expand All @@ -235,9 +254,17 @@ public Request.Builder applyAuth(Request.Builder requestBuilder, String body, Fl
}

String clientSecret = getClientSecret();
if (clientSecret != null)
if (clientSecret == null) {
requestBuilder.body(body + "&client_id=" + encode(getClientId(), UTF_8));
return requestBuilder;
}
if (clientAuthorization == ClientAuthorization.client_secret_basic) {
requestBuilder.header(AUTHORIZATION, "Basic " + new String(Base64.encodeBase64((getClientId() + ":" + clientSecret).getBytes()))).body(body);
else requestBuilder.body(body + "&client_id=" + getClientId());
return requestBuilder;
}
requestBuilder.body(body +
"&client_id=" + encode(getClientId(), UTF_8) +
"&client_secret=" + encode(clientSecret, UTF_8));
return requestBuilder;
}

Expand Down Expand Up @@ -308,7 +335,8 @@ public OAuth2TokenResponseBody refreshTokenRequest(Session session, String wante
.contentType(APPLICATION_X_WWW_FORM_URLENCODED)
.header(ACCEPT, APPLICATION_JSON)
.header(USER_AGENT, USERAGENT),
refreshTokenBodyBuilder(refreshToken).scope(wantedScope).build(), fc)
refreshTokenBodyBuilder(refreshToken).scope(wantedScope)
.build(), fc)
.buildExchange())));
}

Expand All @@ -319,7 +347,8 @@ public OAuth2TokenResponseBody codeTokenRequest(String redirectUri, String code,
.contentType(APPLICATION_X_WWW_FORM_URLENCODED)
.header(ACCEPT, APPLICATION_JSON)
.header(USER_AGENT, USERAGENT),
authorizationCodeBodyBuilder(code, verifier).redirectUri(redirectUri).build(), flowContext).buildExchange())));
authorizationCodeBodyBuilder(code, verifier).redirectUri(redirectUri)
.build(), flowContext).buildExchange())));
}

private OAuth2TokenResponseBody parseTokenResponse(Response response) throws IOException {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.predic8.membrane.core.interceptor.oauth2.authorizationservice;

/**
* See <a
* href="https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication">OIDC
* Core 1.0 chapter 9</a>.
*/
public enum ClientAuthorization {
client_secret_basic,
client_secret_post
}
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ public Outcome handleRequest(Exchange exc) {
auth.setClientSecret(tc.clientSecret);
auth.setScope("openid profile offline_access");
auth.setSubject("sub");
auth.setClientAuthorization(ClientAuthorization.client_secret_post);
return auth;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@

import static com.predic8.membrane.core.RuleManager.RuleDefinitionSource.*;
import static com.predic8.membrane.core.http.MimeType.*;
import static com.predic8.membrane.core.interceptor.oauth2.ParamNames.*;
import static com.predic8.membrane.core.util.URLParamUtil.DuplicateKeyOrInvalidFormStrategy.ERROR;
import static java.net.URLEncoder.encode;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.jupiter.api.Assertions.*;
Expand Down Expand Up @@ -126,7 +128,7 @@ public Outcome handleRequest(Exchange exc) {
}

public synchronized Response handleRequestInternal(Exchange exc) throws Exception {
Map<String, String> params = URLParamUtil.getParams(new URIFactory(), exc, URLParamUtil.DuplicateKeyOrInvalidFormStrategy.ERROR);
Map<String, String> params = URLParamUtil.getParams(new URIFactory(), exc, ERROR);
String requestURI = exc.getRequestURI();
if (requestURI.endsWith("/.well-known/openid-configuration")) {
return Response.ok(wkf.getWellknown()).build();
Expand Down Expand Up @@ -160,13 +162,15 @@ public synchronized Response handleRequestInternal(Exchange exc) throws Exceptio
}

private Response handleTokenRequest(String flowId, Exchange exc) throws Exception {
Map<String, String> params = URLParamUtil.getParams(new URIFactory(), exc, URLParamUtil.DuplicateKeyOrInvalidFormStrategy.ERROR);
String grantType = params.get("grant_type");
Map<String, String> params = URLParamUtil.getParams(new URIFactory(), exc, ERROR);
assertEquals(tc.clientId, params.get(CLIENT_ID));
assertEquals(tc.clientSecret, params.get(CLIENT_SECRET));
String grantType = params.get(GRANT_TYPE);
if (grantType.equals("authorization_code")) {
assertEquals("1234" + flowId, params.get("code"));
assertEquals("http://localhost:31337/oauth2callback", params.get("redirect_uri"));
assertEquals("1234" + flowId, params.get(CODE));
assertEquals("http://localhost:31337/oauth2callback", params.get(REDIRECT_URI));
} else if (grantType.equals("refresh_token")) {
String refreshToken = params.get("refresh_token");
String refreshToken = params.get(REFRESH_TOKEN);
String flowId2 = refreshTokens.get(refreshToken);
if (flowId2 == null)
throw new RuntimeException("Refresh Token not known.");
Expand All @@ -175,9 +179,6 @@ private Response handleTokenRequest(String flowId, Exchange exc) throws Exceptio
} else {
throw new RuntimeException("Illegal grant_type: " + grantType);
}
String secret = tc.clientId + ":" + tc.clientSecret;

assertEquals("Basic " + Base64.getEncoder().encodeToString(secret.getBytes(UTF_8)) , exc.getRequest().getHeader().getFirstValue("Authorization"));

return Response
.ok(om.writeValueAsString(createTokenResponse(flowId, params)))
Expand All @@ -188,7 +189,7 @@ private Response handleTokenRequest(String flowId, Exchange exc) throws Exceptio
private @NotNull Map<String, Object> createTokenResponse(String flowId, Map<String, String> params) throws JoseException, JsonProcessingException {
Map<String, Object> res = new HashMap<>();

String scope = params.get("scope");
String scope = params.get(SCOPE);

if (scope != null) {
res.put("access_token", createToken(accessToken(flowId, scope.contains(tc.api1Id) ? tc.api1Id : tc.api2Id)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,20 @@ public void errorDuringSignIn() throws Exception {
assertEquals("DEMO-123", pd.getInternal().get("error"));
}

@Test
public void errorDuringSecondOAuth2FlowLogsUserOut() throws Exception {
browser.apply(get(tc.getClientAddress() + "/init"));

var ili = browser.apply(get(tc.getClientAddress() + "/is-logged-in"));
assertTrue(ili.getResponse().getBodyAsStringDecoded().contains("true"));

mockAuthorizationServer.returnOAuth2ErrorFromSignIn.set(true);
browser.apply(get(tc.getClientAddress() + "/pe2/init"));

ili = browser.apply(get(tc.getClientAddress() + "/is-logged-in"));
assertTrue(ili.getResponse().getBodyAsStringDecoded().contains("false"));
}

@Test
public void api1and2() throws Exception {
var exc = browser.apply(get(tc.getClientAddress() + "/api/"));
Expand Down