From b58d4ba85a510a16994376f4f6e00ffcbcf61d53 Mon Sep 17 00:00:00 2001 From: Justin Dahmubed Date: Fri, 8 Dec 2017 15:09:33 -0800 Subject: [PATCH 1/4] Not officially supported Google product --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 487c06d..c341369 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # JWTs for Java + This is not an officially supported Google product [![CircleCI](https://img.shields.io/circleci/project/github/auth0/java-jwt.svg?style=flat-square)](https://circleci.com/gh/auth0/java-jwt/tree/master) From e409972b14e9b27c604225529bb116a35e92aedf Mon Sep 17 00:00:00 2001 From: Justin Dahmubed Date: Fri, 2 Feb 2018 16:16:16 -0800 Subject: [PATCH 2/4] Oicclient --- .../java/com/auth0/jwt/creators/Message.java | 61 +++- .../auth0/jwt/oiccli/AccessTokenRequest.java | 10 + .../auth0/jwt/oiccli/AccessTokenResponse.java | 6 + .../jwt/oiccli/AuthorizationRequest.java | 49 +++ .../jwt/oiccli/AuthorizationResponse.java | 21 ++ .../java/com/auth0/jwt/oiccli/Database.java | 13 + .../java/com/auth0/jwt/oiccli/Service.java | 303 ++++++++++++++++++ .../main/java/com/auth0/jwt/oiccli/State.java | 150 +++++++++ .../java/com/auth0/jwt/oiccli/StateInfo.java | 35 ++ .../java/com/auth0/jwt/oiccli/StringUtil.java | 28 ++ .../main/java/com/auth0/jwt/oiccli/Token.java | 46 +++ .../auth0/jwt/oiccli/Utils/ClientInfo.java | 280 ++++++++++++++++ .../java/com/auth0/jwt/oiccli/Utils/JWE.java | 19 ++ .../java/com/auth0/jwt/oiccli/Utils/PKCE.java | 73 +++++ .../com/auth0/jwt/oiccli/Utils/Utils.java | 80 +++++ .../jwt/oiccli/client_auth/BearerBody.java | 59 ++++ .../jwt/oiccli/client_auth/BearerHeader.java | 49 +++ .../ClientAuthenticationMethod.java | 4 + .../oiccli/client_auth/ClientSecretBasic.java | 60 ++++ .../oiccli/client_auth/ClientSecretJWT.java | 23 ++ .../oiccli/client_auth/ClientSecretPost.java | 31 ++ .../client_auth/JWSAuthenticationMethod.java | 117 +++++++ .../jwt/oiccli/client_auth/PrivateKeyJwt.java | 40 +++ .../exceptions/AuthenticationFailure.java | 7 + .../jwt/oiccli/exceptions/ExpiredToken.java | 7 + .../jwt/oiccli/exceptions/HTTPError.java | 7 + .../oiccli/exceptions/MissingEndpoint.java | 7 + .../exceptions/MissingRequiredAttribute.java | 7 + .../jwt/oiccli/exceptions/NoMatchingKey.java | 7 + .../jwt/oiccli/exceptions/UnknownState.java | 7 + .../oiccli/exceptions/UnsupportedType.java | 7 + .../jwt/oiccli/exceptions/ValueError.java | 7 + .../jwt/oiccli/exceptions/WebFingerError.java | 8 + .../oiccli/exceptions/WrongContentType.java | 7 + .../jwt/oiccli/responses/ErrorResponse.java | 23 ++ .../auth0/jwt/oiccli/responses/Response.java | 16 + .../com/auth0/jwt/oiccli/tuples/Tuple.java | 20 ++ .../com/auth0/jwt/oiccli/tuples/Tuple5.java | 58 ++++ .../auth0/jwt/oiccli/util/FakeResponse.java | 27 ++ .../com/auth0/jwt/oiccli/util/Header.java | 10 + .../java/com/auth0/jwt/oiccli/util/Util.java | 232 ++++++++++++++ .../com/auth0/jwt/oiccli/webfinger/JRD.java | 8 + .../jwt/oiccli/webfinger/URINormalizer.java | 48 +++ .../auth0/jwt/oiccli/webfinger/WebFinger.java | 145 +++++++++ 44 files changed, 2218 insertions(+), 4 deletions(-) create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/AccessTokenRequest.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/AccessTokenResponse.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/AuthorizationRequest.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/AuthorizationResponse.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/Database.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/Service.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/State.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/StateInfo.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/StringUtil.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/Token.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/Utils/ClientInfo.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/Utils/JWE.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/Utils/PKCE.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/Utils/Utils.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/client_auth/BearerBody.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/client_auth/BearerHeader.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/client_auth/ClientAuthenticationMethod.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/client_auth/ClientSecretBasic.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/client_auth/ClientSecretJWT.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/client_auth/ClientSecretPost.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/client_auth/JWSAuthenticationMethod.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/client_auth/PrivateKeyJwt.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/exceptions/AuthenticationFailure.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/exceptions/ExpiredToken.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/exceptions/HTTPError.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/exceptions/MissingEndpoint.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/exceptions/MissingRequiredAttribute.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/exceptions/NoMatchingKey.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/exceptions/UnknownState.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/exceptions/UnsupportedType.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/exceptions/ValueError.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/exceptions/WebFingerError.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/exceptions/WrongContentType.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/responses/ErrorResponse.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/responses/Response.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/tuples/Tuple.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/tuples/Tuple5.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/util/FakeResponse.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/util/Header.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/util/Util.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/webfinger/JRD.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/webfinger/URINormalizer.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/webfinger/WebFinger.java diff --git a/lib/src/main/java/com/auth0/jwt/creators/Message.java b/lib/src/main/java/com/auth0/jwt/creators/Message.java index 45d252c..dd26a8e 100644 --- a/lib/src/main/java/com/auth0/jwt/creators/Message.java +++ b/lib/src/main/java/com/auth0/jwt/creators/Message.java @@ -19,10 +19,10 @@ package com.auth0.jwt.creators; +import com.auth0.jwt.oiccli.Token; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.Gson; - import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; @@ -30,9 +30,19 @@ import java.util.HashMap; import java.util.Map; -public class Message { +public class Message implements Cloneable{ + + private String state; + private String code; + private String claim; + private String accessToken; + private Integer expiresIn; + private String tokenType; + private String scope; + private Token idToken; + private String refreshToken; - public String toUrlEncoded(String json) throws UnsupportedEncodingException { + public String toUrlEncoded() throws UnsupportedEncodingException, NoSuchFieldException { return URLEncoder.encode(json, "UTF-8"); } @@ -40,7 +50,7 @@ public String toUrlDecoded(String urlEncoded) throws UnsupportedEncodingExceptio return URLDecoder.decode(urlEncoded, "UTF-8"); } - public String toJSON(HashMap hashMap) { + public String toJSON(Map hashMap) { return new Gson().toJson(hashMap); } @@ -48,4 +58,47 @@ public HashMap fromJSON(String json) throws IOException { return new ObjectMapper().readValue(json, new TypeReference>(){}); } + public Map getCParam() { + throw new UnsupportedOperationException(); + } + + public String getState() { + return state; + } + + public String getCode() { + return code; + } + + public String getClaim() { + return claim; + } + + public String getAccessToken() { + return accessToken; + } + + public Integer getExpiresIn() { + return expiresIn; + } + + public String getTokenType() { + return tokenType; + } + + public String getScope() { + return scope; + } + + public Token getIdToken() { + return idToken; + } + + public String getRefreshToken() { + return refreshToken; + } + + public Object clone() throws CloneNotSupportedException{ + return super.clone(); + } } \ No newline at end of file diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/AccessTokenRequest.java b/lib/src/main/java/com/auth0/jwt/oiccli/AccessTokenRequest.java new file mode 100644 index 0000000..5d540ea --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/AccessTokenRequest.java @@ -0,0 +1,10 @@ +package com.auth0.jwt.oiccli; + +import com.auth0.jwt.oiccli.tuples.Tuple; +import java.util.Map; + +public class AccessTokenRequest { + public Map getCParam() { + throw new UnsupportedOperationException(); + } +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/AccessTokenResponse.java b/lib/src/main/java/com/auth0/jwt/oiccli/AccessTokenResponse.java new file mode 100644 index 0000000..d8cde70 --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/AccessTokenResponse.java @@ -0,0 +1,6 @@ +package com.auth0.jwt.oiccli; + +import com.auth0.jwt.creators.Message; + +public class AccessTokenResponse extends Message{ +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/AuthorizationRequest.java b/lib/src/main/java/com/auth0/jwt/oiccli/AuthorizationRequest.java new file mode 100644 index 0000000..fe727d9 --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/AuthorizationRequest.java @@ -0,0 +1,49 @@ +package com.auth0.jwt.oiccli; + +import com.auth0.jwt.creators.Message; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Field; +import java.net.URLEncoder; +import java.util.HashMap; +import java.util.Map; + +public class AuthorizationRequest extends Message{ + + private String responseType; + private String clientId; + private String redirectUri; + + public String toUrlEncoded() throws UnsupportedEncodingException, NoSuchFieldException { + Field[] fields = this.getClass().getFields(); + String fieldString; + StringBuilder sb = new StringBuilder(); + for(int i = 0; i < fields.length; i++) { + fieldString = fields[i].toString(); + fieldString = fieldString.substring(fieldString.indexOf(".")+1); + sb.append(fieldString+"="+this.getClass().getField(fieldString)); + if(i != fields.length-1) { + sb.append("&"); + } + } + return URLEncoder.encode(sb.toString(), "UTF-8"); + } + + public String toJSON() throws NoSuchFieldException, IllegalAccessException { + Field[] fields = this.getClass().getFields(); + String fieldString; + StringBuilder sb = new StringBuilder(); + Map hMap = new HashMap<>(); + for(int i = 0; i < fields.length; i++) { + fieldString = fields[i].toString(); + fieldString = fieldString.substring(fieldString.indexOf(".")+1); + hMap.put(fieldString, this.getClass().getField(fieldString).get(this)); + } + return toJSON(hMap); + } + + public void update(Map hMap) throws NoSuchFieldException, IllegalAccessException { + for(String key : hMap.keySet()) { + this.getClass().getField(key).set(this, hMap.get(key)); + } + } +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/AuthorizationResponse.java b/lib/src/main/java/com/auth0/jwt/oiccli/AuthorizationResponse.java new file mode 100644 index 0000000..d843192 --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/AuthorizationResponse.java @@ -0,0 +1,21 @@ +package com.auth0.jwt.oiccli; + +import com.auth0.jwt.creators.Message; + +public class AuthorizationResponse extends Message{ + + private String uri; + private String body; + + public String getUri() { + return uri; + } + + public void setUri(String uri) { + this.uri = uri; + } + + public void setBody(String body) { + this.body = body; + } +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/Database.java b/lib/src/main/java/com/auth0/jwt/oiccli/Database.java new file mode 100644 index 0000000..c623db2 --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/Database.java @@ -0,0 +1,13 @@ +package com.auth0.jwt.oiccli; + +import java.util.Map; + +public class Database { + public Map get(String s) { + throw new UnsupportedOperationException(); + } + + public StateInfo getStateInfo(String s) { + throw new UnsupportedOperationException(); + } +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/Service.java b/lib/src/main/java/com/auth0/jwt/oiccli/Service.java new file mode 100644 index 0000000..ea2990d --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/Service.java @@ -0,0 +1,303 @@ +package com.auth0.jwt.oiccli; + +import com.auth0.jwt.creators.Message; +import com.auth0.jwt.oiccli.Utils.ClientInfo; +import com.auth0.jwt.oiccli.exceptions.HTTPError; +import com.auth0.jwt.oiccli.exceptions.MissingEndpoint; +import com.auth0.jwt.oiccli.exceptions.ValueError; +import com.auth0.jwt.oiccli.exceptions.WrongContentType; +import com.auth0.jwt.oiccli.responses.ErrorResponse; +import com.auth0.jwt.oiccli.responses.Response; +import com.auth0.jwt.oiccli.util.FakeResponse; +import com.auth0.jwt.oiccli.util.Util; +import com.google.common.base.Strings; +import java.net.URI; +import java.net.URISyntaxException; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.slf4j.LoggerFactory; + +public class Service { + + private final static org.slf4j.Logger logger = LoggerFactory.getLogger(Service.class); + private static final List successfulCodes = + Arrays.asList(200, 201, 202, 203, 204, 205, 206); + private static final List specialArgs = Arrays.asList("authenticationEndpoint", "algs"); + public Message msgType; + public Message responseCls; + public ErrorResponse errorMessage; + private String endpointName; + private boolean isSynchronous = true; + private String request; + public String defaultAuthenticationMethod; + public String httpMethod; + public String bodyType; + public String responseBodyType; + private String endpoint; + private String httpLib; + private KeyJar keyJar; + private String clientAuthenticationMethod; + private List events; + private Map defaultRequestArgs; + private List preConstruct; + private List postConstruct; + private List postParseResponse; + private Map conf; + + public Service(String httpLib, KeyJar keyJar, String clientAuthenticationMethod, Map conf, Map args) throws NoSuchFieldException, IllegalAccessException { + this.httpLib = httpLib; + this.keyJar = keyJar; + this.clientAuthenticationMethod = clientAuthenticationMethod; + this.events = new ArrayList<>(); + this.endpoint = ""; + this.defaultRequestArgs = new HashMap<>(); + + if(conf != null) { + this.conf = conf; + List params = Arrays.asList("msgType", "responseCls", "errorMessage", "defaultAuthenticationMethod", + "httpMethod", "bodyType", "responseBodyType"); + for(String param : params) { + if(conf.containsKey(param)) { + this.getClass().getField(param).set(this, conf.get("param")); + } + } + } else { + this.conf = new HashMap<>(); + } + + this.preConstruct = new ArrayList<>(); + this.postConstruct = new ArrayList<>(); + this.postParseResponse = new ArrayList<>(); + } + + public void gatherRequestArgs() { + throw new UnsupportedOperationException(); + } + + public void doPreConstruct() { + throw new UnsupportedOperationException(); + } + + public void doPostConstruct() { + throw new UnsupportedOperationException(); + } + + public void doPostParseResponse() { + throw new UnsupportedOperationException(); + } + + public void construct() { + throw new UnsupportedOperationException(); + } + + public static Map> updateHttpArgs(Map httpArgs, Map> info) { + Map hArgs = info.get("hArgs"); + if (hArgs == null) { + hArgs = new HashMap<>(); + } + + if (httpArgs == null) { + httpArgs = hArgs; + } else { + httpArgs.update(info.get("hArgs")); + } + + final String headers = info.get("kwargs").get("headers"); + Map hMap = new HashMap() {{ + put("headers", headers); + }}; + httpArgs.update(hMap); + + info.put("httpArgs", httpArgs); + return info; + } + + public Map parseArgs(ClientInfo clientInfo, Map args) throws NoSuchFieldException, IllegalAccessException { + Map arArgs = new HashMap<>(args); + Object value; + for (String property : this.msgType.getCParam().keySet()) { + if (!arArgs.containsKey(property)) { + value = clientInfo.getClass().getField(property).get(this); + if (value != null) { + arArgs.put(property, value); + } else { + arArgs.put(property, this.defaultRequestArgs.get(property)); + } + } + } + + return arArgs; + } + + public String getEndpoint(Map args) throws MissingEndpoint { + String uri = args.get("endpoint"); + + if (uri != null) { + args.remove("endpoint"); + } else { + if (!Strings.isNullOrEmpty(endpoint)) { + uri = this.endpoint; + } else { + throw new MissingEndpoint("No endpoint specified"); + } + } + + return uri; + } + + public Map uriAndBody(Message cis, String method, Map args) throws UnsupportedEncodingException, UnsupportedType, MissingEndpoint { + String uri = this.getEndpoint(args); + Map response = Util.getOrPost(uri, method, cis, args); + response.put("cis", cis); + Map hMap = new HashMap<>(); + hMap.put("headers", response.get("headers")); + response.put("hArgs", hMap); + + return response; + } + + public Map> doRequestInit(ClientInfo clientInfo, String bodyType, String method, String authenticationMethod, + Map requestArgs, Map httpArgs, Map args) throws NoSuchFieldException, IllegalAccessException, MissingEndpoint, UnsupportedEncodingException, UnsupportedType { + if (Strings.isNullOrEmpty(method)) { + method = this.httpMethod; + } + if (Strings.isNullOrEmpty(authenticationMethod)) { + authenticationMethod = this.defaultAuthenticationMethod; + } + if (Strings.isNullOrEmpty(bodyType)) { + bodyType = this.bodyType; + } + + Map> info = this.requestInfo(clientInfo, method, requestArgs, bodyType, authenticationMethod, false, args); + + return this.updateHttpArgs(httpArgs, info); + } + + private Map> requestInfo(ClientInfo clientInfo, String method, Map requestArgs, String bodyType, String authenticationMethod, boolean b, Map args) { + throw new UnsupportedOperationException(); + } + + public String getUrlInfo(String info) throws URISyntaxException { + String query = null; + String fragment = null; + if (info.contains("?") || info.contains("#")) { + URI uri = new URI(info); + query = uri.getQuery(); + fragment = uri.getFragment(); + } + + if (!Strings.isNullOrEmpty(query)) { + return query; + } else { + return fragment; + } + } + + public static ErrorResponse parseErrorMessage(String responseText, String bodyType) { + String bodyTypeResult; + if (bodyType.equals("txt")) { + bodyTypeResult = "urlencoded"; + } else { + bodyTypeResult = bodyType; + } + + ErrorResponse errorResponse = new ErrorResponse().deserialize(responseText, bodyTypeResult); + errorResponse.verify(); + + return errorResponse; + } + + public static String getValueType(FakeResponse response, String bodyType) throws WrongContentType, ValueError { + if (!Strings.isNullOrEmpty(bodyType)) { + return Util.verifyHeader(response, bodyType); + } else { + return "urlencoded"; + } + } + + public static Response parseRequestResponse(FakeResponse response, ClientInfo clientInfo, String responseBodyType, + String state, Map args) throws ParseException, WrongContentType, ValueError, HTTPError { + int statusCode = response.getStatusCode(); + if (successfulCodes.contains(statusCode)) { + logger.debug("Response body type " + responseBodyType); + String type = Util.getResponseBodyType(response); + + if(!type.equals(responseBodyType)) { + logger.warn("Not the body type I expected: " + type + "!=" + responseBodyType); + } + List types = Arrays.asList("json", "jwt", "urlencoded"); + String valueType; + if(types.contains(type)) { + valueType = type; + } else { + valueType = responseBodyType; + } + logger.debug("Successful response " + response.getText()); + return parseResponse(response.getText(), clientInfo, valueType, state, args); + } else if (statusCode == 302 || statusCode == 303) { + return response; + } else if (statusCode == 500) { + logger.error("(" + statusCode + ")" + response.getText()); + throw new ParseException("ERROR: Something went wrong " + response.getText(), 0); + } else if (statusCode >= 400 && statusCode < 500) { + logger.error("Error response (" + statusCode + "): " + response.getText()); + String valueType = getValueType(response, responseBodyType); + return parseErrorMessage(response.getText(), valueType); + } else { + logger.error("Error response (" + statusCode + "):" + response.getText()); + throw new HTTPError("HTTP ERROR: " + response.getText() + "[" + statusCode + "]" + " on " + response.getUrl()); + } + } + + public Response serviceRequest(String url, String method, String body, String responseBodyType, Map httpArgs, + ClientInfo clientInfo, Map args) throws ParseException, ValueError, WrongContentType { + if (httpArgs == null) { + httpArgs = new HashMap<>(); + } + + logger.debug("Doing request with: URL: " + url + ", method: " + method + ", data: " + body + ", https_args: " + httpArgs); + FakeResponse response = this.httpLib(url, method, body, httpArgs); + + if (!args.containsKey("keyjar")) { + args.put("keyjar", this.keyJar); + } + if (responseBodyType == null) { + responseBodyType = this.responseBodyType; + } + + return parseRequestResponse(response, clientInfo, responseBodyType, "", args); + } + + public String getConfigurationAttribute(String attribute, String defaultValue) { + if(this.conf.containsKey(attribute)) { + return this.conf.get(attribute); + } else { + return defaultValue; + } + } + + public void buildServices() { + throw new UnsupportedOperationException(); + } + + public Response serviceRequest(String url, Map args) throws ParseException, ValueError, WrongContentType { + return serviceRequest(url, "GET", null, "", null, null, args); + } + + public Response serviceRequest(String url, String method, ClientInfo clientInfo, Map args) throws ParseException, ValueError, WrongContentType { + return serviceRequest(url, "GET", null, "", null, null, args); + } + + public Response serviceRequest(String url, String method, ClientInfo clientInfo) throws ParseException, ValueError, WrongContentType { + return serviceRequest(url, "GET", null, "", null, null, null); + } + + private static FakeResponse parseResponse(Object text, ClientInfo clientInfo, String valueType, String state, Map args) { + throw new UnsupportedOperationException(); + } + +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/State.java b/lib/src/main/java/com/auth0/jwt/oiccli/State.java new file mode 100644 index 0000000..0174dda --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/State.java @@ -0,0 +1,150 @@ +package com.auth0.jwt.oiccli; + +import com.auth0.jwt.creators.Message; +import com.auth0.jwt.oiccli.exceptions.ExpiredToken; +import com.auth0.jwt.oiccli.exceptions.UnknownState; +import com.google.common.base.Strings; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +public class State { + + private String clientId; + private Database db; + private String dbName; + private int lifetime; + private String tokenInfo; + + public State(String clientId, Database db, String dbName, int lifetime) { + this.clientId = clientId; + this.db = db; + if(this.db == null) { + if(!Strings.isNullOrEmpty(dbName)) { + this.db = shelve.open(dbName, true); + } else { + this.db = new Database(); + } + } + this.lifetime = lifetime; + } + + public StateInfo updateTokenInfo(StateInfo info, Message authorizationResponse) { + Token token = info.getToken(); + if (token == null) { + token = new Token(); + } + + String tokenString = authorizationResponse.getAccessToken(); + token.setAccessToken(tokenString); + Integer expiresAtInteger = authorizationResponse.getExpiresIn(); + int expiresAt; + if (expiresAtInteger != null) { + expiresAt = expiresAtInteger.intValue(); + token.setExp(System.currentTimeMillis() + expiresAt); + token.setExpiresIn(expiresAt); + } else { + token.setExp(System.currentTimeMillis() + ((Long) token.getExpiresIn()).longValue()); + } + + token.setTokenType(authorizationResponse.getTokenType()); + token.setScope(authorizationResponse.getScope()); + + info.setToken(token); + + return info; + } + + public StateInfo addResponse(Message authorizationResponse, String state) throws UnknownState { + if (Strings.isNullOrEmpty(state)) { + state = authorizationResponse.getState(); + } + + StateInfo stateInfo = this.getDB().getStateInfo(state); + if (stateInfo == null) { + throw new UnknownState(state); + } + + if(authorizationResponse instanceof AuthorizationResponse) { + stateInfo.setCode(authorizationResponse.getCode()); + } + + this.updateTokenInfo(stateInfo, authorizationResponse); + + stateInfo.setToken(authorizationResponse.getIdToken()); + stateInfo.setRefreshToken(authorizationResponse.getRefreshToken()); + + //TODO: Updated the state database + //self[state] = _state_info + + return stateInfo; + } + + public StateInfo addInfo(String state, Map args) { + StateInfo stateInfo = this.getDB().getStateInfo(state); + stateInfo.update(args); + + //TODO: Updated the state database + //self[state] = _state_info + + return stateInfo; + } + + public Token getTokenInfo(String state, long now) throws ExpiredToken { + StateInfo stateInfo = this.getDB().getStateInfo(state); + Token token = null; + long exp = 0; + if(stateInfo != null) { + token = stateInfo.getToken(); + if(token != null) { + exp = token.getExp(); + } + } + + if (now == 0) { + now = System.currentTimeMillis(); + } + if (now > exp) { + throw new ExpiredToken("Passed best before"); + } + return token; + } + + public Token getTokenInfo(String state) throws ExpiredToken { + return getTokenInfo(state, 0); + } + + public Map getResponseArgs(String state, AccessTokenRequest request, int now) throws ExpiredToken, NoSuchFieldException, IllegalAccessException { + StateInfo stateInfo = this.getDB().getStateInfo(state); + Map responseArgs = new HashMap<>(); + for (String claim : request.getCParam().keySet()) { + if (claim.equals("accessToken")) { + Map tInfo = this.getTokenInfo(state, now); + if (tInfo == null) { + continue; + } + responseArgs.put(claim, tInfo.get("accessToken")); + } else { + responseArgs.put(claim, stateInfo.getClass().getField(claim).get(this)); + } + } + + return responseArgs; + } + + private Token getIdToken(String state) { + return this.getDB().getStateInfo(state).getToken(); + } + + private Database getDB() { + return db; + } + + public State(String clientId, Database db, String dbName) { + this(clientId, db, dbName, 600); + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/StateInfo.java b/lib/src/main/java/com/auth0/jwt/oiccli/StateInfo.java new file mode 100644 index 0000000..dc74d3b --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/StateInfo.java @@ -0,0 +1,35 @@ +package com.auth0.jwt.oiccli; + +import java.util.Map; + +public class StateInfo { + + private String code; + private String claim; + private Token token; + private String refreshToken; + + public void setCode(String code) { + this.code = code; + } + + public void setClaim(String claim) { + this.claim = claim; + } + + public Token getToken() { + return token; + } + + public void setToken(Token token) { + this.token = token; + } + + public void update(Map args) { + throw new UnsupportedOperationException(); + } + + public void setRefreshToken(String refreshToken) { + this.refreshToken = refreshToken; + } +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/StringUtil.java b/lib/src/main/java/com/auth0/jwt/oiccli/StringUtil.java new file mode 100644 index 0000000..6a25ee3 --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/StringUtil.java @@ -0,0 +1,28 @@ +package com.auth0.jwt.oiccli; + +import org.apache.commons.text.CharacterPredicates; +import org.apache.commons.text.RandomStringGenerator; + +public class StringUtil { + + public static String generateRandomString(int length) { + return new RandomStringGenerator.Builder() + .withinRange('0', 'z') + .filteredBy(CharacterPredicates.LETTERS, CharacterPredicates.DIGITS) + .build().generate(length); + } + + public static String alg2keytype(String algorithm) { + if (algorithm == null || algorithm.toLowerCase().equals("none")) { + return "none"; + } else if (algorithm.startsWith("RS") || algorithm.startsWith("PS")) { + return "RSA"; + } else if (algorithm.startsWith("HS") || algorithm.startsWith("A")) { + return "oct"; + } else if (algorithm.startsWith("ES") || algorithm.startsWith("ECDH-ES")) { + return "EC"; + } else { + return null; + } + } +} \ No newline at end of file diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/Token.java b/lib/src/main/java/com/auth0/jwt/oiccli/Token.java new file mode 100644 index 0000000..25c696a --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/Token.java @@ -0,0 +1,46 @@ +package com.auth0.jwt.oiccli; + +public class Token { + + private String accessToken; + private long exp; + private long expiresIn; + private String tokenType; + private String scope; + + public String getAccessToken() { + return accessToken; + } + + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } + + public long getExp() { + return exp; + } + + public void setExp(long exp) { + this.exp = exp; + } + + public long getExpiresIn() { + return expiresIn; + } + + public void setExpiresIn(int expiresIn) { + this.expiresIn = expiresIn; + } + + public String getTokenType() { + return tokenType; + } + + public void setTokenType(String tokenType) { + this.tokenType = tokenType; + } + + public void setScope(String scope) { + this.scope = scope; + } +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/Utils/ClientInfo.java b/lib/src/main/java/com/auth0/jwt/oiccli/Utils/ClientInfo.java new file mode 100644 index 0000000..b852327 --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/Utils/ClientInfo.java @@ -0,0 +1,280 @@ +package com.auth0.jwt.oiccli.Utils; + +import com.auth0.jwt.oiccli.Database; +import com.auth0.jwt.oiccli.State; +import com.auth0.jwt.oiccli.exceptions.ValueError; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableMap; +import java.io.File; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.interfaces.RSAKey; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.slf4j.LoggerFactory; + +public class ClientInfo { + + private static final String ALG = "alg"; + private static final String ENC = "enc"; + private static final String SIGN = "sign"; + private static final String RS256 = "RS256"; + private static final String HS256 = "HS256"; + private static final String SHA_256 = "SHA-256"; + private static final Map userInfoMap = + ImmutableMap.of(SIGN, "userinfoSignedResponseAlg", + ALG, "userinfoEncryptedResponseAlg", + ENC, "userinfoEncryptedResponseEnc"); + private static final Map idTokenMap = + ImmutableMap.of(SIGN, "idTokenSignedResponseAlg", + ALG, "idTokenEncryptedResponseAlg", + ENC, "idTokenEncryptedResponseEnc"); + private static final Map requestMap = + ImmutableMap.of(SIGN, "requestObjectSigningAlg", + ALG, "requestObjectEncryptionAlg", + ENC, "requestObjectEncryptionEnc"); + private static final Map> ATTRIBUTE_MAP = + ImmutableMap.of("userinfo", userInfoMap, "idToken", idTokenMap, "request", requestMap); + private static final Map DEF_SIGN_ALG = + ImmutableMap.of("idToken", RS256, + "userInfo", RS256, + "requestObject", RS256, + "clientSecretJwt", HS256, + "privateKeyJwt", RS256); + private String baseUrl; + private String requestsDir; + private String cId; + private String cSecret; + private String issuer; + private Map redirectUris; + private Map> clientPrefs; + private Map allow; + Map> behavior; + private KeyJar keyJar; + private State stateDB; + private boolean shouldBeStrictOnPreferences; + private Map> providerInfo; + private Map registrationResponse; + private Map> kid; + private List events; + private Map> config; + final private static org.slf4j.Logger logger = LoggerFactory.getLogger(ClientInfo.class); + + public ClientInfo(KeyJar keyJar, Map> config, List events, + Database db, String dbName, boolean shouldBeStrictOnPreferences, Map args) throws NoSuchFieldException, IllegalAccessException { + if(keyJar != null) { + this.keyJar = keyJar; + } else { + this.keyJar = new KeyJar(); + } + this.stateDB = new State("", db, dbName); + this.events = events; + this.shouldBeStrictOnPreferences = shouldBeStrictOnPreferences; + this.providerInfo = new HashMap<>(); + this.registrationResponse = new HashMap<>(); + this.kid = new HashMap() {{ + put("sig", new HashMap<>()); + put(ENC, new HashMap<>()); + }}; + if(config == null) { + this.config = new HashMap<>(); + } else { + this.config = config; + } + this.baseUrl = ""; + this.requestsDir = ""; + this.allow = new HashMap<>(); + this.behavior = new HashMap<>(); + this.clientPrefs = new HashMap<>(); + this.cId = ""; + this.cSecret = ""; + this.issuer = ""; + + for (String key : args.keySet()) { + this.getClass().getField(key).set(key, args.get(key)); + } + + List attributes = new ArrayList<>(Arrays.asList("clientId", "issuer", "clientSecret", "baseUrl", "requestsDir")); + Map value; + for (String attribute : attributes) { + value = config.get(attribute); + if (value != null) { + this.getClass().getField(attribute).set(attribute, value); + } else { + this.getClass().getField(attribute).set(attribute, ""); + } + } + + attributes = new ArrayList<>(Arrays.asList("allow", "clientPrefs", "behavior", "providerInfo")); + for (String attribute : attributes) { + value = config.get(attribute); + if (value != null) { + this.getClass().getField(attribute).set(attribute, value); + } else { + this.getClass().getField(attribute).set(attribute, new HashMap<>()); + } + } + + if (!Strings.isNullOrEmpty(requestsDir)) { + File file = new File(requestsDir); + if (!file.isDirectory()) { + try { + file.mkdir(); + } catch (SecurityException se) { + logger.error("Directory " + requestsDir + " was not created"); + } + } + } + + Map redirectUris = config.get("redirectUris"); + if (redirectUris != null) { + this.redirectUris = redirectUris; + } else { + this.redirectUris = null; + } + + this.importKeys(config.get("keys")); + + if (config.containsKey("keydefs")) { + this.keyJar = buildKeyJar(config.get("keydefs"), this.keyJar)[1]; + } + } + + //client_secret = property(get_client_secret, set_client_secret) + + public String getClientSecret() { + return cSecret; + } + + public void setClientSecret(String cSecret) { + if(Strings.isNullOrEmpty(cSecret)) { + this.cSecret = ""; + } else { + this.cSecret = cSecret; + if(this.keyJar == null) { + this.keyJar = new KeyJar(); + } + this.keyJar.addSymmetric("", cSecret); + } + } + + public String getClientId() { + return this.cId; + } + + public void setClientId(String clientId) { + this.cId = clientId; + this.stateDB.setClientId(clientId); + } + + //client_id = property(get_client_id, set_client_id) + + public String filenameFromWebname(String webName) throws ValueError { + if(!webName.startsWith(this.baseUrl)) { + throw new ValueError("Webname doesn't match baseUrl"); + } + webName = webName.substring(baseUrl.length()); + if (webName.startsWith("/")) { + return webName.substring(1); + } else { + return webName; + } + } + + public Map> signEncAlgs(String type) { + Map> response = new HashMap<>(); + List value; + for (String key : ATTRIBUTE_MAP.get(type).keySet()) { + value = this.registrationResponse.get(ATTRIBUTE_MAP.get(key)); + if (ATTRIBUTE_MAP.get(key) != null && value != null) { + response.put(key, value); + } else { + if (key.equals(SIGN)) { + response.put(key, Arrays.asList(DEF_SIGN_ALG.get(type))); + } + } + } + + return response; + } + + public boolean verifyAlgSupport(String algorithm, String usage, String type) { + List supported = this.providerInfo.get(usage + "" + type + "valuesSupported"); + return supported.contains(algorithm); + } + + public List generateRequestUris(String requestsDir) throws NoSuchAlgorithmException { + MessageDigest digest = MessageDigest.getInstance(SHA_256); + List providerInfoList = this.providerInfo.get("issuer"); + if(providerInfoList != null && !providerInfoList.isEmpty()) { + String providerInfo = providerInfoList.get(0); + if(!Strings.isNullOrEmpty(providerInfo)) { + digest.update(providerInfo.getBytes()); + } + } else { + digest.update(this.issuer.getBytes()); + } + + digest.update(this.baseUrl.getBytes()); + + if(!requestsDir.startsWith("/")) { + return Arrays.asList(this.baseUrl + "/" + requestsDir + "/" + digest.digest()); + } else { + return Arrays.asList(this.baseUrl + requestsDir + "/" + digest.digest()); + } + } + + public void importKeys(Map>> keySpec) { + for (String key : keySpec.keySet()) { + if (key.equals("file")) { + Map> hMap = keySpec.get(key); + for (String hMapKey : hMap.keySet()) { + if (hMapKey.equals("rsa")) { + Key key; + KeyBundle keyBundle; + for (String file : hMap.get(hMapKey)) { + //TODO: importPrivateRsaKeyFromFile is from cryptojwt + key = new RSAKey(importPrivateRsaKeyFromFile(file), "sig"); + keyBundle = new KeyBundle(); + keyBundle.append(key); + this.keyJar.addKb("", keyBundle); + } + } + } + } else if (key.equals("url")) { + KeyBundle keyBundle; + for (String issuer : keySpec.keySet()) { + keyBundle = new KeyBundle(keySpec.get(issuer)); + this.keyJar.addKb(keyBundle); + } + } + } + } + + public State getStateDb() { + return this.stateDB; + } + + public Map> getConfig() { + return config; + } + + public Map> getBehavior() { + return behavior; + } + + public KeyJar getKeyJar() { + return keyJar; + } + + public Map getRegistrationResponse() { + return registrationResponse; + } + + public Map> getProviderInfo() { + return providerInfo; + } +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/Utils/JWE.java b/lib/src/main/java/com/auth0/jwt/oiccli/Utils/JWE.java new file mode 100644 index 0000000..b1a5a48 --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/Utils/JWE.java @@ -0,0 +1,19 @@ +package com.auth0.jwt.oiccli.Utils; + +import com.auth0.jwt.creators.Message; +import java.util.List; + +public class JWE { + private String kid; + + public JWE(Message message, String encryptionAlg, String encryptionEnc) { + } + + public void setKid(String kid) { + this.kid = kid; + } + + public Message encrypt(List keys) { + throw new UnsupportedOperationException(); + } +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/Utils/PKCE.java b/lib/src/main/java/com/auth0/jwt/oiccli/Utils/PKCE.java new file mode 100644 index 0000000..9c9feeb --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/Utils/PKCE.java @@ -0,0 +1,73 @@ +package com.auth0.jwt.oiccli.Utils; + +import com.auth0.jwt.oiccli.StringUtil; +import com.auth0.jwt.oiccli.exceptions.ExpiredToken; +import com.sun.org.apache.xml.internal.security.utils.Base64; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.HashMap; +import java.util.Map; + +public class PKCE { + private static final String S256 = "S256"; + private static final String S384 = "S384"; + private static final String S512 = "S512"; + private static final String SHA_256 = "SHA-256"; + private static final String SHA_384 = "SHA-384"; + private static final String SHA_512 = "SHA-512"; + + public static Map addCodeChallenge(ClientInfo clientInfo, String state) throws NoSuchAlgorithmException, NoSuchAlgorithmException { + Integer cvLength = (Integer) clientInfo.getConfig().get("codeChallenge").get("length"); + if (cvLength == null) { + cvLength = 64; + } + + String codeVerifier = StringUtil.generateRandomString(cvLength); + codeVerifier = Base64.encode(codeVerifier.getBytes()); + + String method = (String) clientInfo.getConfig().get("codeChallenge").get("method"); + if (method == null) { + method = S256; + } + + MessageDigest digest= null; + switch (method) { + case S256: + digest = MessageDigest.getInstance(SHA_256); + break; + case S384: + digest = MessageDigest.getInstance(SHA_384); + break; + case S512: + digest = MessageDigest.getInstance(SHA_512); + break; + } + + String codeVerifierHex = bytesToHex(codeVerifier.getBytes()); + + byte[] codeVerifierHexByteArr = digest.digest(codeVerifierHex.getBytes()); + String codeChallenge = Base64.encode(codeVerifierHexByteArr); + + clientInfo.getStateDb().addInfo(state, codeVerifier, method); + + Map hMap = new HashMap<>(); + hMap.put("codeChallenge", codeChallenge); + hMap.put("codeChallengeMethod", method); + + return hMap; + } + + private static String bytesToHex(byte[] hash) { + StringBuffer hexString = new StringBuffer(); + for (int i = 0; i < hash.length; i++) { + String hex = Integer.toHexString(0xff & hash[i]); + if(hex.length() == 1) hexString.append('0'); + hexString.append(hex); + } + return hexString.toString(); + } + + public static Object getCodeVerifier(ClientInfo clientInfo, String state) throws ExpiredToken { + return clientInfo.getStateDb().getTokenInfo("state" + state).get("codeVerifier"); + } +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/Utils/Utils.java b/lib/src/main/java/com/auth0/jwt/oiccli/Utils/Utils.java new file mode 100644 index 0000000..0846cdb --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/Utils/Utils.java @@ -0,0 +1,80 @@ +package com.auth0.jwt.oiccli.Utils; + +import com.auth0.jwt.creators.Message; +import com.auth0.jwt.oiccli.StringUtil; +import com.auth0.jwt.oiccli.tuples.Tuple; +import com.auth0.jwt.oiccli.exceptions.MissingRequiredAttribute; +import com.google.common.base.Strings; +import java.io.File; +import java.nio.file.Paths; +import java.util.List; +import java.util.Map; + +public class Utils { + + public static Message requestObjectEncryption(Message message, ClientInfo clientInfo, Map args) throws MissingRequiredAttribute { + String encryptionAlg = (String) args.get("requestObjectEncryptionAlg"); + + if(Strings.isNullOrEmpty(encryptionAlg)) { + List listOfAlgs = clientInfo.getBehavior().get("requestObjectEncryptionAlg"); + if(listOfAlgs != null || !listOfAlgs.isEmpty()) { + encryptionAlg = listOfAlgs.get(0); + } + + if(encryptionAlg == null) { + return message; + } + } + + String encryptionEnc = (String) args.get("requestObjectEncryptionEnc"); + + if(Strings.isNullOrEmpty(encryptionEnc)) { + List listOfAlgs = clientInfo.getBehavior().get("requestObjectEncryptionEnc"); + if(listOfAlgs != null || !listOfAlgs.isEmpty()) { + encryptionEnc = listOfAlgs.get(0); + } + + if(encryptionEnc == null) { + throw new MissingRequiredAttribute("No requestObjectEncryptionEnc specified"); + } + } + + JWE jwe = new JWE(message, encryptionAlg, encryptionEnc); + String keyType = StringUtil.alg2keytype(encryptionAlg); + + String kid = (String) args.get("encKid"); + if(Strings.isNullOrEmpty(kid)) { + kid = ""; + } + + if(!args.containsKey("target")) { + throw new MissingRequiredAttribute("No target specified"); + } + + List keys; + if(!Strings.isNullOrEmpty(kid)) { + keys = clientInfo.getKeyJar().getEncryptKey(keyType, args.get("target"), kid); + jwe.setKid(kid); + } else { + keys = clientInfo.getKeyJar().getEncryptKey(keyType, args.get("target")); + } + + return jwe.encrypt(keys); + } + + public static Tuple constructRequestUri(String localDir, String basePath, Map args) { + File file = new File(localDir); + if(!file.isDirectory()) { + file.mkdirs(); + } + String name = StringUtil.generateRandomString(10) + ".jwt"; + File fileExists = Paths.get(localDir, name).toFile(); + while(fileExists.exists()) { + name = StringUtil.generateRandomString(10); + fileExists = Paths.get(localDir, name).toFile(); + } + + String webName = basePath + name; + return new Tuple(fileExists.toString(), webName); + } +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/client_auth/BearerBody.java b/lib/src/main/java/com/auth0/jwt/oiccli/client_auth/BearerBody.java new file mode 100644 index 0000000..d525228 --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/client_auth/BearerBody.java @@ -0,0 +1,59 @@ +package com.auth0.jwt.oiccli.client_auth; + +import com.auth0.jwt.jwts.JWT; +import com.auth0.jwt.oiccli.State; +import com.auth0.jwt.oiccli.StringUtil; +import com.auth0.jwt.oiccli.Utils.ClientInfo; +import com.auth0.jwt.oiccli.exceptions.AuthenticationFailure; +import com.auth0.jwt.oiccli.exceptions.ValueError; +import com.google.common.base.Strings; +import java.util.List; +import java.util.Map; + +public class BearerBody { + + public Map> construct(ResourceRequest resourceRequest, ClientInfo clientInfo, Map> httpArgs, + Map args) throws AuthenticationFailure { + + if (Strings.isNullOrEmpty(resourceRequest.getAccessToken())) { + AccessToken accessToken = args.get("accessToken"); + if (accessToken != null) { + resourceRequest.setAccessToken(accessToken); + } else { + if (args.get("state") == null) { + State state = clientInfo.getStateDb(); + if (state == null) { + throw new AuthenticationFailure("Missing state specification"); + } + + args.put("state", state); + } + + //resourceRequest.setAccessToken(clientInfo.getStateDb().getTokenInfo(state).get("accessToken")); + } + } + + return httpArgs; + } + + public static JWT assertionJWT(String clientId, List keys, List audience, + String algorithm, int lifeTime) { + long now = System.currentTimeMillis(); + + AuthenticationToken at = new AuthenticationToken(clientId, clientId, audience, StringUtil.generateRandomString(32), + now + lifeTime, now); + return at.toJwt(keys, algorithm); + } + + public static String bearerAuth(ResourceRequest resourceRequest, String authentication) throws ValueError { + String accessToken = resourceRequest.getAccessToken(); + if(!Strings.isNullOrEmpty(accessToken)) { + return accessToken; + } else { + if(!authentication.startsWith("Bearer ")) { + throw new ValueError("Not a bearer token"); + } + return authentication.substring(7); + } + } +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/client_auth/BearerHeader.java b/lib/src/main/java/com/auth0/jwt/oiccli/client_auth/BearerHeader.java new file mode 100644 index 0000000..fce174d --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/client_auth/BearerHeader.java @@ -0,0 +1,49 @@ +package com.auth0.jwt.oiccli.client_auth; + +import com.auth0.jwt.oiccli.Utils.ClientInfo; +import com.auth0.jwt.oiccli.exceptions.MissingRequiredAttribute; +import com.google.common.base.Strings; +import java.util.HashMap; +import java.util.Map; + +public class BearerHeader { + + public Map> construct(ResourceRequest resourceRequest, ClientInfo clientInfo, Map> httpArgs, + Map args) throws MissingRequiredAttribute { + String accessToken; + if (resourceRequest != null) { + if (!Strings.isNullOrEmpty(resourceRequest.getAccessToken())) { + accessToken = resourceRequest.getAccessToken(); + resourceRequest.setAccessToken(null); + resourceRequest.getCParam().setAccessToken(SINGLE_OPTIONAL_STRING); + } else { + accessToken = requestArgs.get("accessToken"); + + if (accessToken == null) { + accessToken = clientInfo.getStateDb().getTokenInfo(args).get("accessToken"); + } + } + } else { + accessToken = args.get("accessToken"); + if (accessToken == null) { + throw new MissingRequiredAttribute("accessToken"); + } + } + + String bearer = "Bearer " + accessToken; + if (httpArgs == null) { + Map hMap = new HashMap<>(); + hMap.put("Authorization", bearer); + httpArgs.put("headers", hMap); + } else { + Map hMap = httpArgs.get("headers"); + if (hMap == null) { + hMap = new HashMap<>(); + } + hMap.put("Authorization", bearer); + httpArgs.put("headers", hMap); + } + + return httpArgs; + } +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/client_auth/ClientAuthenticationMethod.java b/lib/src/main/java/com/auth0/jwt/oiccli/client_auth/ClientAuthenticationMethod.java new file mode 100644 index 0000000..465e1b4 --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/client_auth/ClientAuthenticationMethod.java @@ -0,0 +1,4 @@ +package com.auth0.jwt.oiccli.client_auth; + +public class ClientAuthenticationMethod { +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/client_auth/ClientSecretBasic.java b/lib/src/main/java/com/auth0/jwt/oiccli/client_auth/ClientSecretBasic.java new file mode 100644 index 0000000..fecd8cd --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/client_auth/ClientSecretBasic.java @@ -0,0 +1,60 @@ +package com.auth0.jwt.oiccli.client_auth; + +import com.auth0.jwt.oiccli.AccessTokenRequest; +import com.auth0.jwt.oiccli.Utils.ClientInfo; +import com.auth0.jwt.oiccli.exceptions.AuthenticationFailure; +import java.util.HashMap; +import java.util.Map; +import org.apache.commons.codec.binary.Base64; + +public class ClientSecretBasic { + + public Map> construct(AccessTokenRequest request, ClientInfo cliInfo, Map> httpArgs, + Map args) throws AuthenticationFailure { + if (httpArgs == null) { + httpArgs = new HashMap<>(); + } + + if (!httpArgs.containsKey("headers")) { + httpArgs.put("headers", new HashMap()); + } + + String password = args.get("password"); + if (password == null) { + password = httpArgs.get("password").get("password"); + if (password == null) { + password = request.getClientSecret(); + if (password == null) { + password = cliInfo.getClientSecret(); + } + } + } + + String user = args.get("user"); + if (user == null) { + user = cliInfo.getClientId(); + } + + String credentials = user + ":" + password; + String authz = new String(Base64.encodeBase64(credentials.getBytes())); + Map hMap = new HashMap<>(); + hMap.put("Authorization", "Basic " + authz); + httpArgs.put("headers", hMap); + + request.setClientSecret(null); + + if (request.get("grantType").equals("authorizationCode")) { + if (request.getClientId() != null) { + request.setClientId(cliInfo.getClientId()); + } + } else { + boolean req = request.getCParam("clientId").get(VREQUIRED); + + if (!req) { + request.remove("clientId"); + } + } + + return httpArgs; + } +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/client_auth/ClientSecretJWT.java b/lib/src/main/java/com/auth0/jwt/oiccli/client_auth/ClientSecretJWT.java new file mode 100644 index 0000000..a5312c8 --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/client_auth/ClientSecretJWT.java @@ -0,0 +1,23 @@ +package com.auth0.jwt.oiccli.client_auth; + +import com.auth0.jwt.oiccli.StringUtil; +import com.auth0.jwt.oiccli.Utils.ClientInfo; +import com.auth0.jwt.oiccli.exceptions.AuthenticationFailure; +import java.security.Key; +import java.util.Map; + +public class ClientSecretJWT { + + public static String chooseAlgorithm(String entity, Map args) throws AuthenticationFailure { + return JWSAuthenticationMethod.chooseAlgorithm(entity, args); + } + + public static String chooseAlgorithm(Map args) { + return JWSAuthenticationMethod.chooseAlgorithm("clientSecretJwt", args); + } + + @Override + public Key getSigningKey(String algorithm, ClientInfo clientInfo) { + return clientInfo.getKeyJar().getSigningKey(StringUtil.alg2keytype(algorithm), algorithm); + } +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/client_auth/ClientSecretPost.java b/lib/src/main/java/com/auth0/jwt/oiccli/client_auth/ClientSecretPost.java new file mode 100644 index 0000000..4d6382a --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/client_auth/ClientSecretPost.java @@ -0,0 +1,31 @@ +package com.auth0.jwt.oiccli.client_auth; + +import com.auth0.jwt.oiccli.AccessTokenRequest; +import com.auth0.jwt.oiccli.Utils.ClientInfo; +import com.auth0.jwt.oiccli.exceptions.AuthenticationFailure; +import com.google.common.base.Strings; +import java.util.Map; + +public class ClientSecretPost extends ClientSecretBasic { + + public Map> construct(AccessTokenRequest request, ClientInfo clientInfo, Map> httpArgs, + Map args) throws AuthenticationFailure { + if (Strings.isNullOrEmpty(request.getClientSecret())) { + Map clientSecret = httpArgs.get("clientSecret"); + if (clientSecret != null) { + request.setClientSecret(clientSecret.get("clientSecret")); + httpArgs.remove("clientSecret"); + } else { + if (!Strings.isNullOrEmpty(clientInfo.getClientSecret())) { + request.setClientSecret(clientInfo.getClientSecret()); + } else { + throw new AuthenticationFailure("Missing client secret"); + } + } + } + + request.setClientId(clientInfo.getClientId()); + + return httpArgs; + } +} \ No newline at end of file diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/client_auth/JWSAuthenticationMethod.java b/lib/src/main/java/com/auth0/jwt/oiccli/client_auth/JWSAuthenticationMethod.java new file mode 100644 index 0000000..e795f08 --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/client_auth/JWSAuthenticationMethod.java @@ -0,0 +1,117 @@ +package com.auth0.jwt.oiccli.client_auth; + +import com.auth0.jwt.oiccli.StringUtil; +import com.auth0.jwt.oiccli.Utils.ClientInfo; +import com.auth0.jwt.oiccli.exceptions.AuthenticationFailure; +import com.auth0.jwt.oiccli.exceptions.NoMatchingKey; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.junit.Assert; + +public class JWSAuthenticationMethod extends ClientAuthenticationMethod { + + private static final Map DEF_SIGN_ALG = new HashMap() {{ + put("id_token", "RS256"); + put("userinfo", "RS256"); + put("request_object", "RS256"); + put("client_secret_jwt", "HS256"); + put("private_key_jwt", "RS256"); + }}; + private static final String JWT_BEARER = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"; + private static final String V_Required = 1; + + public static String chooseAlgorithm(String context, Map args) throws AuthenticationFailure { + String algorithm = args.get("algorithm"); + if (algorithm == null) { + algorithm = DEF_SIGN_ALG.get(context); + if (algorithm == null) { + throw new AuthenticationFailure("Missing algorithm specification"); + } + } + + return algorithm; + } + + public String chooseAlgorithm(Map args) throws AuthenticationFailure { + return chooseAlgorithm(null, args); + } + + public Key getSigningKey(String algorithm, ClientInfo clientInfo) { + return clientInfo.getKeyJar().getSigningKey( + StringUtil.alg2keytype(algorithm), algorithm); + } + + public Key getKeyByKid(String kid, String algorithm, ClientInfo clientInfo) throws NoMatchingKey { + Key key = clientInfo.getKeyJar().getKeyByKid(kid); + String ktype; + if (key != null) { + ktype = StringUtil.alg2keytype(algorithm); + try { + Assert.assertTrue(key.getType().equals(ktype)); + return key; + } catch (AssertionError error) { + throw new NoMatchingKey("Wrong key type"); + } + } else { + throw new NoMatchingKey("No key with kid " + kid); + } + } + + public Map construct(AccessTokenRequest request, ClientInfo clientInfo, Map requestArgs, + Map httpArgs, Map args) throws AuthenticationFailure, NoMatchingKey { + String algorithm = null; + List audience = null; + if (args.containsKey("clientAssertion")) { + request.setClientAssertion(args.get("clientAssertion")); + if (args.containsKey("clientAssertionType")) { + request.setClientAssertionType(args.get("clientAssertionType")); + } else { + request.setClientAssertionType(JWT_BEARER); + } + } else if (request.getClientAssertion() != null) { + if (request.getClientAssertionType() == null) { + request.setClientAssertionType(JWT_BEARER); + } + } else { + if (args.get("authenticationEndpoint").equals("token") || args.get("authenticationEndpoint").equals("refresh")) { + algorithm = clientInfo.getRegistrationResponse().get("tokenEndpointAuthSigningAlg"); + audience = clientInfo.getProviderInfo().get("tokenEndpoint"); + } else { + audience = clientInfo.getProviderInfo().get("issuer"); + } + } + + if (algorithm == null) { + algorithm = this.chooseAlgorithm(args); + } + + String ktype = StringUtil.alg2keytype(algorithm); + List signingKey = null; + if (args.containsKey("kid")) { + signingKey = Arrays.asList(this.getKeyByKid(args.get("kid"), algorithm, clientInfo)); + } else if (clientInfo.getKid().get("sig").containsKey(ktype)) { + Key key = this.getKeyByKid(clientInfo.getKid().get("sig").get("ktype"), algorithm, clientInfo); + if (key != null) { + signingKey = Arrays.asList(key); + } else { + signingKey = Arrays.asList(this.getSigningKey(algorithm, clientInfo)); + } + } + + Map hMap = new HashMap<>(); + hMap.put("lifetime", args.get("lifetime")); + + request.setClientAssertion(BearerBody.assertionJWT(clientInfo.getClientId(), signingKey, audience, algorithm, 600)); + request.setClientAssertionType(JWT_BEARER); + request.setClientSecret(null); + + if (request.getCParam.getClientId().get(V_Required) == null) { + request.setClientId(null); + } + + return new HashMap<>(); + } + +} \ No newline at end of file diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/client_auth/PrivateKeyJwt.java b/lib/src/main/java/com/auth0/jwt/oiccli/client_auth/PrivateKeyJwt.java new file mode 100644 index 0000000..54799d3 --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/client_auth/PrivateKeyJwt.java @@ -0,0 +1,40 @@ +package com.auth0.jwt.oiccli.client_auth; + +import com.auth0.jwt.oiccli.StringUtil; +import com.auth0.jwt.oiccli.Utils.ClientInfo; +import com.auth0.jwt.oiccli.exceptions.AuthenticationFailure; +import java.security.Key; +import java.util.Map; + +public class PrivateKeyJwt extends JWSAuthenticationMethod { + + public static String chooseAlgorithm(String entity, Map args) throws AuthenticationFailure { + return JWSAuthenticationMethod.chooseAlgorithm(entity, args); + } + + @Override + public String chooseAlgorithm(Map args) throws AuthenticationFailure { + return chooseAlgorithm("private_key_jwt", args); + } + + @Override + public Key getSigningKey(String algorithm, ClientInfo clientInfo) { + return clientInfo.getKeyJar().getSigningKey(StringUtil.alg2keytype(algorithm), "", algorithm); + } + + public static boolean validClientInfo(ClientInfo clientInfo, long when) { + long eta = clientInfo.getClientSecretExpiresAt(); + long now; + if(when != 0) { + now = when; + } else { + now = System.currentTimeMillis(); + } + + if(eta != 0 && eta < now) { + return false; + } + + return true; + } +} \ No newline at end of file diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/AuthenticationFailure.java b/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/AuthenticationFailure.java new file mode 100644 index 0000000..5ff4243 --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/AuthenticationFailure.java @@ -0,0 +1,7 @@ +package com.auth0.jwt.oiccli.exceptions; + +public class AuthenticationFailure extends Exception { + public AuthenticationFailure(String message) { + super(message); + } +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/ExpiredToken.java b/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/ExpiredToken.java new file mode 100644 index 0000000..3b8510c --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/ExpiredToken.java @@ -0,0 +1,7 @@ +package com.auth0.jwt.oiccli.exceptions; + +public class ExpiredToken extends Exception { + public ExpiredToken(String message) { + super(message); + } +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/HTTPError.java b/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/HTTPError.java new file mode 100644 index 0000000..2c6a204 --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/HTTPError.java @@ -0,0 +1,7 @@ +package com.auth0.jwt.oiccli.exceptions; + +public class HTTPError extends Exception { + public HTTPError(String message) { + super(message); + } +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/MissingEndpoint.java b/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/MissingEndpoint.java new file mode 100644 index 0000000..303eaaa --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/MissingEndpoint.java @@ -0,0 +1,7 @@ +package com.auth0.jwt.oiccli.exceptions; + +public class MissingEndpoint extends Exception { + public MissingEndpoint(String message) { + super(message); + } +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/MissingRequiredAttribute.java b/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/MissingRequiredAttribute.java new file mode 100644 index 0000000..e2456c8 --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/MissingRequiredAttribute.java @@ -0,0 +1,7 @@ +package com.auth0.jwt.oiccli.exceptions; + +public class MissingRequiredAttribute extends Exception{ + public MissingRequiredAttribute(String message) { + super(message); + } +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/NoMatchingKey.java b/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/NoMatchingKey.java new file mode 100644 index 0000000..2036130 --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/NoMatchingKey.java @@ -0,0 +1,7 @@ +package com.auth0.jwt.oiccli.exceptions; + +public class NoMatchingKey extends Exception { + public NoMatchingKey(String message) { + super(message); + } +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/UnknownState.java b/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/UnknownState.java new file mode 100644 index 0000000..350a456 --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/UnknownState.java @@ -0,0 +1,7 @@ +package com.auth0.jwt.oiccli.exceptions; + +public class UnknownState extends Exception { + public UnknownState(String state) { + super(state); + } +} \ No newline at end of file diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/UnsupportedType.java b/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/UnsupportedType.java new file mode 100644 index 0000000..c350479 --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/UnsupportedType.java @@ -0,0 +1,7 @@ +package com.auth0.jwt.oiccli.exceptions; + +public class UnsupportedType extends Exception{ + public UnsupportedType(String message) { + super(message); + } +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/ValueError.java b/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/ValueError.java new file mode 100644 index 0000000..c7d850d --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/ValueError.java @@ -0,0 +1,7 @@ +package com.auth0.jwt.oiccli.exceptions; + +public class ValueError extends Exception{ + public ValueError(String message) { + super(message); + } +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/WebFingerError.java b/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/WebFingerError.java new file mode 100644 index 0000000..eb6568d --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/WebFingerError.java @@ -0,0 +1,8 @@ +package com.auth0.jwt.oiccli.exceptions; + +public class WebFingerError extends Exception{ + + public WebFingerError(String message) { + super(message); + } +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/WrongContentType.java b/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/WrongContentType.java new file mode 100644 index 0000000..5ffd12f --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/WrongContentType.java @@ -0,0 +1,7 @@ +package com.auth0.jwt.oiccli.exceptions; + +public class WrongContentType extends Exception{ + public WrongContentType(String message) { + super(message); + } +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/responses/ErrorResponse.java b/lib/src/main/java/com/auth0/jwt/oiccli/responses/ErrorResponse.java new file mode 100644 index 0000000..eaed1b3 --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/responses/ErrorResponse.java @@ -0,0 +1,23 @@ +package com.auth0.jwt.oiccli.responses; + +public class ErrorResponse extends Response{ + + private int statusCode; + private String text; + + public void verify() { + throw new UnsupportedOperationException(); + } + + public ErrorResponse deserialize(String text, String bodyTypeResult) { + throw new UnsupportedOperationException(); + } + + public int getStatusCode() { + return statusCode; + } + + public String getText() { + return text; + } +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/responses/Response.java b/lib/src/main/java/com/auth0/jwt/oiccli/responses/Response.java new file mode 100644 index 0000000..1443839 --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/responses/Response.java @@ -0,0 +1,16 @@ +package com.auth0.jwt.oiccli.responses; + +import java.util.Map; + +public class Response { + + private Map headers; + + public String getText() { + throw new UnsupportedOperationException(); + } + + public Map getHeaders() { + return headers; + } +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/tuples/Tuple.java b/lib/src/main/java/com/auth0/jwt/oiccli/tuples/Tuple.java new file mode 100644 index 0000000..265c003 --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/tuples/Tuple.java @@ -0,0 +1,20 @@ +package com.auth0.jwt.oiccli.tuples; + +public class Tuple { + + private Object a; + private Object b; + + public Tuple(Object a, Object b) { + this.a = a; + this.b = b; + } + + public Object getA() { + return a; + } + + public Object getB() { + return b; + } +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/tuples/Tuple5.java b/lib/src/main/java/com/auth0/jwt/oiccli/tuples/Tuple5.java new file mode 100644 index 0000000..77359df --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/tuples/Tuple5.java @@ -0,0 +1,58 @@ +package com.auth0.jwt.oiccli.tuples; + +public class Tuple5 { + + private Class classType; + private boolean aBoolean; + private Object object1; + private Object object2; + private boolean aBoolean2; + + public Tuple5(Class classType, boolean aBoolean, Object object1, Object object2, boolean aBoolean2) { + this.classType = classType; + this.aBoolean = aBoolean; + this.object1 = object1; + this.object2 = object2; + this.aBoolean2 = aBoolean2; + } + + public Class getClassType() { + return classType; + } + + public void setClassType(Class classType) { + this.classType = classType; + } + + public boolean isaBoolean() { + return aBoolean; + } + + public void setaBoolean(boolean aBoolean) { + this.aBoolean = aBoolean; + } + + public Object getObject1() { + return object1; + } + + public void setObject1(Object object1) { + this.object1 = object1; + } + + public Object getObject2() { + return object2; + } + + public void setObject2(Object object2) { + this.object2 = object2; + } + + public boolean isaBoolean2() { + return aBoolean2; + } + + public void setaBoolean2(boolean aBoolean2) { + this.aBoolean2 = aBoolean2; + } +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/util/FakeResponse.java b/lib/src/main/java/com/auth0/jwt/oiccli/util/FakeResponse.java new file mode 100644 index 0000000..2b32e68 --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/util/FakeResponse.java @@ -0,0 +1,27 @@ +package com.auth0.jwt.oiccli.util; + +import com.auth0.jwt.oiccli.responses.Response; + +public class FakeResponse extends Response { + + private Header headers; + private String text; + private int statusCode; + private String url; + + public Header getHeaders() { + return headers; + } + + public String getText() { + return text; + } + + public int getStatusCode() { + return statusCode; + } + + public String getUrl() { + return url; + } +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/util/Header.java b/lib/src/main/java/com/auth0/jwt/oiccli/util/Header.java new file mode 100644 index 0000000..c019c73 --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/util/Header.java @@ -0,0 +1,10 @@ +package com.auth0.jwt.oiccli.util; + +public class Header { + + private String contentType; + + public String getContentType() { + return contentType; + } +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/util/Util.java b/lib/src/main/java/com/auth0/jwt/oiccli/util/Util.java new file mode 100644 index 0000000..3a8e60b --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/util/Util.java @@ -0,0 +1,232 @@ +package com.auth0.jwt.oiccli.util; + +import com.auth0.jwt.oiccli.AuthorizationRequest; +import com.auth0.jwt.oiccli.AuthorizationResponse; +import com.auth0.jwt.oiccli.exceptions.UnsupportedType; +import com.auth0.jwt.oiccli.exceptions.ValueError; +import com.auth0.jwt.oiccli.exceptions.WrongContentType; +import com.auth0.jwt.oiccli.responses.Response; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.google.common.base.Strings; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Field; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URLDecoder; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Util { + + private static final String URL_ENCODED = "application/x-www-form-urlencoded"; + private static final String JSON_ENCODED = "application/json"; + private static final String JRD_JSON = "application/jrd+json"; + private static final String JWT_ENCODED = "application/jwt"; + private static final String PLAIN_TEXT = "text/plain"; + private static final String HTML_TEXT = "text/html"; + private static final String JWT = "jwt"; + private static final String JSON = "json"; + private static final String URLENCODED = "urlencoded"; + private static final String TEXT = "txt"; + private static final String DEFAULT_POST_CONTENT_TYPE = URL_ENCODED; + private static final Map pairs = new HashMap() {{ + put("port", "portSpecified"); + put("domain", "domainSpecified"); + put("path", "pathSpecified"); + }}; + private static final Map attributes = new HashMap() {{ + put("version", null); + put("name", ""); + put("value", null); + put("port", null); + put("portSpecified", false); + put("domain", ""); + put("domainSpecified", false); + put("domainInitialDot", false); + put("path", ""); + put("pathSpecified", false); + put("secure", false); + put("expires", null); + put("discard", true); + put("comment", null); + put("commentUrl", null); + put("rest", ""); + put("rfc2109", true); + }}; + private final static Logger logger = LoggerFactory.getLogger(Util.class); + private final static Map sortOrder = new HashMap() {{ + put("RS", 0); + put("ES", 1); + put("HS", 2); + put("PS", 3); + put("no", 4); + }}; + + public static boolean matchTo(String value, List valuesList) { + for (String index : valuesList) { + if (index.startsWith(value)) { + return true; + } + } + return false; + } + + public static boolean matchTo(String value, String valueExpected) { + return valueExpected.startsWith(value); + } + + public String getResponseBodyType(Response response) throws ValueError { + String contentType = response.getHeaders().get("contentType"); + if(Strings.isNullOrEmpty(contentType)) { + throw new ValueError("Missing Content-type specification"); + } + + return getResponseBodyTypeHelperMethod(contentType); + } + + public static String getResponseBodyTypeHelperMethod(String contentType) { + String bodyType = null; + if (matchTo(JSON_ENCODED, contentType) || matchTo(JRD_JSON, contentType)) { + bodyType = JSON; + } else if (matchTo(JWT_ENCODED, contentType)) { + bodyType = JWT; + } else if (matchTo(URL_ENCODED, contentType)) { + bodyType = URLENCODED; + } + return bodyType; + } + + public static String verifyHeader(FakeResponse response, String bodyType) throws WrongContentType, ValueError { + logger.debug("Response headers: " + response.getHeaders().toString()); + logger.debug("Response txt: " + response.getText().toString()); + + String contentType = response.getHeaders().getContentType(); + if (Strings.isNullOrEmpty(contentType)) { + if (!Strings.isNullOrEmpty(bodyType)) { + return bodyType; + } else { + return "txt"; + } + } + + logger.debug("Expected body type: " + bodyType); + + if (bodyType.isEmpty()) { + bodyType = getResponseBodyTypeHelperMethod(contentType); + if(bodyType == null) { + bodyType = TEXT; + } + } else if (bodyType.equals(JSON)) { + if (matchTo(JWT_ENCODED, contentType)) { + bodyType = JWT; + } else if(!matchTo(JSON_ENCODED, contentType) || !matchTo(JRD_JSON, contentType)){ + throw new WrongContentType(contentType); + } + } else if (bodyType.equals(JWT)) { + if (!matchTo(JWT_ENCODED, contentType)) { + throw new WrongContentType(contentType); + } + } else if (bodyType.equals(URLENCODED)) { + if (!matchTo(DEFAULT_POST_CONTENT_TYPE, contentType) && + !matchTo(PLAIN_TEXT, contentType)) { + throw new WrongContentType(contentType); + } + } else if (bodyType.equals(TEXT)) { + if (!matchTo(PLAIN_TEXT, contentType) && !matchTo(HTML_TEXT, contentType)) { + throw new WrongContentType("Content type: " + contentType); + } + } else { + throw new ValueError("Unknown return format: " + bodyType); + } + + logger.debug("Got body type: " + bodyType); + return bodyType; + } + + public Integer sortSignAlgorithm(String algorithm1, String algorithm2) { + if (sortOrder.get(algorithm1.substring(0, 2)) < sortOrder.get(algorithm2.substring(0, 2))) { + return -1; + } else if (sortOrder.get(algorithm1.substring(0, 2)) > sortOrder.get(algorithm2.substring(0, 2))) { + return 1; + } else { + return algorithm1.compareTo(algorithm2); + } + } + + public static long dateToTime(String date) throws ParseException { + DateFormat inputFormat = new SimpleDateFormat("dd MMM yyy HH:mm:ss zz"); + Date d = inputFormat.parse(date); + return d.getTime(); + } + + public static Map getOrPost(String uri, String method, AuthorizationRequest request, String contentType, String accept, Map args) throws UnsupportedEncodingException, UnsupportedType, JsonProcessingException, CloneNotSupportedException, URISyntaxException, NoSuchFieldException, IllegalAccessException { + Map response = new HashMap<>(); + String urlEncoded; + Field[] keys; + AuthorizationRequest requestClone; + AuthorizationResponse authorizationResponse = new AuthorizationResponse(); + if (method.equals("GET") || method.equals("DELETE")) { + keys = request.getClass().getDeclaredFields(); + if(keys != null) { + requestClone = (AuthorizationRequest) request.clone(); + URI url = new URI(uri); + String query = url.getQuery(); + if(!Strings.isNullOrEmpty(query)) { + requestClone.update(splitQuery(query)); + } + + query = requestClone.toUrlEncoded(); + String urlResponse = uri + "?" + query; + authorizationResponse.setUri(urlResponse); + } else { + authorizationResponse.setUri(uri); + } + } else if (method.equals("POST") || method.equals("PUT")) { + authorizationResponse.setUri(uri); + if (contentType.equals(URL_ENCODED)) { + authorizationResponse.setBody(request.toUrlEncoded()); + } else if (contentType.equals(JSON_ENCODED)) { + authorizationResponse.setBody(request.toJSON()); + } else { + throw new UnsupportedType("Unsupported content type " + contentType); + } + + Map headers = new HashMap<>(); + headers.put("Content-Type", contentType); + + if (accept != null) { + headers = new HashMap<>(); + headers.put("Accept", accept); + } + if (args.containsKey("headers")) { + //kwargs["headers"].update(header_ext) + } else { + args.put("headers", headers); + } + response.put("args", args); + } else { + throw new UnsupportedType("Unsupported HTTP method " + method); + } + + return response; + } + + private static Map splitQuery(String query) throws UnsupportedEncodingException { + Map queryPairs = new LinkedHashMap(); + String[] pairs = query.split("&"); + for (String pair : pairs) { + int idx = pair.indexOf("="); + queryPairs.put(URLDecoder.decode(pair.substring(0, idx), "UTF-8"), URLDecoder.decode(pair.substring(idx + 1), "UTF-8")); + } + + return queryPairs; + } +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/webfinger/JRD.java b/lib/src/main/java/com/auth0/jwt/oiccli/webfinger/JRD.java new file mode 100644 index 0000000..ea771ba --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/webfinger/JRD.java @@ -0,0 +1,8 @@ +package com.auth0.jwt.oiccli.webfinger; + +public class JRD { + + public String toJSON() { + return ""; + } +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/webfinger/URINormalizer.java b/lib/src/main/java/com/auth0/jwt/oiccli/webfinger/URINormalizer.java new file mode 100644 index 0000000..55d1cc1 --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/webfinger/URINormalizer.java @@ -0,0 +1,48 @@ +package com.auth0.jwt.oiccli.webfinger; + +public class URINormalizer { + + public boolean hasScheme(String path) { + if (path.contains("://")) { + return true; + } else { + String authority = path.replace("/", "#") + .replace("?", "#").split("#")[0]; + + String hostOrPort; + if (authority.contains(":")) { + hostOrPort = authority.split(":", 1)[1]; + if (hostOrPort.matches("^\\d+$")) { + return false; + } + } else { + return false; + } + } + + return true; + } + + public static boolean isAccountSchemeAssumed(String path) { + String[] arr; + if (path.contains("@")) { + arr = path.split("@"); + String host = arr[arr.length - 1]; + return !(host.contains(":") || host.contains("/") || host.contains("?")); + } else { + return false; + } + } + + public String normalize(String path) { + if (!this.hasScheme(path)) { + if (this.isAccountSchemeAssumed(path)) { + path = "acct:" + path; + } else { + path = "https://" + path; + } + } + + return path.split("#")[0]; + } +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/webfinger/WebFinger.java b/lib/src/main/java/com/auth0/jwt/oiccli/webfinger/WebFinger.java new file mode 100644 index 0000000..179c3ce --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/webfinger/WebFinger.java @@ -0,0 +1,145 @@ +package com.auth0.jwt.oiccli.webfinger; + +import com.auth0.jwt.oiccli.tuples.Tuple; +import com.auth0.jwt.oiccli.exceptions.WebFingerError; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.google.common.base.Strings; +import com.google.gson.Gson; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.slf4j.LoggerFactory; + +public class WebFinger { + public String defaultRelt; + public Object httpd; + private JRD jrd; + private List> events; + private static final String WF_URL = "https://%s/.well-known/webfinger"; + final private static org.slf4j.Logger logger = LoggerFactory.getLogger(WebFinger.class); + private static final String OIC_ISSUER = "http://openid.net/specs/connect/1.0/issuer"; + private static final Gson gson = new Gson(); + + public WebFinger(String defaultRelt, Object httpd) { + this.defaultRelt = defaultRelt; + this.httpd = httpd; + this.jrd = null; + this.events = new ArrayList<>(); + } + + public String query(String resource, List rel) throws URISyntaxException, WebFingerError { + resource = new URINormalizer().normalize(resource); + List queryParamsTuple = new ArrayList<>(Arrays.asList(new Tuple("resource", resource))); + + if (rel == null) { + if (!Strings.isNullOrEmpty(this.defaultRelt)) { + queryParamsTuple.add(new Tuple("rel", this.defaultRelt)); + } + } else { + for (String index : rel) { + queryParamsTuple.add(new Tuple("rel", index)); + } + } + + String host; + if (resource.startsWith("http")) { + URI uri = new URI(resource); + host = uri.getHost(); + int port = uri.getPort(); + if (port != -1) { + host += ":" + port; + } + } else if (resource.startsWith("acct:")) { + String[] arr = resource.split("@"); + host = arr[arr.length - 1]; + arr = host.replace("/", "#").replace("?", "#").split("#"); + host = arr[0]; + } else if (resource.startsWith("device:")) { + String[] arr = resource.split(":"); + host = arr[1]; + } else { + throw new WebFingerError("Unknown schema"); + } + + String queryParams = ""; + for (int i = 0; + i < queryParamsTuple.size(); + i++) { + queryParams += queryParamsTuple.get(i).getA() + "=" + queryParamsTuple.get(i).getB(); + if (i != queryParamsTuple.size() - 1) { + queryParams += "&"; + } + } + + return String.format(WF_URL, host) + "?" + URLEncoder.encode(queryParams); + } + + public Map httpArgs(JRD jrd) throws JsonProcessingException { + if (jrd == null) { + if (this.jrd != null) { + jrd = this.jrd; + } else { + return null; + } + } + + Map hMap = new HashMap() {{ + put("Access-Control-Allow-Origin", "*"); + put("Content-Type", "application/json; charset=UTF-8"); + }}; + + Map headersAndBody = new HashMap<>(); + headersAndBody.put("headers", hMap); + headersAndBody.put("body", jrd.toJSON()); + + return headersAndBody; + } + + public static Object linkDeser(Object val, String sFormat) { + if (val instanceof Map) { + return val; + } else if (sFormat.equals("dict") || sFormat.equals("json")) { + if (!(val instanceof String)) { + val = gson.toJson(val); + sFormat = "json"; + } + } + + return link().deserialize(val, sFormat); + } + + /*public static Object messageSer(Object inst, String sFormat, int lev) throws MessageException, OicMsgError { + Object res; + if (sFormat.equals("urlencoded") || sFormat.equals("json")) { + if (inst instanceof Map) { + if (sFormat.equals("json")) { + res = gson.toJson(inst); + } else { + res = Base64.encodeBase64URLSafe() + } + } + //elif isinstance(inst, LINK): + //res = inst.serialize(sformat, lev) + else { + res = inst; + } + } else if (sFormat.equals("dict")) { + if (inst instanceof Map) { + res = inst.serialize(sFormat, lev); + } else if (inst instanceof String) { + res = inst; + } else { + throw new MessageException("Wrong type: " + inst.getClass()); + } + } else { + throw new OicMsgError("Unknown sFormat" + inst); + } + + return res; + }*/ +} From 451ecaf8543b33857e16c4d63c45a99b139b8aa5 Mon Sep 17 00:00:00 2001 From: Justin Dahmubed Date: Tue, 6 Feb 2018 13:25:56 -0800 Subject: [PATCH 3/4] Oicclient excluding Service --- .../jwt/oiccli/AuthorizationResponse.java | 6 + .../java/com/auth0/jwt/oiccli/Service.java | 12 +- .../main/java/com/auth0/jwt/oiccli/State.java | 25 ++- .../java/com/auth0/jwt/oiccli/StateInfo.java | 13 ++ .../auth0/jwt/oiccli/Utils/ClientInfo.java | 43 ++++- .../java/com/auth0/jwt/oiccli/Utils/PKCE.java | 3 +- .../com/auth0/jwt/oiccli/Utils/Utils.java | 22 +-- .../jwt/oiccli/client_auth/BearerBody.java | 2 +- .../jwt/oiccli/client_auth/BearerHeader.java | 4 +- .../oiccli/client_auth/ClientSecretBasic.java | 15 +- .../oiccli/client_auth/ClientSecretJWT.java | 2 +- .../client_auth/JWSAuthenticationMethod.java | 17 +- .../oiccli/exceptions/ConfigurationError.java | 7 + .../oiccli/exceptions/MissingParameter.java | 7 + .../jwt/oiccli/exceptions/ParameterError.java | 7 + .../jwt/oiccli/exceptions/UnknownState.java | 4 +- .../auth0/jwt/oiccli/service/AccessToken.java | 30 +++ .../jwt/oiccli/service/Authorization.java | 141 ++++++++++++++ .../com/auth0/jwt/oiccli/service/CheckID.java | 31 ++++ .../jwt/oiccli/service/CheckSession.java | 31 ++++ .../auth0/jwt/oiccli/service/EndSession.java | 43 +++++ .../oiccli/service/ProviderInfoDiscovery.java | 139 ++++++++++++++ .../jwt/oiccli/service/Registration.java | 75 ++++++++ .../oiccli/service/RegistrationResponse.java | 4 + .../com/auth0/jwt/oiccli/service/Service.java | 43 +++++ .../auth0/jwt/oiccli/service/UserInfo.java | 175 ++++++++++++++++++ .../jwt/oiccli/service/VerifiedIdToken.java | 10 + .../auth0/jwt/oiccli/service/WebFinger.java | 74 ++++++++ .../java/com/auth0/jwt/oiccli/util/Util.java | 4 +- .../com/auth0/jwt/oiccli/webfinger/JRD.java | 9 + .../auth0/jwt/oiccli/webfinger/WebFinger.java | 8 +- 31 files changed, 955 insertions(+), 51 deletions(-) create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/exceptions/ConfigurationError.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/exceptions/MissingParameter.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/exceptions/ParameterError.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/service/AccessToken.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/service/Authorization.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/service/CheckID.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/service/CheckSession.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/service/EndSession.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/service/ProviderInfoDiscovery.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/service/Registration.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/service/RegistrationResponse.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/service/Service.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/service/UserInfo.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/service/VerifiedIdToken.java create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/service/WebFinger.java diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/AuthorizationResponse.java b/lib/src/main/java/com/auth0/jwt/oiccli/AuthorizationResponse.java index d843192..a649d84 100644 --- a/lib/src/main/java/com/auth0/jwt/oiccli/AuthorizationResponse.java +++ b/lib/src/main/java/com/auth0/jwt/oiccli/AuthorizationResponse.java @@ -1,11 +1,13 @@ package com.auth0.jwt.oiccli; import com.auth0.jwt.creators.Message; +import com.auth0.jwt.oiccli.service.VerifiedIdToken; public class AuthorizationResponse extends Message{ private String uri; private String body; + private VerifiedIdToken verifiedIdToken; public String getUri() { return uri; @@ -18,4 +20,8 @@ public void setUri(String uri) { public void setBody(String body) { this.body = body; } + + public VerifiedIdToken getVerifiedIdToken() { + return verifiedIdToken; + } } diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/Service.java b/lib/src/main/java/com/auth0/jwt/oiccli/Service.java index ea2990d..2130eec 100644 --- a/lib/src/main/java/com/auth0/jwt/oiccli/Service.java +++ b/lib/src/main/java/com/auth0/jwt/oiccli/Service.java @@ -4,6 +4,7 @@ import com.auth0.jwt.oiccli.Utils.ClientInfo; import com.auth0.jwt.oiccli.exceptions.HTTPError; import com.auth0.jwt.oiccli.exceptions.MissingEndpoint; +import com.auth0.jwt.oiccli.exceptions.UnsupportedType; import com.auth0.jwt.oiccli.exceptions.ValueError; import com.auth0.jwt.oiccli.exceptions.WrongContentType; import com.auth0.jwt.oiccli.responses.ErrorResponse; @@ -11,6 +12,7 @@ import com.auth0.jwt.oiccli.util.FakeResponse; import com.auth0.jwt.oiccli.util.Util; import com.google.common.base.Strings; +import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; import java.text.ParseException; @@ -43,12 +45,12 @@ public class Service { private String clientAuthenticationMethod; private List events; private Map defaultRequestArgs; - private List preConstruct; + protected List preConstruct; private List postConstruct; - private List postParseResponse; + protected List postParseResponse; private Map conf; - public Service(String httpLib, KeyJar keyJar, String clientAuthenticationMethod, Map conf, Map args) throws NoSuchFieldException, IllegalAccessException { + public Service(String httpLib, KeyJar keyJar, String clientAuthenticationMethod, Map conf) throws NoSuchFieldException, IllegalAccessException { this.httpLib = httpLib; this.keyJar = keyJar; this.clientAuthenticationMethod = clientAuthenticationMethod; @@ -280,6 +282,10 @@ public String getConfigurationAttribute(String attribute, String defaultValue) { } } + public String getConfigurationAttribute(String attribute) { + return getConfigurationAttribute(attribute, null); + } + public void buildServices() { throw new UnsupportedOperationException(); } diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/State.java b/lib/src/main/java/com/auth0/jwt/oiccli/State.java index 0174dda..1dcfa1d 100644 --- a/lib/src/main/java/com/auth0/jwt/oiccli/State.java +++ b/lib/src/main/java/com/auth0/jwt/oiccli/State.java @@ -4,7 +4,6 @@ import com.auth0.jwt.oiccli.exceptions.ExpiredToken; import com.auth0.jwt.oiccli.exceptions.UnknownState; import com.google.common.base.Strings; -import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -29,6 +28,20 @@ public State(String clientId, Database db, String dbName, int lifetime) { this.lifetime = lifetime; } + public String createState(String receiver, AuthorizationRequest request, String state) { + if(Strings.isNullOrEmpty(state)) { + state = StringUtil.generateRandomString(24); + } + + long now = System.currentTimeMillis(); + + StateInfo stateInfo = new StateInfo(this.clientId, receiver, now); + stateInfo.update(request); + + //self[_state] = _state_info + return state; + } + public StateInfo updateTokenInfo(StateInfo info, Message authorizationResponse) { Token token = info.getToken(); if (token == null) { @@ -114,16 +127,20 @@ public Token getTokenInfo(String state) throws ExpiredToken { return getTokenInfo(state, 0); } + public Map getNonceToState(String nonce) { + return this.getDB().get("nonce" + nonce); + } + public Map getResponseArgs(String state, AccessTokenRequest request, int now) throws ExpiredToken, NoSuchFieldException, IllegalAccessException { StateInfo stateInfo = this.getDB().getStateInfo(state); Map responseArgs = new HashMap<>(); for (String claim : request.getCParam().keySet()) { if (claim.equals("accessToken")) { - Map tInfo = this.getTokenInfo(state, now); + Token tInfo = this.getTokenInfo(state, now); if (tInfo == null) { continue; } - responseArgs.put(claim, tInfo.get("accessToken")); + responseArgs.put(claim, tInfo.getAccessToken()); } else { responseArgs.put(claim, stateInfo.getClass().getField(claim).get(this)); } @@ -132,7 +149,7 @@ public Map getResponseArgs(String state, AccessTokenRequest requ return responseArgs; } - private Token getIdToken(String state) { + public Token getIdToken(String state) { return this.getDB().getStateInfo(state).getToken(); } diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/StateInfo.java b/lib/src/main/java/com/auth0/jwt/oiccli/StateInfo.java index dc74d3b..546b621 100644 --- a/lib/src/main/java/com/auth0/jwt/oiccli/StateInfo.java +++ b/lib/src/main/java/com/auth0/jwt/oiccli/StateInfo.java @@ -8,6 +8,15 @@ public class StateInfo { private String claim; private Token token; private String refreshToken; + private String clientId; + private String as; + private long iat; + + public StateInfo(String clientId, String receiver, long now) { + this.clientId = clientId; + this.as = receiver; + this.iat = now; + } public void setCode(String code) { this.code = code; @@ -32,4 +41,8 @@ public void update(Map args) { public void setRefreshToken(String refreshToken) { this.refreshToken = refreshToken; } + + public void update(AuthorizationRequest request) { + throw new UnsupportedOperationException(); + } } diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/Utils/ClientInfo.java b/lib/src/main/java/com/auth0/jwt/oiccli/Utils/ClientInfo.java index b852327..a62bc71 100644 --- a/lib/src/main/java/com/auth0/jwt/oiccli/Utils/ClientInfo.java +++ b/lib/src/main/java/com/auth0/jwt/oiccli/Utils/ClientInfo.java @@ -49,7 +49,7 @@ public class ClientInfo { private String cId; private String cSecret; private String issuer; - private Map redirectUris; + private List redirectUris; private Map> clientPrefs; private Map allow; Map> behavior; @@ -57,7 +57,7 @@ public class ClientInfo { private State stateDB; private boolean shouldBeStrictOnPreferences; private Map> providerInfo; - private Map registrationResponse; + private Map> registrationResponse; private Map> kid; private List events; private Map> config; @@ -124,7 +124,7 @@ public ClientInfo(KeyJar keyJar, Map> config, List> config, List> signEncAlgs(String type) { } public boolean verifyAlgSupport(String algorithm, String usage, String type) { - List supported = this.providerInfo.get(usage + "" + type + "valuesSupported"); + List supported = this.providerInfo.get(usage + type + "valuesSupported"); return supported.contains(algorithm); } @@ -270,11 +271,43 @@ public KeyJar getKeyJar() { return keyJar; } - public Map getRegistrationResponse() { + public Map> getRegistrationResponse() { return registrationResponse; } public Map> getProviderInfo() { return providerInfo; } + + public void setIssuer(String issuer) { + this.issuer = issuer; + } + + public String getIssuer() { + return issuer; + } + + public Map> getClientPrefs() { + return clientPrefs; + } + + public boolean getShouldBeStrictOnPreferences() { + return shouldBeStrictOnPreferences; + } + + public void setBehavior(Map> behavior) { + this.behavior = behavior; + } + + public List getRedirectUris() { + return redirectUris; + } + + public String getRequestsDir() { + return requestsDir; + } + + public void setRegistrationResponse(Map> registrationResponse) { + this.registrationResponse = registrationResponse; + } } diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/Utils/PKCE.java b/lib/src/main/java/com/auth0/jwt/oiccli/Utils/PKCE.java index 9c9feeb..c6ad58f 100644 --- a/lib/src/main/java/com/auth0/jwt/oiccli/Utils/PKCE.java +++ b/lib/src/main/java/com/auth0/jwt/oiccli/Utils/PKCE.java @@ -2,6 +2,7 @@ import com.auth0.jwt.oiccli.StringUtil; import com.auth0.jwt.oiccli.exceptions.ExpiredToken; +import com.google.common.base.Strings; import com.sun.org.apache.xml.internal.security.utils.Base64; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -26,7 +27,7 @@ public static Map addCodeChallenge(ClientInfo clientInfo, String codeVerifier = Base64.encode(codeVerifier.getBytes()); String method = (String) clientInfo.getConfig().get("codeChallenge").get("method"); - if (method == null) { + if (Strings.isNullOrEmpty(method)) { method = S256; } diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/Utils/Utils.java b/lib/src/main/java/com/auth0/jwt/oiccli/Utils/Utils.java index 0846cdb..7314d1d 100644 --- a/lib/src/main/java/com/auth0/jwt/oiccli/Utils/Utils.java +++ b/lib/src/main/java/com/auth0/jwt/oiccli/Utils/Utils.java @@ -17,11 +17,9 @@ public static Message requestObjectEncryption(Message message, ClientInfo client if(Strings.isNullOrEmpty(encryptionAlg)) { List listOfAlgs = clientInfo.getBehavior().get("requestObjectEncryptionAlg"); - if(listOfAlgs != null || !listOfAlgs.isEmpty()) { + if(listOfAlgs != null && !listOfAlgs.isEmpty()) { encryptionAlg = listOfAlgs.get(0); - } - - if(encryptionAlg == null) { + } else { return message; } } @@ -30,15 +28,13 @@ public static Message requestObjectEncryption(Message message, ClientInfo client if(Strings.isNullOrEmpty(encryptionEnc)) { List listOfAlgs = clientInfo.getBehavior().get("requestObjectEncryptionEnc"); - if(listOfAlgs != null || !listOfAlgs.isEmpty()) { + if(listOfAlgs != null && !listOfAlgs.isEmpty()) { encryptionEnc = listOfAlgs.get(0); - } - - if(encryptionEnc == null) { + } else { throw new MissingRequiredAttribute("No requestObjectEncryptionEnc specified"); } } - + //JWE - cryptojwt JWE jwe = new JWE(message, encryptionAlg, encryptionEnc); String keyType = StringUtil.alg2keytype(encryptionAlg); @@ -68,13 +64,13 @@ public static Tuple constructRequestUri(String localDir, String basePath, Map> construct(ResourceRequest resourceReques args.put("state", state); } - //resourceRequest.setAccessToken(clientInfo.getStateDb().getTokenInfo(state).get("accessToken")); + resourceRequest.setAccessToken(clientInfo.getStateDb().getTokenInfo(args).get("accessToken")); } } diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/client_auth/BearerHeader.java b/lib/src/main/java/com/auth0/jwt/oiccli/client_auth/BearerHeader.java index fce174d..23b519a 100644 --- a/lib/src/main/java/com/auth0/jwt/oiccli/client_auth/BearerHeader.java +++ b/lib/src/main/java/com/auth0/jwt/oiccli/client_auth/BearerHeader.java @@ -19,13 +19,13 @@ public Map> construct(ResourceRequest resourceReques } else { accessToken = requestArgs.get("accessToken"); - if (accessToken == null) { + if (Strings.isNullOrEmpty(accessToken)) { accessToken = clientInfo.getStateDb().getTokenInfo(args).get("accessToken"); } } } else { accessToken = args.get("accessToken"); - if (accessToken == null) { + if (Strings.isNullOrEmpty(accessToken)) { throw new MissingRequiredAttribute("accessToken"); } } diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/client_auth/ClientSecretBasic.java b/lib/src/main/java/com/auth0/jwt/oiccli/client_auth/ClientSecretBasic.java index fecd8cd..81c4e81 100644 --- a/lib/src/main/java/com/auth0/jwt/oiccli/client_auth/ClientSecretBasic.java +++ b/lib/src/main/java/com/auth0/jwt/oiccli/client_auth/ClientSecretBasic.java @@ -3,6 +3,7 @@ import com.auth0.jwt.oiccli.AccessTokenRequest; import com.auth0.jwt.oiccli.Utils.ClientInfo; import com.auth0.jwt.oiccli.exceptions.AuthenticationFailure; +import com.google.common.base.Strings; import java.util.HashMap; import java.util.Map; import org.apache.commons.codec.binary.Base64; @@ -20,18 +21,18 @@ public Map> construct(AccessTokenRequest request, Cl } String password = args.get("password"); - if (password == null) { + if (Strings.isNullOrEmpty(password)) { password = httpArgs.get("password").get("password"); - if (password == null) { + if (Strings.isNullOrEmpty(password)) { password = request.getClientSecret(); - if (password == null) { + if (Strings.isNullOrEmpty(password)) { password = cliInfo.getClientSecret(); } } } String user = args.get("user"); - if (user == null) { + if (Strings.isNullOrEmpty(user)) { user = cliInfo.getClientId(); } @@ -43,12 +44,12 @@ public Map> construct(AccessTokenRequest request, Cl request.setClientSecret(null); - if (request.get("grantType").equals("authorizationCode")) { - if (request.getClientId() != null) { + if (request.get("grantType") != null && request.get("grantType").equals("authorizationCode")) { + if (Strings.isNullOrEmpty(request.getClientId())) { request.setClientId(cliInfo.getClientId()); } } else { - boolean req = request.getCParam("clientId").get(VREQUIRED); + boolean req = request.getCParam("clientId").get(JWSAuthenticationMethod.V_Required); if (!req) { request.remove("clientId"); diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/client_auth/ClientSecretJWT.java b/lib/src/main/java/com/auth0/jwt/oiccli/client_auth/ClientSecretJWT.java index a5312c8..93b186a 100644 --- a/lib/src/main/java/com/auth0/jwt/oiccli/client_auth/ClientSecretJWT.java +++ b/lib/src/main/java/com/auth0/jwt/oiccli/client_auth/ClientSecretJWT.java @@ -12,7 +12,7 @@ public static String chooseAlgorithm(String entity, Map args) th return JWSAuthenticationMethod.chooseAlgorithm(entity, args); } - public static String chooseAlgorithm(Map args) { + public static String chooseAlgorithm(Map args) throws AuthenticationFailure { return JWSAuthenticationMethod.chooseAlgorithm("clientSecretJwt", args); } diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/client_auth/JWSAuthenticationMethod.java b/lib/src/main/java/com/auth0/jwt/oiccli/client_auth/JWSAuthenticationMethod.java index e795f08..37094e8 100644 --- a/lib/src/main/java/com/auth0/jwt/oiccli/client_auth/JWSAuthenticationMethod.java +++ b/lib/src/main/java/com/auth0/jwt/oiccli/client_auth/JWSAuthenticationMethod.java @@ -4,6 +4,7 @@ import com.auth0.jwt.oiccli.Utils.ClientInfo; import com.auth0.jwt.oiccli.exceptions.AuthenticationFailure; import com.auth0.jwt.oiccli.exceptions.NoMatchingKey; +import com.google.common.base.Strings; import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -20,13 +21,13 @@ public class JWSAuthenticationMethod extends ClientAuthenticationMethod { put("private_key_jwt", "RS256"); }}; private static final String JWT_BEARER = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"; - private static final String V_Required = 1; + public static final int V_Required = 1; public static String chooseAlgorithm(String context, Map args) throws AuthenticationFailure { String algorithm = args.get("algorithm"); - if (algorithm == null) { + if (Strings.isNullOrEmpty(algorithm)) { algorithm = DEF_SIGN_ALG.get(context); - if (algorithm == null) { + if (Strings.isNullOrEmpty(algorithm)) { throw new AuthenticationFailure("Missing algorithm specification"); } } @@ -59,7 +60,7 @@ public Key getKeyByKid(String kid, String algorithm, ClientInfo clientInfo) thro } } - public Map construct(AccessTokenRequest request, ClientInfo clientInfo, Map requestArgs, + public Map construct(AccessTokenRequest request, ClientInfo clientInfo, Map httpArgs, Map args) throws AuthenticationFailure, NoMatchingKey { String algorithm = null; List audience = null; @@ -75,15 +76,15 @@ public Map construct(AccessTokenRequest request, ClientInfo clie request.setClientAssertionType(JWT_BEARER); } } else { - if (args.get("authenticationEndpoint").equals("token") || args.get("authenticationEndpoint").equals("refresh")) { - algorithm = clientInfo.getRegistrationResponse().get("tokenEndpointAuthSigningAlg"); + if (args.get("authenticationEndpoint") != null && args.get("authenticationEndpoint").equals("token") || args.get("authenticationEndpoint") != null && args.get("authenticationEndpoint").equals("refresh")) { + algorithm = clientInfo.getRegistrationResponse().get("tokenEndpointAuthSigningAlg").get(0); audience = clientInfo.getProviderInfo().get("tokenEndpoint"); } else { audience = clientInfo.getProviderInfo().get("issuer"); } } - if (algorithm == null) { + if (Strings.isNullOrEmpty(algorithm)) { algorithm = this.chooseAlgorithm(args); } @@ -98,6 +99,8 @@ public Map construct(AccessTokenRequest request, ClientInfo clie } else { signingKey = Arrays.asList(this.getSigningKey(algorithm, clientInfo)); } + } else { + signingKey = Arrays.asList(this.getSigningKey(algorithm, clientInfo)); } Map hMap = new HashMap<>(); diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/ConfigurationError.java b/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/ConfigurationError.java new file mode 100644 index 0000000..5675da2 --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/ConfigurationError.java @@ -0,0 +1,7 @@ +package com.auth0.jwt.oiccli.exceptions; + +public class ConfigurationError extends Exception { + public ConfigurationError(String message) { + super(message); + } +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/MissingParameter.java b/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/MissingParameter.java new file mode 100644 index 0000000..b6e66b9 --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/MissingParameter.java @@ -0,0 +1,7 @@ +package com.auth0.jwt.oiccli.exceptions; + +public class MissingParameter extends Exception { + public MissingParameter(String message) { + super(message); + } +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/ParameterError.java b/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/ParameterError.java new file mode 100644 index 0000000..60e0ee5 --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/ParameterError.java @@ -0,0 +1,7 @@ +package com.auth0.jwt.oiccli.exceptions; + +public class ParameterError extends Exception { + public ParameterError(String message) { + super(message); + } +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/UnknownState.java b/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/UnknownState.java index 350a456..211dc51 100644 --- a/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/UnknownState.java +++ b/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/UnknownState.java @@ -1,7 +1,7 @@ package com.auth0.jwt.oiccli.exceptions; public class UnknownState extends Exception { - public UnknownState(String state) { - super(state); + public UnknownState(String message) { + super(message); } } \ No newline at end of file diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/service/AccessToken.java b/lib/src/main/java/com/auth0/jwt/oiccli/service/AccessToken.java new file mode 100644 index 0000000..d13b69d --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/service/AccessToken.java @@ -0,0 +1,30 @@ +package com.auth0.jwt.oiccli.service; + +import com.auth0.jwt.oiccli.AuthorizationResponse; +import com.auth0.jwt.oiccli.exceptions.ParameterError; +import com.auth0.jwt.oiccli.Utils.ClientInfo; +import com.auth0.jwt.oiccli.exceptions.UnknownState; +import com.google.common.base.Strings; +import java.util.Arrays; +import java.util.Map; + +public class AccessToken extends service.AccessToken { + private AccessTokenRequest accessTokenRequest; //oicmsg + private AccessTokenResponse accessTokenResponse; //oicmsg + private TokenErrorResponse tokenErrorResponse; //oicmsg + private List<> postParseResponse; + + public AccessToken(String httpLib, KeyJar keyJar, String clientAuthenticationMethod, Map conf) { + super(httpLib, keyJar, clientAuthenticationMethod, conf); + this.postParseResponse = Arrays.asList(this.oicPostParseResponse, storeIdToken); + } + + public void oicPostParseResponse(AuthorizationResponse response, ClientInfo cliInfo, String state) throws ParameterError, UnknownState { + cliInfo.getStateDb().addResponse(response, state); + VerifiedIdToken idt = response.getVerifiedIdToken(); + String nonceToState = cliInfo.getStateDb().nonceToState(idt.getNonce()); + if (!Strings.isNullOrEmpty(nonceToState) && !nonceToState.equals(state)) { + throw new ParameterError("Someone has messed with the 'nonce'"); + } + } +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/service/Authorization.java b/lib/src/main/java/com/auth0/jwt/oiccli/service/Authorization.java new file mode 100644 index 0000000..446c473 --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/service/Authorization.java @@ -0,0 +1,141 @@ +package com.auth0.jwt.oiccli.service; + +import com.auth0.jwt.creators.Message; +import com.auth0.jwt.oiccli.AuthorizationRequest; +import com.auth0.jwt.oiccli.AuthorizationResponse; +import com.auth0.jwt.oiccli.StringUtil; +import com.auth0.jwt.oiccli.Utils.ClientInfo; +import com.auth0.jwt.oiccli.Utils.Utils; +import com.auth0.jwt.oiccli.exceptions.ValueError; +import com.auth0.jwt.oiccli.tuples.Tuple; +import com.google.common.base.Strings; +import java.io.BufferedWriter; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class Authorization extends service.Authorization { + + private AuthorizationRequest authorizationRequest; //oicmsg + private AuthorizationResponse authorizationResponse; //oicmsg + private AuthorizationErrorResponse authorizationErrorResponse; //oicmsg + private Map> defaultRequestArgs; + //keyjar - oicmsg + public Authorization(String httpLib, KeyJar keyJar, String clientAuthenticationMethod, Map conf) { + super(httpLib, keyJar, clientAuthenticationMethod, conf); + this.defaultRequestArgs = new HashMap() {{ + put("scope", Arrays.asList("openId")); + }}; + this.preConstruct = Arrays.asList(oicPreConstruct); + this.postConstruct = Arrays.asList(this.oicPostConstruct); + this.postParseResponse.add(storeIdToken); + } + + public List> oicPreConstruct(ClientInfo clientInfo, Map requestArgs, Map args) { + if (requestArgs != null) { + String responseType = (String) requestArgs.get("responseType"); + if (Strings.isNullOrEmpty(responseType)) { + requestArgs.put("responseType", clientInfo.getBehavior().get("responseTypes").get(0)); + if (responseType.contains("token") || responseType.contains("idToken")) { + if (!requestArgs.containsKey("nonce")) { + requestArgs.put("nonce", StringUtil.generateRandomString(32)); + } + } + } + } else { + requestArgs = new HashMap(); + } + + Map postArgs = new HashMap<>(); + for (String attribute : Arrays.asList("requestObjectSigningAlg", "algorithm", "sigKid")) { + postArgs.put(attribute, args.get(attribute)); + args.remove(attribute); + } + + if (args.containsKey("requestMethod")) { + if (args.get("requestMethod").equals("reference")) { + postArgs.put("requestParam", "requestUri"); + } else { + postArgs.put("requestParam", "request"); + } + args.remove("requestMethod"); + } + + List responseMode = clientInfo.getBehavior().get("responseMode"); + if (responseMode != null && !responseMode.isEmpty() && responseMode.contains("formPost")) { + requestArgs.put("responseMode", responseMode); + } + + clientInfo.getStateDb().createState(clientInfo.getIssuer(), requestArgs, (String) requestArgs.get("state")); + + if (!requestArgs.containsKey("state")) { + requestArgs.put("state", clientInfo.getStateDb().createState(clientInfo.getIssuer(), requestArgs)); + } + + return Arrays.asList(requestArgs, postArgs); + } + + public Map oicPostConstruct(ClientInfo clientInfo, Map req, Map args) throws ValueError { + //TODO: Check with Roland about this method + + String requestParam = args.get("requestParam"); + args.remove("requestParam"); + + String algorithm = null; + for (String argument : Arrays.asList("requestObjectSigningAlg", "algorithm")) { + algorithm = args.get(argument); + } + + if (algorithm == null) { + algorithm = clientInfo.getBehavior().get("requestObjectSigningAlg"); + if (algorithm == null) { + algorithm = "RS256"; + } + } + + args.put("requestObjectSigningAlg", algorithm); + + if (!args.containsKey("keys") && algorithm != null && !algorithm.equals("none")) { + String kty = StringUtil.alg2keytype(algorithm); + String kid = args.get("sigKid"); + if (kid == null) { + kid = clientInfo.getKid().get("sig").get(kty); + } + args.put("keys", clientInfo.getKeyJar().getSigningKey(kty, kid)); + } + + /* + _req = make_openid_request(req, **kwargs) + */ + + Message _req = Utils.requestObjectEncryption(_req, clientInfo, args); + + String webName; + String fileName; + if (requestParam.equals("request")) { + req.put("request", _req); + } else { + webName = clientInfo.getRegistrationResponse().get("requestUris").get(0); + fileName = clientInfo.filenameFromWebname(webName); + if(Strings.isNullOrEmpty(fileName)) { + Tuple tuple = Utils.constructRequestUri(null, null, args); + webName = (String) tuple.getA(); + fileName = (String) tuple.getB(); + } + BufferedWriter writer = null; + try { + writer = new BufferedWriter(new FileWriter(fileName)); + writer.write(_req); + writer.close(); + } catch (IOException e) { + e.printStackTrace(); + } + req.put("requestUri", webName); + } + + return req; + } +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/service/CheckID.java b/lib/src/main/java/com/auth0/jwt/oiccli/service/CheckID.java new file mode 100644 index 0000000..849d319 --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/service/CheckID.java @@ -0,0 +1,31 @@ +package com.auth0.jwt.oiccli.service; + +import com.auth0.jwt.creators.Message; +import com.auth0.jwt.oiccli.Service; +import com.auth0.jwt.oiccli.Utils.ClientInfo; +import com.auth0.jwt.oiccli.exceptions.MissingParameter; +import com.auth0.jwt.oiccli.responses.ErrorResponse; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class CheckID extends Service { + private static CheckIDRequest checkIDRequest; + private static Message message; + private static ErrorResponse errorResponse; + private static String endpointName = ""; + private static boolean isSynchronous = true; + private static String request = "checkId"; + + public CheckID(String httpLib, KeyJar keyJar, String clientAuthenticationMethod, Map conf) throws NoSuchFieldException, IllegalAccessException { + super(httpLib, keyJar, clientAuthenticationMethod, conf); + this.preConstruct = Arrays.asList(this.oicPreConstruct); + } + + public List> oicPreConstruct(ClientInfo clientInfo, Map requestArgs, Map args) throws MissingParameter { + requestArgs = UserInfo.setIdToken(clientInfo, requestArgs, args); + return Arrays.asList(requestArgs, new HashMap()); + } + +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/service/CheckSession.java b/lib/src/main/java/com/auth0/jwt/oiccli/service/CheckSession.java new file mode 100644 index 0000000..13d2aac --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/service/CheckSession.java @@ -0,0 +1,31 @@ +package com.auth0.jwt.oiccli.service; + +import com.auth0.jwt.creators.Message; +import com.auth0.jwt.oiccli.Service; +import com.auth0.jwt.oiccli.Utils.ClientInfo; +import com.auth0.jwt.oiccli.exceptions.MissingParameter; +import com.auth0.jwt.oiccli.responses.ErrorResponse; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class CheckSession extends Service { + private static CheckSessionRequest checkSessionRequest; + private static Message message; + private static ErrorResponse errorResponse; + private static String endpointName = ""; + private static boolean isSynchronous = true; + private static String request = "checkSession"; + + public CheckSession(String httpLib, KeyJar keyJar, String clientAuthenticationMethod, Map conf) throws NoSuchFieldException, IllegalAccessException { + super(httpLib, keyJar, clientAuthenticationMethod, conf); + this.preConstruct = Arrays.asList(oicPreConstruct); + } + + public List> oicPreConstruct(ClientInfo clientInfo, Map requestArgs, Map args) throws MissingParameter { + requestArgs = UserInfo.setIdToken(clientInfo, requestArgs, args); + return Arrays.asList(requestArgs, new HashMap()); + } + +} \ No newline at end of file diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/service/EndSession.java b/lib/src/main/java/com/auth0/jwt/oiccli/service/EndSession.java new file mode 100644 index 0000000..2443230 --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/service/EndSession.java @@ -0,0 +1,43 @@ +package com.auth0.jwt.oiccli.service; + +import com.auth0.jwt.creators.Message; +import com.auth0.jwt.oiccli.Service; +import com.auth0.jwt.oiccli.Utils.ClientInfo; +import com.auth0.jwt.oiccli.exceptions.MissingParameter; +import com.auth0.jwt.oiccli.responses.ErrorResponse; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class EndSession extends Service { + private static EndSessionRequest endSessionRequest; + private static Message message; + private static ErrorResponse errorResponse; + private static String endpointName = "endSessionEndpoint"; + private static boolean isSynchronous = true; + private static String request = "endSession"; + + public EndSession(String httpLib, KeyJar keyJar, String clientAuthenticationMethod, Map conf) throws NoSuchFieldException, IllegalAccessException { + super(httpLib, keyJar, clientAuthenticationMethod, conf); + this.preConstruct = Arrays.asList(oicPreConstruct); + } + + public List> oicPreConstruct(ClientInfo clientInfo, Map requestArgs, Map args) throws MissingParameter { + requestArgs = UserInfo.setIdToken(clientInfo, requestArgs, args); + return Arrays.asList(requestArgs, new HashMap()); + } + + /* + def factory(req_name, **kwargs): + for name, obj in inspect.getmembers(sys.modules[__name__]): + if inspect.isclass(obj) and issubclass(obj, oiccli.Service): + try: + if obj.__name__ == req_name: + return obj(**kwargs) + except AttributeError: + pass + + return service.factory(req_name, **kwargs) + */ +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/service/ProviderInfoDiscovery.java b/lib/src/main/java/com/auth0/jwt/oiccli/service/ProviderInfoDiscovery.java new file mode 100644 index 0000000..ee3f106 --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/service/ProviderInfoDiscovery.java @@ -0,0 +1,139 @@ +package com.auth0.jwt.oiccli.service; + +import com.auth0.jwt.creators.Message; +import com.auth0.jwt.oiccli.Utils.ClientInfo; +import com.auth0.jwt.oiccli.exceptions.ConfigurationError; +import com.auth0.jwt.oiccli.responses.ErrorResponse; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import oiccli.service.Service; + +public class ProviderInfoDiscovery extends service.ProviderInfoDiscovery { + private Message message; + private ProviderConfigurationResponse providerConfigurationResponse; //oicmsg + private ErrorResponse errorResponse; + private KeyJar keyJar; + private final static Logger logger = LoggerFactory.getLogger(ProviderInfoDiscovery.class); + + public ProviderInfoDiscovery(String httpLib, KeyJar keyJar, String clientAuthenticationMethod, Map conf) { + super(httpLib, keyJar, clientAuthenticationMethod, conf); + this.postParseResponse.insert(0, this.oicPostParseResponse); + + if(conf != null) { + if(conf.get("preLoadKeys") != null) { + this.postParseResponse.append(this.preLoadKeys); + } + } + } + + public void oicPostParseResponse(Map> response, ClientInfo clientInfo) throws ConfigurationError { + matchPreferences(clientInfo, response, clientInfo.getIssuer()); + } + //jwks - cryptojwt + public Jwks preLoadKeys(Map> response) { + Jwks jwks = this.keyJar.exportJwksAsJson(response.get("issuer")); + logger.info("Preloaded keys for " + response.get("issuer") + ": " + jwks); + return jwks; + } + + public static void matchPreferences(ClientInfo clientInfo, Map> pcr, String issuer) throws ConfigurationError { + if (pcr == null) { + pcr = clientInfo.getProviderInfo(); + } + //rr - oicmsg + RegistrationRequest rr = new oic.RegistrationRequest(); + Set set = Service.PREFERENCE2PROVIDER.entrySet(); + Iterator iterator = set.iterator(); + Map.Entry mapEntry; + String key, value; + List listOfValues, values; + while (iterator.hasNext()) { + mapEntry = (Map.Entry) iterator.next(); + key = (String) mapEntry.getKey(); + value = (String) mapEntry.getValue(); + values = clientInfo.getClientPrefs().get(key); + listOfValues = pcr.get(value); + if (listOfValues == null) { + listOfValues = Arrays.asList(Service.PROVIDERDEFAULT.get(key)); + } + if (listOfValues == null) { + logger.info("No info from provider on " + key + " and no default."); + if (clientInfo.getShouldBeStrictOnPreferences()) { + throw new ConfigurationError("OP couldn't match preferences: " + key); + } else { + listOfValues = values; + } + } + + if (listOfValues.contains(values)) { + setBehavior(clientInfo, key, values); + } else { + + List vTypes = rr.getCParam().get(key); + setBehavior(clientInfo, key, new ArrayList()); + for (String valueIndex : values) { + if (listOfValues.contains(valueIndex)) { + Map> behavior = clientInfo.getBehavior(); + if(behavior.get(key) != null) { + List list = behavior.get(key); + list.add(valueIndex); + behavior.put(key, list); + } else { + behavior.put(key, Arrays.asList(valueIndex)); + } + clientInfo.setBehavior(behavior); + break; + } + } + } + + if (!clientInfo.getBehavior().containsKey(key)) { + throw new ConfigurationError("OP couldn't match preferences " + key); + } + } + + for (String keyIndex : clientInfo.getClientPrefs().keySet()) { + if (!clientInfo.getBehavior().containsKey(keyIndex)) { + /* + vtyp = regreq.c_param[key] + if isinstance(vtyp[0], list): + pass + elif isinstance(val, list) and not isinstance(val, + six.string_types): + val = val[0] + */ + + if (!Service.PREFERENCE2PROVIDER.containsKey(keyIndex)) { + List behavior = clientInfo.getBehavior().get(keyIndex); + if (behavior == null) { + behavior = new ArrayList<>(); + } + behavior.addAll(clientInfo.getClientPrefs().get(keyIndex)); + + setBehavior(clientInfo, keyIndex, behavior); + } + } + } + + logger.debug("cliInfo behavior " + clientInfo.getBehavior().toString()); + + } + + private static void setBehavior(ClientInfo clientInfo, String key, List values) { + Map> behavior = clientInfo.getBehavior(); + if (behavior.containsKey(key)) { + List returnedValue = behavior.get(key); + returnedValue.addAll(values); + behavior.put(key, returnedValue); + } else { + behavior.put(key, values); + } + clientInfo.setBehavior(behavior); + } +} \ No newline at end of file diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/service/Registration.java b/lib/src/main/java/com/auth0/jwt/oiccli/service/Registration.java new file mode 100644 index 0000000..6bd24bc --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/service/Registration.java @@ -0,0 +1,75 @@ +package com.auth0.jwt.oiccli.service; + +import com.auth0.jwt.oiccli.Service; +import com.auth0.jwt.oiccli.Utils.ClientInfo; +import com.auth0.jwt.oiccli.exceptions.MissingRequiredAttribute; +import com.auth0.jwt.oiccli.responses.ErrorResponse; +import com.google.common.base.Strings; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class Registration extends Service { + + private RegistrationRequest registrationRequest; //oicmsg + private RegistrationResponse registrationResponse; //oicmsg + private ErrorResponse errorResponse; //oicmsg + private static String endpointName = "registrationEndpoint"; + private static boolean isSynchronous = true; + private static String request = "registration"; + private static String bodyType = "json"; + private static String httpMethod = "POST"; + + public Registration(String httpLib, KeyJar keyJar, String clientAuthenticationMethod, Map conf) throws NoSuchFieldException, IllegalAccessException { + super(httpLib, keyJar, clientAuthenticationMethod, conf); + + this.preConstruct.add(oicPreConstruct); + this.postParseResponse.add(this.oicPostParseResponse()); + } + + public List>> oicPreConstruct(ClientInfo clientInfo, Map> requestArgs) throws NoSuchAlgorithmException { + for (String key : this.registrationRequest.getCParam().keySet()) { + if (!requestArgs.containsKey(key)) { + requestArgs.put(key, clientInfo.getBehavior().get(key)); + } + } + + if (!requestArgs.containsKey("postLogoutRedirectUris")) { + requestArgs.put("postLogoutRedirectUris", clientInfo.getRedirectUris()); //postLogoutRedirectUris?? + } + + if (!requestArgs.containsKey("redirectUris")) { + List redirectUris = clientInfo.getRedirectUris(); + if(redirectUris != null && !redirectUris.isEmpty()) { + requestArgs.put("redirectUris", redirectUris); + } else { + throw new MissingRequiredAttribute("redirectUris is null or empty") + } + } + + String requestDir = clientInfo.getRequestsDir(); + if(!Strings.isNullOrEmpty(requestDir)) { + if (!Strings.isNullOrEmpty(clientInfo.getProviderInfo().get("requireRequestUriRegistration").get(0))) { + requestArgs.put("requestUris", clientInfo.generateRequestUris(clientInfo.getRequestsDir())); + } + } + + return Arrays.asList(requestArgs, new HashMap>()); + } + + public void oicPostParseResponse(RegistrationResponse response, ClientInfo cliInfo) { + cliInfo.setRegistrationResponse(response); + if (!cliInfo.getRegistrationResponse().containsKey("tokenEndpointAuthMethod")) { + Map> hMap = cliInfo.getRegistrationResponse(); + hMap.put("tokenEndpointAuthMethod", Arrays.asList("clientSecretBasic")); + cliInfo.setRegistrationResponse(hMap); + } + + cliInfo.setClientId(response.getClientId().get(0)); + cliInfo.setClientSecret(response.getClientSecret().get(0)); + cliInfo.setRegistrationExpires(response.getClientSecretExpiresAt().get(0)); + cliInfo.setRegistrationAccessToken(response.getRegistrationAccessToken().get(0)); + } +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/service/RegistrationResponse.java b/lib/src/main/java/com/auth0/jwt/oiccli/service/RegistrationResponse.java new file mode 100644 index 0000000..86a3812 --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/service/RegistrationResponse.java @@ -0,0 +1,4 @@ +package com.auth0.jwt.oiccli.service; + +public class RegistrationResponse { +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/service/Service.java b/lib/src/main/java/com/auth0/jwt/oiccli/service/Service.java new file mode 100644 index 0000000..0f66d97 --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/service/Service.java @@ -0,0 +1,43 @@ +package oiccli.service; + +import java.util.HashMap; +import java.util.Map; + +public class Service { + + private String httpLib; + private KeyJar keyJar; + private String clientAuthenticationMethod; + + public static final Map PREFERENCE2PROVIDER = + new HashMap() {{ + put("request_object_signing_alg", "request_object_signing_alg_values_supported"); + put("request_object_encryption_alg", "request_object_encryption_alg_values_supported"); + put("request_object_encryption_enc", "request_object_encryption_enc_values_supported"); + put("userinfo_signed_response_alg", "userinfo_signing_alg_values_supported"); + put("userinfo_encrypted_response_alg", "userinfo_encryption_alg_values_supported"); + put("userinfo_encrypted_response_enc", "userinfo_encryption_enc_values_supported"); + put("id_token_signed_response_alg", "id_token_signing_alg_values_supported"); + put("id_token_encrypted_response_alg", "id_token_encryption_alg_values_supported"); + put("id_token_encrypted_response_enc", "id_token_encryption_enc_values_supported"); + put("default_acr_values", "acr_values_supported"); + put("subject_type", "subject_types_supported"); + put("token_endpoint_auth_method", "token_endpoint_auth_methods_supported"); + put("token_endpoint_auth_signing_alg", "token_endpoint_auth_signing_alg_values_supported"); + put("response_types", "response_types_supported"); + put("grant_types", "grant_types_supported"); + }}; + + public static final Map PROVIDERDEFAULT = + new HashMap() {{ + put("token_endpoint_auth_method", "client_secret_basic"); + put("id_token_signed_response_alg", "RS256"); + }}; + + + public Service(String httpLib, KeyJar keyJar, String clientAuthenticationMethod) { + this.httpLib = httpLib; + this.keyJar = keyJar; + this.clientAuthenticationMethod = clientAuthenticationMethod; + } +} \ No newline at end of file diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/service/UserInfo.java b/lib/src/main/java/com/auth0/jwt/oiccli/service/UserInfo.java new file mode 100644 index 0000000..6782e44 --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/service/UserInfo.java @@ -0,0 +1,175 @@ +package com.auth0.jwt.oiccli.service; + +import com.auth0.jwt.creators.Message; +import com.auth0.jwt.oiccli.Service; +import com.auth0.jwt.oiccli.Token; +import com.auth0.jwt.oiccli.Utils.ClientInfo; +import com.auth0.jwt.oiccli.exceptions.MissingParameter; +import com.auth0.jwt.oiccli.responses.ErrorResponse; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class UserInfo extends Service { + private Message message; + private OpenIDSchema openIDSchema; + private UserInfoErrorResponse userInfoErrorResponse; + private static String endpointName = "userInfoEndpoint"; + private static boolean isSynchronous = true; + private static String request = "userInfo"; + private static String defaultAuthenticationMethod = "bearerHeader"; + private static String httpMethod = "GET"; + private final static Logger logger = LoggerFactory.getLogger(UserInfo.class); + + public UserInfo(String httpLib, KeyJar keyJar, String clientAuthenticationMethod, Map conf) throws NoSuchFieldException, IllegalAccessException { + super(httpLib, keyJar, clientAuthenticationMethod, conf); + this.preConstruct = Arrays.asList(oicPreConstruct); + this.postParseResponse.set(0, this.oicPostParseResponse); + this.postParseResponse.add(this.verifySub); + } + + public List> oicPreConstruct(ClientInfo clientInfo, Map requestArgs, Map args) { + if (requestArgs == null) { + requestArgs = new HashMap<>(); + } + + if (!requestArgs.containsKey("accessToken")) { + Token token = clientInfo.getStateDb().getTokenInfo(args); + requestArgs.put("accessToken", token.getAccessToken()); + } + + return Arrays.asList(requestArgs, new HashMap()); + } + + public Map> oicPostParseResponse(Map> userInfo, ClientInfo clientInfo) { + return this.unpackAggregatedClaims(userInfo, clientInfo); + } + + public void verifySub(Map> userInfo, ClientInfo clientInfo, Map args) { + throw new UnsupportedOperationException(); + } + + public Map> unpackAggregatedClaims(Map> userInfo, ClientInfo clientInfo) { + Map csrc = userInfo.get("claimSources"); + Set set = csrc.entrySet(); + Iterator iterator = set.iterator(); + Map.Entry mapEntry, mapEntryInner; + String key, keyInner; + Map value, valueInner; + while (iterator.hasNext()) { + mapEntry = (Map.Entry) iterator.next(); + key = (String) mapEntry.getKey(); + value = (Map) mapEntry.getValue(); + if (value.containsKey("JWT")) { + Map> aggregatedClaims = new Message().fromJWT(value.get("JWT"), clientInfo.getKeyJar()); + Map cName = userInfo.get("claimNames"); + set = cName.entrySet(); + iterator = set.iterator(); + List claims = new ArrayList<>(); + while (iterator.hasNext()) { + mapEntryInner = (Map.Entry) iterator.next(); + keyInner = (String) mapEntryInner.getKey(); + valueInner = (Map) mapEntryInner.getValue(); + if (valueInner.equals(value)) { + claims.add(keyInner); + } + } + + for (String claim : claims) { + userInfo.put(claim, aggregatedClaims.get(claim)); + } + } + } + + return userInfo; + } + + public Map> fetchDistributedClaims(Map> userInfo, ClientInfo clientInfo, Method callBack) { + Map csrc = userInfo.get("claimSources"); + Set set = csrc.entrySet(); + Iterator iterator = set.iterator(); + Map.Entry mapEntry, mapEntryInner; + String key, keyInner; + Map value, valueInner; + while (iterator.hasNext()) { + mapEntry = (Map.Entry) iterator.next(); + key = (String) mapEntry.getKey(); + value = (Map) mapEntry.getValue(); + ErrorResponse errorResponse; + if (value.containsKey("endpoint")) { + if (value.containsKey("accessToken")) { + //TODO:callback is a method; figure out how to pass a method as a param to a function + errorResponse = oiccli.Service.serviceRequest(value.get("endpoint"), "GET", value.get("accessToken"), clientInfo); + } else { + if (callBack != null) { + errorResponse = oiccli.Service.serviceRequest(value.get("endpoint"), "GET", callBack(value.get("endpoint")), clientInfo); + } else { + errorResponse = oiccli.Service.serviceRequest(value.get("endpoint"), "GET", clientInfo); + } + } + + List claims = new ArrayList<>(); + Set keys = userInfo.get("claimNames").keySet(); + String valueIndex; + for (String keyIndex : keys) { + valueIndex = userInfo.get("claimNames").get(keyIndex); + if (valueIndex.equals(key)) { + claims.add(valueIndex); + } + } + + if (new HashSet<>(claims).equals(new HashSet<>(errorResponse.getKeys()))) { + logger.warn("Claims from claim source doesn't match what's in the userinfo"); + } + + for (String errorResponseKey : errorResponse.keySet()) { + userInfo.put(errorResponseKey, errorResponse.get(errorResponseKey)); + } + } + } + return userInfo; + } + + public static Map setIdToken(ClientInfo cliInfo, Map requestArgs, Map args) throws MissingParameter { + if (requestArgs == null) { + requestArgs = new HashMap<>(); + } + + String property = args.get("prop"); + if (property == null) { + property = "idToken"; + } + + if (!requestArgs.containsKey(property)) { + Token idToken; + String state = getState(requestArgs, args); + idToken = cliInfo.getStateDb().getIdToken(state); + if (idToken == null) { + throw new MissingParameter("No valid id token available"); + } + requestArgs.put(property, idToken); + } + + return requestArgs; + } + + public static String getState(Map requestArgs, Map args) throws MissingParameter { + String state = (String) args.get("state"); + if (state == null) { + state = (String) requestArgs.get("state"); + if (state == null) { + throw new MissingParameter("state"); + } + } + + return state; + } + +} \ No newline at end of file diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/service/VerifiedIdToken.java b/lib/src/main/java/com/auth0/jwt/oiccli/service/VerifiedIdToken.java new file mode 100644 index 0000000..ebfda51 --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/service/VerifiedIdToken.java @@ -0,0 +1,10 @@ +package com.auth0.jwt.oiccli.service; + +public class VerifiedIdToken { + + private long nonce; + + public long getNonce() { + return nonce; + } +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/service/WebFinger.java b/lib/src/main/java/com/auth0/jwt/oiccli/service/WebFinger.java new file mode 100644 index 0000000..2e1d65c --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/service/WebFinger.java @@ -0,0 +1,74 @@ +package com.auth0.jwt.oiccli.service; + +import com.auth0.jwt.creators.Message; +import com.auth0.jwt.oiccli.Service; +import com.auth0.jwt.oiccli.Utils.ClientInfo; +import com.auth0.jwt.oiccli.exceptions.MissingRequiredAttribute; +import com.auth0.jwt.oiccli.exceptions.ValueError; +import com.auth0.jwt.oiccli.exceptions.WebFingerError; +import com.auth0.jwt.oiccli.responses.ErrorResponse; +import com.auth0.jwt.oiccli.webfinger.JRD; +import com.google.common.base.Strings; +import java.net.URISyntaxException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class WebFinger extends Service{ + + private Message messageType; + private JRD responseCls; + private ErrorResponse errorResponse; + private boolean isSynchronous; + private String request; + private String httpMethod; + private String responseBodyType; + private com.auth0.jwt.oiccli.webfinger.WebFinger webFinger; + private static final String OIC_ISSUER = "http://openid.net/specs/connect/1.0/issuer"; + + public WebFinger(String httpLib, KeyJar keyJar, String clientAuthenticationMethod, + Map conf) throws NoSuchFieldException, IllegalAccessException { + super(httpLib, keyJar, clientAuthenticationMethod, conf); + this.webFinger = new com.auth0.jwt.oiccli.webfinger.WebFinger(httpLib, OIC_ISSUER); + this.postParseResponse.add(this.wf_post_parse_response); + } + + public JRD wfPostParseResponse(JRD response, ClientInfo clientInfo) throws MissingRequiredAttribute, ValueError { + List> links = response.getLinks(); + if(links == null) { + throw new MissingRequiredAttribute("links is null"); + } else { + String href = null; + for(Map link : links) { + if(link.get("rel").equals(OIC_ISSUER)) { + href = link.get("href"); + } + if(Strings.isNullOrEmpty(this.getConfigurationAttribute("allowHttpLinks"))) { + if(!Strings.isNullOrEmpty(href) && href.startsWith("http://")) { + throw new ValueError("http link not allowed (" + href + ")"); + } + } + clientInfo.setIssuer(link.get("href")); + break; + } + } + + return response; + } + + public Map requestInfo(ClientInfo clientInfo, String method, Map requestArgs, boolean lax, + Map args) throws MissingRequiredAttribute, URISyntaxException, WebFingerError { + String resource = args.get("resource"); + if(Strings.isNullOrEmpty(resource)) { + resource = (String) clientInfo.getConfig().get("resource").get("resource"); + if(Strings.isNullOrEmpty(resource)) { + throw new MissingRequiredAttribute("resource is null or empty"); + } + } + + Map hMap = new HashMap<>(); + hMap.put("uri", this.webFinger.query(resource)); + + return hMap; + } +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/util/Util.java b/lib/src/main/java/com/auth0/jwt/oiccli/util/Util.java index 3a8e60b..668f446 100644 --- a/lib/src/main/java/com/auth0/jwt/oiccli/util/Util.java +++ b/lib/src/main/java/com/auth0/jwt/oiccli/util/Util.java @@ -167,7 +167,7 @@ public static long dateToTime(String date) throws ParseException { return d.getTime(); } - public static Map getOrPost(String uri, String method, AuthorizationRequest request, String contentType, String accept, Map args) throws UnsupportedEncodingException, UnsupportedType, JsonProcessingException, CloneNotSupportedException, URISyntaxException, NoSuchFieldException, IllegalAccessException { + public static Map getOrPost(String uri, String method, AuthorizationRequest request, String contentType, boolean accept, Map args) throws UnsupportedEncodingException, UnsupportedType, JsonProcessingException, CloneNotSupportedException, URISyntaxException, NoSuchFieldException, IllegalAccessException { Map response = new HashMap<>(); String urlEncoded; Field[] keys; @@ -202,7 +202,7 @@ public static Map getOrPost(String uri, String method, Authoriza Map headers = new HashMap<>(); headers.put("Content-Type", contentType); - if (accept != null) { + if (accept) { headers = new HashMap<>(); headers.put("Accept", accept); } diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/webfinger/JRD.java b/lib/src/main/java/com/auth0/jwt/oiccli/webfinger/JRD.java index ea771ba..6a1a2eb 100644 --- a/lib/src/main/java/com/auth0/jwt/oiccli/webfinger/JRD.java +++ b/lib/src/main/java/com/auth0/jwt/oiccli/webfinger/JRD.java @@ -1,8 +1,17 @@ package com.auth0.jwt.oiccli.webfinger; +import java.util.List; +import java.util.Map; + public class JRD { + private List> links; + public String toJSON() { return ""; } + + public List> getLinks() { + return links; + } } diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/webfinger/WebFinger.java b/lib/src/main/java/com/auth0/jwt/oiccli/webfinger/WebFinger.java index 179c3ce..b3eff89 100644 --- a/lib/src/main/java/com/auth0/jwt/oiccli/webfinger/WebFinger.java +++ b/lib/src/main/java/com/auth0/jwt/oiccli/webfinger/WebFinger.java @@ -67,9 +67,7 @@ public String query(String resource, List rel) throws URISyntaxException } String queryParams = ""; - for (int i = 0; - i < queryParamsTuple.size(); - i++) { + for (int i = 0; i < queryParamsTuple.size(); i++) { queryParams += queryParamsTuple.get(i).getA() + "=" + queryParamsTuple.get(i).getB(); if (i != queryParamsTuple.size() - 1) { queryParams += "&"; @@ -79,6 +77,10 @@ public String query(String resource, List rel) throws URISyntaxException return String.format(WF_URL, host) + "?" + URLEncoder.encode(queryParams); } + public String query(String resource) throws URISyntaxException, WebFingerError { + return query(resource, null); + } + public Map httpArgs(JRD jrd) throws JsonProcessingException { if (jrd == null) { if (this.jrd != null) { From 0bd3d7dcda59cc70a5e22406ee39700839e2efdf Mon Sep 17 00:00:00 2001 From: Justin Dahmubed Date: Tue, 6 Feb 2018 15:51:35 -0800 Subject: [PATCH 4/4] Service --- .../java/com/auth0/jwt/oiccli/Service.java | 165 +++++++++++++++++- .../jwt/oiccli/exceptions/OiccliError.java | 7 + .../java/com/auth0/jwt/oiccli/util/Util.java | 2 +- 3 files changed, 164 insertions(+), 10 deletions(-) create mode 100644 lib/src/main/java/com/auth0/jwt/oiccli/exceptions/OiccliError.java diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/Service.java b/lib/src/main/java/com/auth0/jwt/oiccli/Service.java index 2130eec..4985472 100644 --- a/lib/src/main/java/com/auth0/jwt/oiccli/Service.java +++ b/lib/src/main/java/com/auth0/jwt/oiccli/Service.java @@ -4,11 +4,13 @@ import com.auth0.jwt.oiccli.Utils.ClientInfo; import com.auth0.jwt.oiccli.exceptions.HTTPError; import com.auth0.jwt.oiccli.exceptions.MissingEndpoint; +import com.auth0.jwt.oiccli.exceptions.OiccliError; import com.auth0.jwt.oiccli.exceptions.UnsupportedType; import com.auth0.jwt.oiccli.exceptions.ValueError; import com.auth0.jwt.oiccli.exceptions.WrongContentType; import com.auth0.jwt.oiccli.responses.ErrorResponse; import com.auth0.jwt.oiccli.responses.Response; +import com.auth0.jwt.oiccli.tuples.Tuple; import com.auth0.jwt.oiccli.util.FakeResponse; import com.auth0.jwt.oiccli.util.Util; import com.google.common.base.Strings; @@ -28,7 +30,7 @@ public class Service { private final static org.slf4j.Logger logger = LoggerFactory.getLogger(Service.class); private static final List successfulCodes = Arrays.asList(200, 201, 202, 203, 204, 205, 206); - private static final List specialArgs = Arrays.asList("authenticationEndpoint", "algs"); + private static final List SPECIAL_ARGS = Arrays.asList("authenticationEndpoint", "algs"); public Message msgType; public Message responseCls; public ErrorResponse errorMessage; @@ -76,8 +78,29 @@ public Service(String httpLib, KeyJar keyJar, String clientAuthenticationMethod, this.postParseResponse = new ArrayList<>(); } - public void gatherRequestArgs() { - throw new UnsupportedOperationException(); + public Map gatherRequestArgs(ClientInfo clientInfo, Map args) throws NoSuchFieldException, IllegalAccessException { + + Map arArgs = new HashMap<>(args); + + String value; + String requestArgsValue; + for(String property : this.msgType.getCParam().keySet()) { + if(!arArgs.containsKey(property)) { + value = (String) clientInfo.getClass().getField(property).get(this); + if(!Strings.isNullOrEmpty(value)) { + arArgs.put(property, value); + } else { + requestArgsValue = this.conf.get("requestArgs").get(property); + if(!Strings.isNullOrEmpty(requestArgsValue)) { + arArgs.put(property, requestArgsValue); + } else { + arArgs.put(property, this.defaultRequestArgs.get(property)); + } + } + } + } + + return arArgs; } public void doPreConstruct() { @@ -179,8 +202,48 @@ public Map> doRequestInit(ClientInfo clientInfo, Stri return this.updateHttpArgs(httpArgs, info); } - private Map> requestInfo(ClientInfo clientInfo, String method, Map requestArgs, String bodyType, String authenticationMethod, boolean b, Map args) { - throw new UnsupportedOperationException(); + private Map> requestInfo(ClientInfo clientInfo, String method, Map requestArgs, String bodyType, String authenticationMethod, boolean lax, Map args) { + if(Strings.isNullOrEmpty(method)) { + method = this.httpMethod; + } + + if(requestArgs == null) { + requestArgs = new HashMap<>(); + } + + Map newArgs = new HashMap<>(); + for(String key : args.keySet()) { + if(!(SPECIAL_ARGS.contains(key) && SPECIAL_ARGS.contains(args.get(key)))) { + newArgs.put(key, args.get(key)); + } + } + + DummyMessage request = this.construct(clientInfo, requestArgs, newArgs); + + if(this.events != null && !this.events.isEmpty()) { + this.events.add("Protocol request", request); + } + + request.setLax(lax); + Map hArgs = new HashMap<>(); + + if(!Strings.isNullOrEmpty(authenticationMethod)) { + hArgs = this.initAuthenticationMethod(request, clientInfo, authenticationMethod, args); + } + + if(hArgs != null) { + if(hArgs.keySet().contains("headers")) { + args.get("headers").update(hArgs.get("headers")); + } else { + args.put("headers", hArgs.get("headers")); + } + } + + if(bodyType.equals("json")) { + args.put("contentType", Util.JSON_ENCODED); + } + + return this.uriAndBody(request, method, args); } public String getUrlInfo(String info) throws URISyntaxException { @@ -286,8 +349,17 @@ public String getConfigurationAttribute(String attribute) { return getConfigurationAttribute(attribute, null); } - public void buildServices() { - throw new UnsupportedOperationException(); + public Service buildServices(List services, Function serviceFactor, String httpLib, KeyJar keyJar, String clientAuthenticationMethod) throws NoSuchFieldException, IllegalAccessException { + Map hMap = new HashMap<>(); + Service service = null; + for(Tuple tuple : services) { + service = serviceFactory(tuple.getA(), httpLib, keyJar, clientAuthenticationMethod, tuple.getB()); + hMap.put(service.request, service); + } + + hMap.put("any", new Service(httpLib, keyJar, clientAuthenticationMethod, null)); + + return service; } public Response serviceRequest(String url, Map args) throws ParseException, ValueError, WrongContentType { @@ -302,8 +374,83 @@ public Response serviceRequest(String url, String method, ClientInfo clientInfo) return serviceRequest(url, "GET", null, "", null, null, null); } - private static FakeResponse parseResponse(Object text, ClientInfo clientInfo, String valueType, String state, Map args) { - throw new UnsupportedOperationException(); + private Response parseResponse(String info, ClientInfo clientInfo, String sFormat, String state, Map args) throws URISyntaxException, ValueError, OiccliError { + if(Strings.isNullOrEmpty(sFormat)) { + sFormat = this.responseBodyType; + } + + logger.debug("response format: " + sFormat); + + if(sFormat.equals("urlencoded")) { + info = this.getUrlInfo(info); + } + + if(this.events != null && !this.events.isEmpty()) { + this.events.add("Response", info); + } + + logger.debug("response cls: " + this.responseCls.toString()); + + Response response = this.responseCls.deserialize(info, sFormat, args); + + if(this.events != null && !this.events.isEmpty()) { + this.events.add("Protocol Response", response); + } + + List errorMessages = null; + if(response.getError() != null && !(response instanceof ErrorResponse)) { + response = null; + errorMessages = Arrays.asList(this.errorMessage); + + /* + if ErrorResponse not in errmsgs: + # Allow unspecified error response + errmsgs.append(ErrorResponse) + */ + + for(ErrorResponse errorResponse : errorMessages) { + response = errorResponse.deserialize(info, sFormat); + response.verify(); + break; + } + + if(response == null) { + logger.debug("Could not map into an error message"); + throw new ValueError("No error message: " + info); + } + + logger.debug("Error response: " + response); + } else { + args.put("clientId", clientInfo.getClientId()); + args.put("issuer", clientInfo.getIssuer()); + + if(!args.containsKey("key") && !args.containsKey("keyjar")) { + args.put("keyjar", this.keyJar); + } + + args.update(this.conf.get("verify")); + + logger.debug("Verify response with " + args); + + boolean shouldVerify = response.verify(args); + + if(!shouldVerify) { + logger.error("Verification of the response failed"); + throw new OiccliError("Verification of the response failed"); + } + + if(response != null && response.getType().equals("AuthorizationResponse") && response.getScope() == null) { + response.setScope(args.get("scope")); + } else { + throw new ResponseError("Missing or faulty response"); + } + + if(!(response instanceof ErrorResponse)) { + this.doPostParseResponse(response, clientInfo, state); + } + } + + return response; } } diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/OiccliError.java b/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/OiccliError.java new file mode 100644 index 0000000..4ce35a9 --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/oiccli/exceptions/OiccliError.java @@ -0,0 +1,7 @@ +package com.auth0.jwt.oiccli.exceptions; + +public class OiccliError extends Exception { + public OiccliError(String message) { + super(message); + } +} diff --git a/lib/src/main/java/com/auth0/jwt/oiccli/util/Util.java b/lib/src/main/java/com/auth0/jwt/oiccli/util/Util.java index 668f446..4e6faeb 100644 --- a/lib/src/main/java/com/auth0/jwt/oiccli/util/Util.java +++ b/lib/src/main/java/com/auth0/jwt/oiccli/util/Util.java @@ -27,7 +27,7 @@ public class Util { private static final String URL_ENCODED = "application/x-www-form-urlencoded"; - private static final String JSON_ENCODED = "application/json"; + public static final String JSON_ENCODED = "application/json"; private static final String JRD_JSON = "application/jrd+json"; private static final String JWT_ENCODED = "application/jwt"; private static final String PLAIN_TEXT = "text/plain";