Skip to content

Commit

Permalink
loginbuddy now proxies calls to the providers userinfo endpoint if th…
Browse files Browse the repository at this point in the history
…ey are made within 60s after issuing the token
  • Loading branch information
SaschaZeGerman committed Sep 13, 2019
1 parent 5b2708d commit 0d2090a
Show file tree
Hide file tree
Showing 10 changed files with 129 additions and 33 deletions.
17 changes: 10 additions & 7 deletions apitest/soapui/project/loginbuddy-with-provider.xml

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@
import java.util.List;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import net.loginbuddy.common.config.Constants;
import net.loginbuddy.common.util.MsgResponse;
import net.loginbuddy.common.util.ParameterValidatorResult;
import net.loginbuddy.common.util.ParameterValidatorResult.RESULT;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
Expand Down Expand Up @@ -204,6 +207,23 @@ public static String stringArrayToString(String[] jsonArray, String separator) {
return str.substring(1, str.length() - 1).replace(",", separator.matches("[,; ]") ? separator : " ");
}

public static String extractAccessToken(ParameterValidatorResult accessTokenParam, String authHeader) {
String token = null;
if(authHeader != null && authHeader.trim().length() > 0 && accessTokenParam.getResult().equals(RESULT.NONE)) {
if(Stream.of(authHeader.split(" ")).anyMatch("bearer"::equalsIgnoreCase)) {
token = authHeader.split(" ")[1];
}
}
if(accessTokenParam.getResult().equals(RESULT.VALID) && authHeader == null) {
token = accessTokenParam.getValue();
}
if(token == null ) {
LOGGER.warning("the access_token is missing or was provided multiple times");
throw new IllegalArgumentException(getErrorAsJson("invalid_request", "Either none or multiple access_token were provided").toJSONString());
}
return token;
}

public static String urlEncode(String input) {
try {
return URLEncoder.encode(input, "UTF-8");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,14 @@ public void run() {
}

/**
* Any value added has a default lifetime of 120. For other lifetimes, use {@link #putWithExpiration(String, Object, Long)}.
* Any value added has a default lifetime of 120. For other lifetimes, use {@link #put(String, Object, Long)}.
*
* @param key
* @param obj
* @return
*/
public Object put(String key, Object obj) {
return putWithExpiration(key, obj, 120L);
return put(key, obj, 120L);
}

/**
Expand All @@ -86,7 +86,7 @@ public Object put(String key, Object obj) {
* @param lifetimeInSeconds for 'null' the lifetime is set to 120
* @return
*/
public Object putWithExpiration(String key, Object obj, Long lifetimeInSeconds) {
public Object put(String key, Object obj, Long lifetimeInSeconds) {

if(lifetimeInSeconds == null || lifetimeInSeconds > 3600 || lifetimeInSeconds <=0) {
lifetimeInSeconds = 120L;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
package net.loginbuddy.common.util;

import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import net.loginbuddy.common.util.ParameterValidatorResult.RESULT;

public class ParameterValidator {

public static ParameterValidatorResult getSingleValue(Enumeration<String> input) {
return getSingleValue(input, null);
}

public static ParameterValidatorResult getSingleValue(Enumeration<String> input, String defaultValue) {
List<String> values = Collections.list(input);
return getSingleValue( values.toArray(new String[0]), defaultValue);
}

/**
* the array should have one, non-empty string
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,8 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response)
long accessTokenLifetime = sessionValues.sessionToken(access_token, refresh_token, id_token);

// associate with access_token. We'll ignore the refresh_token for now. Remember, this is all 'fake'
LoginbuddyCache.getInstance().putWithExpiration(access_token, sessionValues, accessTokenLifetime);
LoginbuddyCache.getInstance().putWithExpiration(refresh_token, sessionValues, sessionValues.get("refresh_token_expiration", Long.class));
LoginbuddyCache.getInstance().put(access_token, sessionValues, accessTokenLifetime);
LoginbuddyCache.getInstance().put(refresh_token, sessionValues, sessionValues.get("refresh_token_expiration", Long.class));

// create the response message that includes the issued token
JSONObject fakeProviderResponse = new JSONObject();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,20 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t
sessionCtx.put(Constants.ACTION_EXPECTED.getKey(), Constants.ACTION_TOKEN_EXCHANGE.getKey());
LoginbuddyCache.getInstance().put(authorizationCode, sessionCtx);

// ***************************************************************
// ** Create a session to be used if the client wants to call the providers Userinfo endpoint itself. Loginbuddy will proxies those calls
// ***************************************************************

JSONObject jo = new JSONObject();
jo.put(Constants.USERINFO_ENDPOINT.getKey(), sessionCtx.getString(Constants.USERINFO_ENDPOINT.getKey()));
jo.put(Constants.JWKS_URI.getKey(), sessionCtx.getString(Constants.JWKS_URI.getKey()));
String[] hint = access_token.split(".");
if(hint.length == 3) {
LoginbuddyCache.getInstance().put(hint[2], jo);
} else {
LoginbuddyCache.getInstance().put(access_token, jo, 60L);
}

response.sendRedirect(getMessageForRedirect(sessionCtx.getString(Constants.CLIENT_REDIRECT_VALID.getKey()), "code", authorizationCode));

} catch (Exception e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,13 @@ public ProviderConfig getProviderConfigByProvider(String providerHint) {
.orElse(null);
}

public ProviderConfig getProviderConfigByIssuer(String issuerHint) {
return getProviders().stream()
.filter(provider -> provider.getIssuer().equalsIgnoreCase(issuerHint))
.findFirst()
.orElse(null);
}

public ProviderConfig getProviderConfigFromJsonString(String providerHint) {
try {
return MAPPER.readValue(providerHint, ProviderConfig.class);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package net.loginbuddy.service.resources;

import java.io.IOException;
import java.util.Base64;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.loginbuddy.common.api.HttpHelper;
import net.loginbuddy.common.cache.LoginbuddyCache;
import net.loginbuddy.common.config.Constants;
import net.loginbuddy.common.util.MsgResponse;
import net.loginbuddy.common.util.ParameterValidator;
import net.loginbuddy.common.util.ParameterValidatorResult;
import net.loginbuddy.service.config.LoginbuddyConfig;
import net.loginbuddy.service.config.ProviderConfig;
import net.loginbuddy.service.server.Overlord;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import sun.misc.BASE64Decoder;

public class Userinfo extends Overlord {

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

ParameterValidatorResult accessToken = ParameterValidator.getSingleValue(request.getParameterValues("access_token"));
String authorizationHeader = request.getHeader("Authorization");

String hint;
String[] token = HttpHelper.extractAccessToken(accessToken, authorizationHeader).split(".");
if(token.length == 3) {
hint = token[2];
} else {
hint = accessToken.getValue();
}

MsgResponse msg;
JSONObject apis = (JSONObject)LoginbuddyCache.getInstance().get(hint);
if(apis == null) {
msg = new MsgResponse();
msg.setStatus(400);
msg.setContentType("application/json");
msg.setMsg(HttpHelper.getErrorAsJson("invalid_request", "the given token is unknown").toJSONString());
} else {
msg = HttpHelper.getAPI(accessToken.getValue(), (String)apis.get(Constants.USERINFO_ENDPOINT.getKey()));
}

response.setStatus(msg.getStatus());
response.setContentType(msg.getContentType());
response.getWriter().write(msg.getMsg());
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}

This file was deleted.

5 changes: 3 additions & 2 deletions net.loginbuddy.service/web/WEB-INF/web.xml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@
</filter>
<filter-mapping>
<filter-name>httpHeaderSecurity</filter-name>
<url-pattern>/*</url-pattern>
<url-pattern>/error.jsp</url-pattern>
<url-pattern>/providers.jsp</url-pattern>
</filter-mapping>

<servlet>
Expand Down Expand Up @@ -111,7 +112,7 @@
</servlet>
<servlet>
<servlet-name>Userinfo</servlet-name>
<servlet-class>net.loginbuddy.service.server.Userinfo</servlet-class>
<servlet-class>net.loginbuddy.service.resources.Userinfo</servlet-class>
</servlet>
<!-- sidecar resources -->
<servlet>
Expand Down

0 comments on commit 0d2090a

Please sign in to comment.