From f78b279d796f7d046630538758225e5a93b1dd22 Mon Sep 17 00:00:00 2001 From: Richard Blaylock Date: Tue, 10 Jan 2017 18:13:17 -0800 Subject: [PATCH 01/16] 1211 Support stormpath_factor_challenge grant type; expose more info from OAuthExceptions. --- .../stormpath/sdk/oauth/Authenticators.java | 9 +++ ...orChallengeGrantRequestAuthentication.java | 14 ++++ ...torChallengeGrantRequestAuthenticator.java | 9 +++ ...lengeGrantRequestAuthenticatorFactory.java | 7 ++ .../servlet/filter/oauth/OAuthException.java | 47 ++++++++++---- .../servlet/mvc/AccessTokenController.java | 65 ++++++++++++++++++- .../impl/application/DefaultApplication.java | 7 ++ ...orChallengeGrantAuthenticationAttempt.java | 50 ++++++++++++++ ...orChallengeGrantRequestAuthentication.java | 38 +++++++++++ ...torChallengeGrantRequestAuthenticator.java | 44 +++++++++++++ ...rChallengeRequestAuthenticatorFactory.java | 16 +++++ ...orChallengeGrantAuthenticationAttempt.java | 20 ++++++ 12 files changed, 311 insertions(+), 15 deletions(-) create mode 100644 api/src/main/java/com/stormpath/sdk/oauth/OAuthStormpathFactorChallengeGrantRequestAuthentication.java create mode 100644 api/src/main/java/com/stormpath/sdk/oauth/OAuthStormpathFactorChallengeGrantRequestAuthenticator.java create mode 100644 api/src/main/java/com/stormpath/sdk/oauth/OAuthStormpathFactorChallengeGrantRequestAuthenticatorFactory.java create mode 100644 impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeGrantAuthenticationAttempt.java create mode 100644 impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeGrantRequestAuthentication.java create mode 100644 impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeGrantRequestAuthenticator.java create mode 100644 impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeRequestAuthenticatorFactory.java create mode 100644 impl/src/main/java/com/stormpath/sdk/impl/oauth/OAuthStormpathFactorChallengeGrantAuthenticationAttempt.java diff --git a/api/src/main/java/com/stormpath/sdk/oauth/Authenticators.java b/api/src/main/java/com/stormpath/sdk/oauth/Authenticators.java index 7e4f875ff4..69d5cf447a 100644 --- a/api/src/main/java/com/stormpath/sdk/oauth/Authenticators.java +++ b/api/src/main/java/com/stormpath/sdk/oauth/Authenticators.java @@ -128,5 +128,14 @@ private Authenticators() { */ public static final OAuthStormpathSocialRequestAuthenticatorFactory OAUTH_STORMPATH_SOCIAL_GRANT_REQUEST_AUTHENTICATOR = (OAuthStormpathSocialRequestAuthenticatorFactory) Classes.newInstance("com.stormpath.sdk.impl.oauth.DefaultOAuthStormpathSocialRequestAuthenticatorFactory"); + + /** + * Constructs {@link OAuthStormpathFactorChallengeGrantRequestAuthenticator}s. + * + * @since 1.1.0 + */ + public static final OAuthStormpathFactorChallengeGrantRequestAuthenticatorFactory OAUTH_STORMPATH_FACTOR_CHALLENGE_GRANT_REQUEST_AUTHENTICATOR = + (OAuthStormpathFactorChallengeGrantRequestAuthenticatorFactory) Classes.newInstance("com.stormpath.sdk.impl.oauth.DefaultOAuthStormpathFactorChallengeRequestAuthenticatorFactory"); + } diff --git a/api/src/main/java/com/stormpath/sdk/oauth/OAuthStormpathFactorChallengeGrantRequestAuthentication.java b/api/src/main/java/com/stormpath/sdk/oauth/OAuthStormpathFactorChallengeGrantRequestAuthentication.java new file mode 100644 index 0000000000..98210d251d --- /dev/null +++ b/api/src/main/java/com/stormpath/sdk/oauth/OAuthStormpathFactorChallengeGrantRequestAuthentication.java @@ -0,0 +1,14 @@ +package com.stormpath.sdk.oauth; + +/** + * This class represents a request to exchange a multifactor authentication code for a valid OAuth 2.0 access token. + * Using stormpath_factor_challenge grant type + * + * @since 1.3.1 + */ +public interface OAuthStormpathFactorChallengeGrantRequestAuthentication extends OAuthGrantRequestAuthentication { + + String getChallenge(); + + String getCode(); +} diff --git a/api/src/main/java/com/stormpath/sdk/oauth/OAuthStormpathFactorChallengeGrantRequestAuthenticator.java b/api/src/main/java/com/stormpath/sdk/oauth/OAuthStormpathFactorChallengeGrantRequestAuthenticator.java new file mode 100644 index 0000000000..bc3d28c593 --- /dev/null +++ b/api/src/main/java/com/stormpath/sdk/oauth/OAuthStormpathFactorChallengeGrantRequestAuthenticator.java @@ -0,0 +1,9 @@ +package com.stormpath.sdk.oauth; + + +/** + * @since 1.3.1 + */ +public interface OAuthStormpathFactorChallengeGrantRequestAuthenticator extends OAuthRequestAuthenticator { + +} diff --git a/api/src/main/java/com/stormpath/sdk/oauth/OAuthStormpathFactorChallengeGrantRequestAuthenticatorFactory.java b/api/src/main/java/com/stormpath/sdk/oauth/OAuthStormpathFactorChallengeGrantRequestAuthenticatorFactory.java new file mode 100644 index 0000000000..6182ab5be8 --- /dev/null +++ b/api/src/main/java/com/stormpath/sdk/oauth/OAuthStormpathFactorChallengeGrantRequestAuthenticatorFactory.java @@ -0,0 +1,7 @@ +package com.stormpath.sdk.oauth; + +/** + * @since 1.3.1 + */ +public interface OAuthStormpathFactorChallengeGrantRequestAuthenticatorFactory extends OAuthRequestAuthenticatorFactory { +} diff --git a/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/filter/oauth/OAuthException.java b/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/filter/oauth/OAuthException.java index 38864c5be4..02a3c18ef9 100644 --- a/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/filter/oauth/OAuthException.java +++ b/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/filter/oauth/OAuthException.java @@ -15,56 +15,75 @@ */ package com.stormpath.sdk.servlet.filter.oauth; +import com.fasterxml.jackson.databind.ObjectMapper; import com.stormpath.sdk.lang.Assert; import com.stormpath.sdk.lang.Strings; +import java.util.LinkedHashMap; +import java.util.Map; + /** * @since 1.0.RC3 */ public class OAuthException extends RuntimeException { + private static final ObjectMapper objectMapper; + + static { + objectMapper = new ObjectMapper(); + } + private final OAuthErrorCode errorCode; + private Map errorMap; + public OAuthException(OAuthErrorCode code) { - this(code, null, null); + this(code, null, (Exception) null); } public OAuthException(OAuthErrorCode code, String message) { super(message != null ? message : (code != null ? code.getValue() : "")); Assert.notNull(code, "OAuthErrorCode cannot be null."); this.errorCode = code; + + initializeErrorMap(); } public OAuthException(OAuthErrorCode code, String message, Exception cause) { super(message != null ? message : (code != null ? code.getValue() : ""), cause); Assert.notNull(code, "OAuthErrorCode cannot be null."); this.errorCode = code; - } - public OAuthErrorCode getErrorCode() { - return errorCode; + initializeErrorMap(); } - public String toJson() { + private void initializeErrorMap() { + errorMap = new LinkedHashMap<>(); - String json = "{" + toJson("error", getErrorCode()); + errorMap.put("error", errorCode.getValue()); String val = getMessage(); if (Strings.hasText(val)) { - json += "," + toJson("message", val); + errorMap.put("message", val); } + } - json += "}"; + public OAuthException(OAuthErrorCode code, Map error, String message) { + this(code, message, null); - return json; + errorMap.putAll(error); } - protected static String toJson(String name, Object value) { - String stringValue = String.valueOf(value); - return quote(name) + ":" + quote(stringValue); + public OAuthErrorCode getErrorCode() { + return errorCode; } - protected static String quote(String val) { - return "\"" + val + "\""; + public String toJson() { + try { + return objectMapper.writeValueAsString(errorMap); + } catch (Exception e) { + throw new IllegalStateException("Unable to serialize OAuthException to json.", e); + } } + } diff --git a/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/mvc/AccessTokenController.java b/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/mvc/AccessTokenController.java index ce08b2c35b..06ba3c2b92 100644 --- a/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/mvc/AccessTokenController.java +++ b/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/mvc/AccessTokenController.java @@ -23,6 +23,7 @@ import com.stormpath.sdk.impl.authc.DefaultHttpServletRequestWrapper; import com.stormpath.sdk.impl.error.DefaultError; import com.stormpath.sdk.impl.oauth.DefaultIdSiteAuthenticationRequest; +import com.stormpath.sdk.impl.oauth.DefaultOAuthStormpathFactorChallengeGrantRequestAuthentication; import com.stormpath.sdk.impl.oauth.DefaultOAuthStormpathSocialGrantRequestAuthentication; import com.stormpath.sdk.lang.Assert; import com.stormpath.sdk.oauth.AccessTokenResult; @@ -33,6 +34,7 @@ import com.stormpath.sdk.oauth.OAuthPasswordGrantRequestAuthentication; import com.stormpath.sdk.oauth.OAuthRefreshTokenRequestAuthentication; import com.stormpath.sdk.oauth.OAuthRequests; +import com.stormpath.sdk.oauth.OAuthStormpathFactorChallengeGrantRequestAuthentication; import com.stormpath.sdk.oauth.OAuthStormpathSocialGrantRequestAuthentication; import com.stormpath.sdk.resource.ResourceException; import com.stormpath.sdk.servlet.authc.FailedAuthenticationRequestEvent; @@ -58,6 +60,8 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import java.util.LinkedHashMap; +import java.util.Map; /** * @since 1.0.RC4 @@ -71,6 +75,7 @@ public class AccessTokenController extends AbstractController { private static final String STORMPATH_SOCIAL_GRANT_TYPE = "stormpath_social"; private static final String STORMPATH_TOKEN_GRANT_TYPE = "stormpath_token"; private static final String REFRESH_TOKEN_GRANT_TYPE = "refresh_token"; + private static final String STORMPATH_FACTOR_CHALLENGE_GRANT_TYPE = "stormpath_factor_challenge"; private static final String GRANT_TYPE_PARAM_NAME = "grant_type"; private RefreshTokenResultFactory refreshTokenResultFactory; @@ -297,14 +302,64 @@ protected AccessTokenResult stormpathSocialAuthenticationRequest(HttpServletRequ return createAccessTokenResult(request, response, authenticationResult); } + /** + * @since 1.3.1 + */ + protected AccessTokenResult stormpathFactorChallengeAuthenticationRequest(HttpServletRequest request, HttpServletResponse response) { + OAuthGrantRequestAuthenticationResult authenticationResult; + + try { + Application app = getApplication(request); + String challenge = request.getParameter("challenge"); + String code = request.getParameter("code"); + OAuthStormpathFactorChallengeGrantRequestAuthentication grantRequestAuthentication = + new DefaultOAuthStormpathFactorChallengeGrantRequestAuthentication(challenge, code); + + authenticationResult = Authenticators.OAUTH_STORMPATH_FACTOR_CHALLENGE_GRANT_REQUEST_AUTHENTICATOR + .forApplication(app) + .authenticate(grantRequestAuthentication); + } catch (ResourceException e) { + log.debug("Unable to authenticate stormpath social grant request: {}", e.getMessage(), e); + throw convertToOAuthException(e, OAuthErrorCode.INVALID_CLIENT); + } catch (IllegalArgumentException ex) { + throw new OAuthException(OAuthErrorCode.INVALID_REQUEST); + } + + return createAccessTokenResult(request, response, authenticationResult); + } + private OAuthException convertToOAuthException(ResourceException e, OAuthErrorCode defaultErrorCode) { com.stormpath.sdk.error.Error error = e.getStormpathError(); String message = error.getMessage(); OAuthErrorCode oauthError = defaultErrorCode; if (error instanceof DefaultError) { - Object errorObject = ((DefaultError) error).getProperty("error"); + DefaultError defaultError = ((DefaultError) error); + + Object errorObject = defaultError.getProperty("error"); oauthError = errorObject == null ? oauthError : new OAuthErrorCode(errorObject.toString()); + + Object action = defaultError.getProperty("action"); + if (action instanceof String) { + // get action map from error based on the action + Map errorMap = new LinkedHashMap<>(); + errorMap.put("error_description", defaultError.getProperty("error_description")); + errorMap.put("action", action); + if ("factor_enroll".equals(action)) { + errorMap.put("state", defaultError.getProperty("state")); + errorMap.put("allowedFactorTypes", defaultError.getProperty("allowedFactorTypes")); + } + else if ("factor_challenge".equals(action)) { + errorMap.put("state", defaultError.getProperty("state")); + errorMap.put("allowedFactorTypes", defaultError.getProperty("allowedFactorTypes")); + errorMap.put("factor", defaultError.getProperty("factor")); + errorMap.put("challenge", defaultError.getProperty("challenge")); + } + else if ("factor_select".equals(action)) { + errorMap.put("factors", defaultError.getProperty("factors")); + } + return new OAuthException(oauthError, errorMap, ""); + } } return new OAuthException(oauthError, message); @@ -439,6 +494,14 @@ protected AccessTokenResult getAccessTokenResult(String grantType, HttpServletRe throw new OAuthException(OAuthErrorCode.INVALID_CLIENT); } break; + case STORMPATH_FACTOR_CHALLENGE_GRANT_TYPE: + try { + result = this.stormpathFactorChallengeAuthenticationRequest(request, response); + } catch (HttpAuthenticationException e) { + log.warn("Unable to authenticate client", e); + throw new OAuthException(OAuthErrorCode.INVALID_CLIENT); + } + break; default: throw new OAuthException(OAuthErrorCode.UNSUPPORTED_GRANT_TYPE, "'" + grantType + "' is an unsupported grant type."); } diff --git a/impl/src/main/java/com/stormpath/sdk/impl/application/DefaultApplication.java b/impl/src/main/java/com/stormpath/sdk/impl/application/DefaultApplication.java index 4bf11a282a..6286f87758 100644 --- a/impl/src/main/java/com/stormpath/sdk/impl/application/DefaultApplication.java +++ b/impl/src/main/java/com/stormpath/sdk/impl/application/DefaultApplication.java @@ -62,6 +62,7 @@ import com.stormpath.sdk.impl.oauth.DefaultOAuthClientCredentialsGrantRequestAuthenticator; import com.stormpath.sdk.impl.oauth.DefaultOAuthPasswordGrantRequestAuthenticator; import com.stormpath.sdk.impl.oauth.DefaultOAuthRefreshTokenRequestAuthenticator; +import com.stormpath.sdk.impl.oauth.DefaultOAuthStormpathFactorChallengeGrantRequestAuthenticator; import com.stormpath.sdk.impl.oauth.DefaultOAuthStormpathSocialGrantRequestAuthenticator; import com.stormpath.sdk.impl.oauth.DefaultOAuthTokenRevocator; import com.stormpath.sdk.impl.provider.ProviderAccountResolver; @@ -86,6 +87,7 @@ import com.stormpath.sdk.oauth.OAuthPasswordGrantRequestAuthenticator; import com.stormpath.sdk.oauth.OAuthPolicy; import com.stormpath.sdk.oauth.OAuthRefreshTokenRequestAuthenticator; +import com.stormpath.sdk.oauth.OAuthStormpathFactorChallengeGrantRequestAuthenticator; import com.stormpath.sdk.oauth.OAuthStormpathSocialGrantRequestAuthenticator; import com.stormpath.sdk.oauth.OAuthTokenRevocator; import com.stormpath.sdk.organization.Organization; @@ -881,6 +883,11 @@ public OAuthStormpathSocialGrantRequestAuthenticator createStormpathSocialGrantA return new DefaultOAuthStormpathSocialGrantRequestAuthenticator(this, getDataStore()); } + /* @since 1.3.1 */ + public OAuthStormpathFactorChallengeGrantRequestAuthenticator createStormpathFactorChallengeGrantAuthenticator() { + return new DefaultOAuthStormpathFactorChallengeGrantRequestAuthenticator(this, getDataStore()); + } + /* @since 1.0.RC7 */ public OAuthPasswordGrantRequestAuthenticator createPasswordGrantAuthenticator() { return new DefaultOAuthPasswordGrantRequestAuthenticator(this, getDataStore()); diff --git a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeGrantAuthenticationAttempt.java b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeGrantAuthenticationAttempt.java new file mode 100644 index 0000000000..e910c90187 --- /dev/null +++ b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeGrantAuthenticationAttempt.java @@ -0,0 +1,50 @@ +package com.stormpath.sdk.impl.oauth; + +import com.stormpath.sdk.impl.ds.InternalDataStore; +import com.stormpath.sdk.impl.resource.AbstractResource; +import com.stormpath.sdk.impl.resource.Property; +import com.stormpath.sdk.impl.resource.StringProperty; + +import java.util.Map; + +/** + * @since 1.3.1 + */ +public class DefaultOAuthStormpathFactorChallengeGrantAuthenticationAttempt extends AbstractResource implements OAuthStormpathFactorChallengeGrantAuthenticationAttempt { + + static final StringProperty CHALLENGE = new StringProperty("challenge"); + static final StringProperty CODE = new StringProperty("code"); + + private static final Map PROPERTY_DESCRIPTORS = createPropertyDescriptorMap(CHALLENGE, CODE); + + public DefaultOAuthStormpathFactorChallengeGrantAuthenticationAttempt(InternalDataStore dataStore) { + super(dataStore); + } + + public DefaultOAuthStormpathFactorChallengeGrantAuthenticationAttempt(InternalDataStore dataStore, Map properties) { + super(dataStore, properties); + } + + @Override + public void setChallenge(String challenge) { + setProperty(CHALLENGE, challenge); + } + + @Override + public void setCode(String code) { + setProperty(CODE, code); + } + + public String getChallenge() { + return getString(CHALLENGE); + } + + public String getCode() { + return getString(CODE); + } + + @Override + public Map getPropertyDescriptors() { + return PROPERTY_DESCRIPTORS; + } +} diff --git a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeGrantRequestAuthentication.java b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeGrantRequestAuthentication.java new file mode 100644 index 0000000000..e283a240e7 --- /dev/null +++ b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeGrantRequestAuthentication.java @@ -0,0 +1,38 @@ +package com.stormpath.sdk.impl.oauth; + +import com.stormpath.sdk.lang.Assert; +import com.stormpath.sdk.oauth.OAuthStormpathFactorChallengeGrantRequestAuthentication; +import com.stormpath.sdk.oauth.OAuthStormpathSocialGrantRequestAuthentication; + +/** + * @since 1.3.1 + */ +public class DefaultOAuthStormpathFactorChallengeGrantRequestAuthentication implements OAuthStormpathFactorChallengeGrantRequestAuthentication { + private final static String grant_type = "stormpath_factor_challenge"; + + private String challenge; + private String code; + + public DefaultOAuthStormpathFactorChallengeGrantRequestAuthentication(String challenge, String code) { + Assert.hasText(challenge, "challenge cannot be null or empty."); + Assert.hasText(code, "code cannot be null or empty."); + + this.challenge = challenge; + this.code = code; + } + + @Override + public String getChallenge() { + return challenge; + } + + @Override + public String getCode() { + return code; + } + + @Override + public String getGrantType() { + return grant_type; + } +} diff --git a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeGrantRequestAuthenticator.java b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeGrantRequestAuthenticator.java new file mode 100644 index 0000000000..9f4ab69d12 --- /dev/null +++ b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeGrantRequestAuthenticator.java @@ -0,0 +1,44 @@ +package com.stormpath.sdk.impl.oauth; + +import com.stormpath.sdk.application.Application; +import com.stormpath.sdk.ds.DataStore; +import com.stormpath.sdk.impl.http.HttpHeaders; +import com.stormpath.sdk.impl.http.MediaType; +import com.stormpath.sdk.lang.Assert; +import com.stormpath.sdk.oauth.GrantAuthenticationToken; +import com.stormpath.sdk.oauth.OAuthGrantRequestAuthenticationResult; +import com.stormpath.sdk.oauth.OAuthRequestAuthentication; +import com.stormpath.sdk.oauth.OAuthStormpathFactorChallengeGrantRequestAuthentication; +import com.stormpath.sdk.oauth.OAuthStormpathFactorChallengeGrantRequestAuthenticator; + +/** + * @since 1.3.1 + */ +public class DefaultOAuthStormpathFactorChallengeGrantRequestAuthenticator extends AbstractOAuthRequestAuthenticator implements OAuthStormpathFactorChallengeGrantRequestAuthenticator { + + private final static String OAUTH_TOKEN_PATH = "/oauth/token"; + + public DefaultOAuthStormpathFactorChallengeGrantRequestAuthenticator(Application application, DataStore dataStore) { + super(application, dataStore); + } + + @Override + public OAuthGrantRequestAuthenticationResult authenticate(OAuthRequestAuthentication authenticationRequest) { + Assert.notNull(this.application, "application cannot be null or empty"); + Assert.isInstanceOf(OAuthStormpathFactorChallengeGrantRequestAuthentication.class, authenticationRequest, "authenticationRequest must be an instance of OAuthStormpathFactorChallengeGrantRequestAuthentication."); + OAuthStormpathFactorChallengeGrantRequestAuthentication authentication = (OAuthStormpathFactorChallengeGrantRequestAuthentication) authenticationRequest; + + OAuthStormpathFactorChallengeGrantAuthenticationAttempt authenticationAttempt = new DefaultOAuthStormpathFactorChallengeGrantAuthenticationAttempt(dataStore); + authenticationAttempt.setChallenge(authentication.getChallenge()); + authenticationAttempt.setCode(authentication.getCode()); + + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + + GrantAuthenticationToken grantResult = dataStore.create(application.getHref() + OAUTH_TOKEN_PATH, authenticationAttempt, GrantAuthenticationToken.class, httpHeaders); + + OAuthGrantRequestAuthenticationResultBuilder builder = new DefaultOAuthGrantRequestAuthenticationResultBuilder(grantResult); + + return builder.build(); + } +} diff --git a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeRequestAuthenticatorFactory.java b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeRequestAuthenticatorFactory.java new file mode 100644 index 0000000000..67f635b5b3 --- /dev/null +++ b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeRequestAuthenticatorFactory.java @@ -0,0 +1,16 @@ +package com.stormpath.sdk.impl.oauth; + +import com.stormpath.sdk.application.Application; +import com.stormpath.sdk.impl.application.DefaultApplication; +import com.stormpath.sdk.oauth.OAuthStormpathFactorChallengeGrantRequestAuthenticator; +import com.stormpath.sdk.oauth.OAuthStormpathFactorChallengeGrantRequestAuthenticatorFactory; + +/** + * @since 1.3.1 + */ +public class DefaultOAuthStormpathFactorChallengeRequestAuthenticatorFactory implements OAuthStormpathFactorChallengeGrantRequestAuthenticatorFactory { + @Override + public OAuthStormpathFactorChallengeGrantRequestAuthenticator forApplication(Application application) { + return ((DefaultApplication) application).createStormpathFactorChallengeGrantAuthenticator(); + } +} diff --git a/impl/src/main/java/com/stormpath/sdk/impl/oauth/OAuthStormpathFactorChallengeGrantAuthenticationAttempt.java b/impl/src/main/java/com/stormpath/sdk/impl/oauth/OAuthStormpathFactorChallengeGrantAuthenticationAttempt.java new file mode 100644 index 0000000000..6e2839c59f --- /dev/null +++ b/impl/src/main/java/com/stormpath/sdk/impl/oauth/OAuthStormpathFactorChallengeGrantAuthenticationAttempt.java @@ -0,0 +1,20 @@ +package com.stormpath.sdk.impl.oauth; + +import com.stormpath.sdk.resource.Resource; + +/** + * @since 1.3.1 + */ +public interface OAuthStormpathFactorChallengeGrantAuthenticationAttempt extends Resource { + /** + * Method used to set the href of the challenge to be verified. + * @param challenge the href of the challenge to be verified. + */ + void setChallenge(String challenge); + + /** + * Method used to set the code for the multifactor challenge. + * @param code the code for the multifactor challenge. + */ + void setCode(String code); +} From 0c08883cfb0bc126e6cffa6e4b231c239de9ea89 Mon Sep 17 00:00:00 2001 From: Richard Blaylock Date: Fri, 13 Jan 2017 13:33:58 -0800 Subject: [PATCH 02/16] 1211 Add tests for stormpath_factor_challenge grant type, and good thing, too. --- .../sdk/impl/application/ApplicationIT.groovy | 151 ++++++++++++++++++ ...orChallengeGrantAuthenticationAttempt.java | 12 +- ...torChallengeGrantRequestAuthenticator.java | 1 + ...orChallengeGrantAuthenticationAttempt.java | 6 + 4 files changed, 169 insertions(+), 1 deletion(-) diff --git a/extensions/httpclient/src/test/groovy/com/stormpath/sdk/impl/application/ApplicationIT.groovy b/extensions/httpclient/src/test/groovy/com/stormpath/sdk/impl/application/ApplicationIT.groovy index 3a08a232b3..e3a46f6b37 100644 --- a/extensions/httpclient/src/test/groovy/com/stormpath/sdk/impl/application/ApplicationIT.groovy +++ b/extensions/httpclient/src/test/groovy/com/stormpath/sdk/impl/application/ApplicationIT.groovy @@ -32,12 +32,18 @@ import com.stormpath.sdk.application.ApplicationAccountStoreMapping import com.stormpath.sdk.application.ApplicationAccountStoreMappingList import com.stormpath.sdk.application.Applications import com.stormpath.sdk.authc.UsernamePasswordRequests +import com.stormpath.sdk.challenge.google.GoogleAuthenticatorChallenge +import com.stormpath.sdk.challenge.sms.SmsChallenge import com.stormpath.sdk.client.AuthenticationScheme import com.stormpath.sdk.client.Client import com.stormpath.sdk.client.ClientIT import com.stormpath.sdk.directory.AccountStore import com.stormpath.sdk.directory.Directories import com.stormpath.sdk.directory.Directory +import com.stormpath.sdk.factor.FactorOptions +import com.stormpath.sdk.factor.Factors +import com.stormpath.sdk.factor.google.GoogleAuthenticatorFactor +import com.stormpath.sdk.factor.sms.SmsFactor import com.stormpath.sdk.group.Group import com.stormpath.sdk.group.Groups import com.stormpath.sdk.http.HttpMethod @@ -47,6 +53,7 @@ import com.stormpath.sdk.impl.ds.DefaultDataStore import com.stormpath.sdk.impl.error.DefaultError import com.stormpath.sdk.impl.http.authc.SAuthc1RequestAuthenticator import com.stormpath.sdk.impl.idsite.IdSiteClaims +import com.stormpath.sdk.impl.oauth.DefaultOAuthStormpathFactorChallengeGrantRequestAuthentication import com.stormpath.sdk.impl.resource.AbstractResource import com.stormpath.sdk.impl.saml.SamlResultStatus import com.stormpath.sdk.impl.security.ApiKeySecretEncryptionService @@ -62,6 +69,7 @@ import com.stormpath.sdk.oauth.OAuthPolicy import com.stormpath.sdk.oauth.OAuthRefreshTokenRequestAuthentication import com.stormpath.sdk.oauth.OAuthRequestAuthenticator import com.stormpath.sdk.oauth.OAuthRequests +import com.stormpath.sdk.oauth.OAuthStormpathFactorChallengeGrantRequestAuthentication import com.stormpath.sdk.oauth.OAuthTokenRevocator import com.stormpath.sdk.oauth.OAuthTokenRevocators import com.stormpath.sdk.oauth.RefreshToken @@ -82,11 +90,17 @@ import io.jsonwebtoken.Jws import io.jsonwebtoken.JwsHeader import io.jsonwebtoken.Jwts import io.jsonwebtoken.SignatureAlgorithm +import org.apache.commons.codec.binary.Base32 import org.apache.commons.codec.binary.Base64 import org.testng.annotations.Test +import javax.crypto.Mac +import javax.crypto.spec.SecretKeySpec import javax.servlet.http.HttpServletRequest import java.lang.reflect.Field +import java.security.InvalidKeyException +import java.security.NoSuchAlgorithmException +import java.util.concurrent.TimeUnit import static com.stormpath.sdk.application.Applications.newCreateRequestFor import static org.easymock.EasyMock.createMock @@ -1860,6 +1874,142 @@ class ApplicationIT extends ClientIT { assertEquals result.getExpiresIn(), 3600 } + /* @since 1.3.1 */ + @Test + void testCreateStormpathFactorChallengeTokenForAppAccountWithBadCode() { + def app = createTempApp() + + def account = createTestAccount(app) + + GoogleAuthenticatorFactor factor = createGoogleAuthenticatorFactor(account) + + def challenge = client.instantiate(GoogleAuthenticatorChallenge) + challenge = factor.createChallenge(challenge) + + String bogusCode = "000000" + OAuthStormpathFactorChallengeGrantRequestAuthentication request = new DefaultOAuthStormpathFactorChallengeGrantRequestAuthentication(challenge.href, bogusCode) + + try { + Authenticators.OAUTH_STORMPATH_FACTOR_CHALLENGE_GRANT_REQUEST_AUTHENTICATOR.forApplication(app).authenticate(request) + fail() + } + catch (ResourceException re) { + assertEquals(re.getStatus(), 400) + assertEquals(re.getCode(), 13104) + assertEquals("The code submitted is not valid.", re.getDeveloperMessage()) + } + } + + /* @since 1.3.1 */ + @Test + void testCreateStormpathFactorChallengeTokenForAppAccountWithValidCode() { + def app = createTempApp() + + def account = createTestAccount(app) + + GoogleAuthenticatorFactor factor = createGoogleAuthenticatorFactor(account) + + def challenge = client.instantiate(GoogleAuthenticatorChallenge) + challenge = factor.createChallenge(challenge) + + String padded = getValidCode(factor) + + OAuthStormpathFactorChallengeGrantRequestAuthentication request = new DefaultOAuthStormpathFactorChallengeGrantRequestAuthentication(challenge.href, padded) + + def result = Authenticators.OAUTH_STORMPATH_FACTOR_CHALLENGE_GRANT_REQUEST_AUTHENTICATOR.forApplication(app).authenticate(request) + assertNotNull result.getAccessTokenHref() + assertEquals result.getAccessToken().getHref(), result.getAccessTokenHref() + assertEquals(result.getAccessToken().getAccount().getHref(), account.getHref()) + assertEquals(result.getAccessToken().getApplication().getHref(), app.getHref()) + assertTrue Strings.hasText(result.getAccessTokenString()) + + assertNotNull result.getRefreshToken().getHref() + assertEquals(result.getRefreshToken().getAccount().getHref(), account.getHref()) + assertEquals(result.getRefreshToken().getApplication().getHref(), app.getHref()) + + assertEquals result.getTokenType(), "Bearer" + assertEquals result.getExpiresIn(), 3600 + } + + private String getValidCode(GoogleAuthenticatorFactor factor) { + byte[] secret = new Base32().decode(factor.getSecret()) + + final long timeWindow = System.currentTimeMillis() / TimeUnit.SECONDS.toMillis(30) + + int validCode = calculateCode(secret, timeWindow) + String padded = String.format("%06d", validCode) + return padded + } + + private GoogleAuthenticatorFactor createGoogleAuthenticatorFactor(Account account) { + GoogleAuthenticatorFactor factor = client.instantiate(GoogleAuthenticatorFactor) + factor = factor.setAccountName("accountName").setIssuer("issuer") + + def builder = Factors.GOOGLE_AUTHENTICATOR.newCreateRequestFor(factor).createChallenge() + factor = account.createFactor(builder.build()) + + FactorOptions factorOptions = Factors.options().withMostRecentChallenge() + factor = client.getResource(factor.href, GoogleAuthenticatorFactor.class, factorOptions) + return factor + } + + private static final String HMAC_HASH_FUNCTION = "HmacSHA1"; + private static final int KEY_MODULUS = (int) Math.pow(10, CODE_DIGITS); + private static final int CODE_DIGITS = 6; + + private static int calculateCode(byte[] key, long tm) { + // Allocating an array of bytes to represent the specified instant + // of time. + byte[] data = new byte[8]; + long value = tm; + + // Converting the instant of time from the long representation to a + // big-endian array of bytes (RFC4226, 5.2. Description). + for (int i = 8; i-- > 0; value >>>= 8) { + data[i] = (byte) value; + } + + // Building the secret key specification for the HmacSHA1 algorithm. + SecretKeySpec signKey = new SecretKeySpec(key, HMAC_HASH_FUNCTION); + + try { + // Getting an HmacSHA1 algorithm implementation from the JCE. + Mac mac = Mac.getInstance(HMAC_HASH_FUNCTION); + + // Initializing the MAC algorithm. + mac.init(signKey); + + // Processing the instant of time and getting the encrypted data. + byte[] hash = mac.doFinal(data); + + // Building the validation code performing dynamic truncation + // (RFC4226, 5.3. Generating an HOTP value) + int offset = hash[hash.length - 1] & 0xF; + + // We are using a long because Java hasn't got an unsigned integer type + // and we need 32 unsigned bits). + long truncatedHash = 0; + + for (int i = 0; i < 4; ++i) { + truncatedHash <<= 8; + + // Java bytes are signed but we need an unsigned integer: + // cleaning off all but the LSB. + truncatedHash |= (hash[offset + i] & 0xFF); + } + + // Clean bits higher than the 32nd (inclusive) and calculate the + // module with the maximum validation code value. + truncatedHash &= 0x7FFFFFFF; + truncatedHash %= KEY_MODULUS; + + // Returning the validation code to the caller. + return (int) truncatedHash; + } catch (NoSuchAlgorithmException | InvalidKeyException ex) { + throw new IllegalStateException(ex); + } + } + /* @since 1.0.RC7 */ @Test @@ -2438,4 +2588,5 @@ class ApplicationIT extends ClientIT { assertFalse result.newAccount } + } diff --git a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeGrantAuthenticationAttempt.java b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeGrantAuthenticationAttempt.java index e910c90187..9becfac045 100644 --- a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeGrantAuthenticationAttempt.java +++ b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeGrantAuthenticationAttempt.java @@ -12,10 +12,11 @@ */ public class DefaultOAuthStormpathFactorChallengeGrantAuthenticationAttempt extends AbstractResource implements OAuthStormpathFactorChallengeGrantAuthenticationAttempt { + static final StringProperty GRANT_TYPE = new StringProperty("grant_type"); static final StringProperty CHALLENGE = new StringProperty("challenge"); static final StringProperty CODE = new StringProperty("code"); - private static final Map PROPERTY_DESCRIPTORS = createPropertyDescriptorMap(CHALLENGE, CODE); + private static final Map PROPERTY_DESCRIPTORS = createPropertyDescriptorMap(GRANT_TYPE, CHALLENGE, CODE); public DefaultOAuthStormpathFactorChallengeGrantAuthenticationAttempt(InternalDataStore dataStore) { super(dataStore); @@ -25,6 +26,11 @@ public DefaultOAuthStormpathFactorChallengeGrantAuthenticationAttempt(InternalDa super(dataStore, properties); } + @Override + public void setGrantType(String grantType) { + setProperty(GRANT_TYPE, grantType); + } + @Override public void setChallenge(String challenge) { setProperty(CHALLENGE, challenge); @@ -35,6 +41,10 @@ public void setCode(String code) { setProperty(CODE, code); } + public String getGrantType() { + return getString(GRANT_TYPE); + } + public String getChallenge() { return getString(CHALLENGE); } diff --git a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeGrantRequestAuthenticator.java b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeGrantRequestAuthenticator.java index 9f4ab69d12..36db889012 100644 --- a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeGrantRequestAuthenticator.java +++ b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeGrantRequestAuthenticator.java @@ -29,6 +29,7 @@ public OAuthGrantRequestAuthenticationResult authenticate(OAuthRequestAuthentica OAuthStormpathFactorChallengeGrantRequestAuthentication authentication = (OAuthStormpathFactorChallengeGrantRequestAuthentication) authenticationRequest; OAuthStormpathFactorChallengeGrantAuthenticationAttempt authenticationAttempt = new DefaultOAuthStormpathFactorChallengeGrantAuthenticationAttempt(dataStore); + authenticationAttempt.setGrantType(authentication.getGrantType()); authenticationAttempt.setChallenge(authentication.getChallenge()); authenticationAttempt.setCode(authentication.getCode()); diff --git a/impl/src/main/java/com/stormpath/sdk/impl/oauth/OAuthStormpathFactorChallengeGrantAuthenticationAttempt.java b/impl/src/main/java/com/stormpath/sdk/impl/oauth/OAuthStormpathFactorChallengeGrantAuthenticationAttempt.java index 6e2839c59f..e3148ddd95 100644 --- a/impl/src/main/java/com/stormpath/sdk/impl/oauth/OAuthStormpathFactorChallengeGrantAuthenticationAttempt.java +++ b/impl/src/main/java/com/stormpath/sdk/impl/oauth/OAuthStormpathFactorChallengeGrantAuthenticationAttempt.java @@ -6,6 +6,12 @@ * @since 1.3.1 */ public interface OAuthStormpathFactorChallengeGrantAuthenticationAttempt extends Resource { + /** + * Method used to set the Authentication Grant Type that will be used for the token exchange request. + * @param grantType the Authentication Grant Type that will be used for the token exchange request. + */ + void setGrantType(String grantType); + /** * Method used to set the href of the challenge to be verified. * @param challenge the href of the challenge to be verified. From c93dc375b7e58b7e106a46b34462bda3a9f27361 Mon Sep 17 00:00:00 2001 From: Richard Blaylock Date: Fri, 13 Jan 2017 16:35:08 -0800 Subject: [PATCH 03/16] 1211 Correct @since tag. --- api/src/main/java/com/stormpath/sdk/oauth/Authenticators.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/main/java/com/stormpath/sdk/oauth/Authenticators.java b/api/src/main/java/com/stormpath/sdk/oauth/Authenticators.java index 69d5cf447a..43b60f7a51 100644 --- a/api/src/main/java/com/stormpath/sdk/oauth/Authenticators.java +++ b/api/src/main/java/com/stormpath/sdk/oauth/Authenticators.java @@ -132,7 +132,7 @@ private Authenticators() { /** * Constructs {@link OAuthStormpathFactorChallengeGrantRequestAuthenticator}s. * - * @since 1.1.0 + * @since 1.3.1 */ public static final OAuthStormpathFactorChallengeGrantRequestAuthenticatorFactory OAUTH_STORMPATH_FACTOR_CHALLENGE_GRANT_REQUEST_AUTHENTICATOR = (OAuthStormpathFactorChallengeGrantRequestAuthenticatorFactory) Classes.newInstance("com.stormpath.sdk.impl.oauth.DefaultOAuthStormpathFactorChallengeRequestAuthenticatorFactory"); From 3cfef09c0d977557ff75ab5d73ab723bc28bcf65 Mon Sep 17 00:00:00 2001 From: Richard Blaylock Date: Fri, 13 Jan 2017 17:33:08 -0800 Subject: [PATCH 04/16] 1211 Add (potential) sleep to avoid intermittent test failure due to expiring code. --- .../sdk/impl/application/ApplicationIT.groovy | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/extensions/httpclient/src/test/groovy/com/stormpath/sdk/impl/application/ApplicationIT.groovy b/extensions/httpclient/src/test/groovy/com/stormpath/sdk/impl/application/ApplicationIT.groovy index d31494bfe1..f76414cb00 100644 --- a/extensions/httpclient/src/test/groovy/com/stormpath/sdk/impl/application/ApplicationIT.groovy +++ b/extensions/httpclient/src/test/groovy/com/stormpath/sdk/impl/application/ApplicationIT.groovy @@ -92,6 +92,8 @@ import io.jsonwebtoken.Jwts import io.jsonwebtoken.SignatureAlgorithm import org.apache.commons.codec.binary.Base32 import org.apache.commons.codec.binary.Base64 +import org.joda.time.DateTime +import org.joda.time.DateTimeZone import org.testng.annotations.Test import javax.crypto.Mac @@ -1910,6 +1912,8 @@ class ApplicationIT extends ClientIT { GoogleAuthenticatorFactor factor = createGoogleAuthenticatorFactor(account) + sleepToAvoidCrossingThirtySecondMark() + def challenge = client.instantiate(GoogleAuthenticatorChallenge) challenge = factor.createChallenge(challenge) @@ -2011,6 +2015,20 @@ class ApplicationIT extends ClientIT { } } + protected void sleepToAvoidCrossingThirtySecondMark() { + DateTime now = new DateTime(DateTimeZone.UTC) + int seconds = now.getSecondOfMinute() + int secondsToWait + if ((seconds <= 30) && (seconds > 25)) { + secondsToWait = 31 - seconds + } + else if ((seconds <= 60) && (seconds > 55)) { + secondsToWait = 61 - seconds + } + + sleep(secondsToWait * 1000) + } + /* @since 1.0.RC7 */ @Test From 07c9e0dbf041614d71b78e1e57573b6df1c1b82f Mon Sep 17 00:00:00 2001 From: Richard Blaylock Date: Fri, 13 Jan 2017 17:36:53 -0800 Subject: [PATCH 05/16] 1211 Clarify names of Google Authentictor-specific tests. --- .../com/stormpath/sdk/impl/application/ApplicationIT.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/httpclient/src/test/groovy/com/stormpath/sdk/impl/application/ApplicationIT.groovy b/extensions/httpclient/src/test/groovy/com/stormpath/sdk/impl/application/ApplicationIT.groovy index f76414cb00..f89b792f68 100644 --- a/extensions/httpclient/src/test/groovy/com/stormpath/sdk/impl/application/ApplicationIT.groovy +++ b/extensions/httpclient/src/test/groovy/com/stormpath/sdk/impl/application/ApplicationIT.groovy @@ -1879,7 +1879,7 @@ class ApplicationIT extends ClientIT { /* @since 1.3.1 */ @Test - void testCreateStormpathFactorChallengeTokenForAppAccountWithBadCode() { + void testCreateStormpathFactorChallengeTokenForGoogleAuthenticatorFactorWithBadCode() { def app = createTempApp() def account = createTestAccount(app) @@ -1905,7 +1905,7 @@ class ApplicationIT extends ClientIT { /* @since 1.3.1 */ @Test - void testCreateStormpathFactorChallengeTokenForAppAccountWithValidCode() { + void testCreateStormpathFactorChallengeTokenForGoogleAuthenticatorFactorWithValidCode() { def app = createTempApp() def account = createTestAccount(app) From 3721a33290587b3926e7570d76bd64b80f0b0346 Mon Sep 17 00:00:00 2001 From: Richard Blaylock Date: Fri, 13 Jan 2017 17:37:34 -0800 Subject: [PATCH 06/16] 1211 Avoid asserting error messages. --- .../com/stormpath/sdk/impl/application/ApplicationIT.groovy | 1 - 1 file changed, 1 deletion(-) diff --git a/extensions/httpclient/src/test/groovy/com/stormpath/sdk/impl/application/ApplicationIT.groovy b/extensions/httpclient/src/test/groovy/com/stormpath/sdk/impl/application/ApplicationIT.groovy index f89b792f68..6e8d28eb61 100644 --- a/extensions/httpclient/src/test/groovy/com/stormpath/sdk/impl/application/ApplicationIT.groovy +++ b/extensions/httpclient/src/test/groovy/com/stormpath/sdk/impl/application/ApplicationIT.groovy @@ -1899,7 +1899,6 @@ class ApplicationIT extends ClientIT { catch (ResourceException re) { assertEquals(re.getStatus(), 400) assertEquals(re.getCode(), 13104) - assertEquals("The code submitted is not valid.", re.getDeveloperMessage()) } } From 872f82b82452a88643674a512a9ffb7bf08b4a44 Mon Sep 17 00:00:00 2001 From: Richard Blaylock Date: Fri, 13 Jan 2017 18:08:10 -0800 Subject: [PATCH 07/16] 1211 Simplify and clarify TOTP generation method. --- .../sdk/impl/application/ApplicationIT.groovy | 48 +++++++------------ 1 file changed, 17 insertions(+), 31 deletions(-) diff --git a/extensions/httpclient/src/test/groovy/com/stormpath/sdk/impl/application/ApplicationIT.groovy b/extensions/httpclient/src/test/groovy/com/stormpath/sdk/impl/application/ApplicationIT.groovy index 6e8d28eb61..20a11ba962 100644 --- a/extensions/httpclient/src/test/groovy/com/stormpath/sdk/impl/application/ApplicationIT.groovy +++ b/extensions/httpclient/src/test/groovy/com/stormpath/sdk/impl/application/ApplicationIT.groovy @@ -1916,9 +1916,9 @@ class ApplicationIT extends ClientIT { def challenge = client.instantiate(GoogleAuthenticatorChallenge) challenge = factor.createChallenge(challenge) - String padded = getValidCode(factor) + String validCode = calculateCurrentTOTP(new Base32().decode(factor.getSecret())) - OAuthStormpathFactorChallengeGrantRequestAuthentication request = new DefaultOAuthStormpathFactorChallengeGrantRequestAuthentication(challenge.href, padded) + OAuthStormpathFactorChallengeGrantRequestAuthentication request = new DefaultOAuthStormpathFactorChallengeGrantRequestAuthentication(challenge.href, validCode) def result = Authenticators.OAUTH_STORMPATH_FACTOR_CHALLENGE_GRANT_REQUEST_AUTHENTICATOR.forApplication(app).authenticate(request) assertNotNull result.getAccessTokenHref() @@ -1935,16 +1935,6 @@ class ApplicationIT extends ClientIT { assertEquals result.getExpiresIn(), 3600 } - private String getValidCode(GoogleAuthenticatorFactor factor) { - byte[] secret = new Base32().decode(factor.getSecret()) - - final long timeWindow = System.currentTimeMillis() / TimeUnit.SECONDS.toMillis(30) - - int validCode = calculateCode(secret, timeWindow) - String padded = String.format("%06d", validCode) - return padded - } - private GoogleAuthenticatorFactor createGoogleAuthenticatorFactor(Account account) { GoogleAuthenticatorFactor factor = client.instantiate(GoogleAuthenticatorFactor) factor = factor.setAccountName("accountName").setIssuer("issuer") @@ -1961,39 +1951,35 @@ class ApplicationIT extends ClientIT { private static final int KEY_MODULUS = (int) Math.pow(10, CODE_DIGITS); private static final int CODE_DIGITS = 6; - private static int calculateCode(byte[] key, long tm) { - // Allocating an array of bytes to represent the specified instant - // of time. + /** + * Calculates a TOTP from the given key which should agree with the one generated + * by Google Authenticator when provided with the same key. + * See https://en.wikipedia.org/wiki/Time-based_One-time_Password_Algorithm + * + * @param key the key used to compute the TOTP + * @return the current TOTP, as would be computed by Google Authenticator + */ + private static String calculateCurrentTOTP(byte[] key) { + long timeCounter = System.currentTimeMillis() / TimeUnit.SECONDS.toMillis(30) + byte[] data = new byte[8]; - long value = tm; + long value = timeCounter; - // Converting the instant of time from the long representation to a - // big-endian array of bytes (RFC4226, 5.2. Description). for (int i = 8; i-- > 0; value >>>= 8) { data[i] = (byte) value; } - // Building the secret key specification for the HmacSHA1 algorithm. SecretKeySpec signKey = new SecretKeySpec(key, HMAC_HASH_FUNCTION); try { - // Getting an HmacSHA1 algorithm implementation from the JCE. Mac mac = Mac.getInstance(HMAC_HASH_FUNCTION); - - // Initializing the MAC algorithm. mac.init(signKey); - // Processing the instant of time and getting the encrypted data. byte[] hash = mac.doFinal(data); - // Building the validation code performing dynamic truncation - // (RFC4226, 5.3. Generating an HOTP value) int offset = hash[hash.length - 1] & 0xF; - // We are using a long because Java hasn't got an unsigned integer type - // and we need 32 unsigned bits). long truncatedHash = 0; - for (int i = 0; i < 4; ++i) { truncatedHash <<= 8; @@ -2007,9 +1993,9 @@ class ApplicationIT extends ClientIT { truncatedHash &= 0x7FFFFFFF; truncatedHash %= KEY_MODULUS; - // Returning the validation code to the caller. - return (int) truncatedHash; - } catch (NoSuchAlgorithmException | InvalidKeyException ex) { + return String.format("%06d", (int) truncatedHash) + } + catch (NoSuchAlgorithmException | InvalidKeyException ex) { throw new IllegalStateException(ex); } } From e880ae88f07331bdff1c08938001eddde2e7c52a Mon Sep 17 00:00:00 2001 From: Richard Blaylock Date: Fri, 13 Jan 2017 18:08:59 -0800 Subject: [PATCH 08/16] 1211 Combine object mapper declaration and initialization. --- .../stormpath/sdk/servlet/filter/oauth/OAuthException.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/filter/oauth/OAuthException.java b/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/filter/oauth/OAuthException.java index 02a3c18ef9..e71c993469 100644 --- a/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/filter/oauth/OAuthException.java +++ b/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/filter/oauth/OAuthException.java @@ -27,11 +27,7 @@ */ public class OAuthException extends RuntimeException { - private static final ObjectMapper objectMapper; - - static { - objectMapper = new ObjectMapper(); - } + private static final ObjectMapper objectMapper = new ObjectMapper(); private final OAuthErrorCode errorCode; From d8550f338d09eb31fcc1533b22d223674e07dc3a Mon Sep 17 00:00:00 2001 From: Richard Blaylock Date: Fri, 13 Jan 2017 18:58:18 -0800 Subject: [PATCH 09/16] 1211 Move constructor to live with other constructors. --- .../sdk/servlet/filter/oauth/OAuthException.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/filter/oauth/OAuthException.java b/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/filter/oauth/OAuthException.java index e71c993469..e84874db04 100644 --- a/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/filter/oauth/OAuthException.java +++ b/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/filter/oauth/OAuthException.java @@ -53,6 +53,12 @@ public OAuthException(OAuthErrorCode code, String message, Exception cause) { initializeErrorMap(); } + public OAuthException(OAuthErrorCode code, Map error, String message) { + this(code, message, null); + + errorMap.putAll(error); + } + private void initializeErrorMap() { errorMap = new LinkedHashMap<>(); @@ -64,12 +70,6 @@ private void initializeErrorMap() { } } - public OAuthException(OAuthErrorCode code, Map error, String message) { - this(code, message, null); - - errorMap.putAll(error); - } - public OAuthErrorCode getErrorCode() { return errorCode; } From 948d790cc6887f479a673d727208acc69ec6c3a6 Mon Sep 17 00:00:00 2001 From: Richard Blaylock Date: Fri, 13 Jan 2017 19:12:42 -0800 Subject: [PATCH 10/16] 1211 Make string constants constants. --- .../servlet/mvc/AccessTokenController.java | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/mvc/AccessTokenController.java b/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/mvc/AccessTokenController.java index 06ba3c2b92..6bb3d3a5eb 100644 --- a/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/mvc/AccessTokenController.java +++ b/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/mvc/AccessTokenController.java @@ -78,6 +78,15 @@ public class AccessTokenController extends AbstractController { private static final String STORMPATH_FACTOR_CHALLENGE_GRANT_TYPE = "stormpath_factor_challenge"; private static final String GRANT_TYPE_PARAM_NAME = "grant_type"; + private static final String OAUTH_RESPONSE_ERROR = "error"; + private static final String OAUTH_RESPONSE_ACTION = "action"; + private static final String OAUTH_RESPONSE_ERROR_DESCRIPTION = "error_description"; + private static final String OAUTH_RESPONSE_STATE = "state"; + private static final String OAUTH_RESPONSE_ALLOWED_FACTOR_TYPES = "allowedFactorTypes"; + private static final String OAUTH_RESPONSE_FACTOR = "factor"; + private static final String OAUTH_RESPONSE_CHALLENGE = "challenge"; + private static final String OAUTH_RESPONSE_FACTORS = "factors"; + private RefreshTokenResultFactory refreshTokenResultFactory; private RefreshTokenAuthenticationRequestFactory refreshTokenAuthenticationRequestFactory; private RequestAuthorizer requestAuthorizer; @@ -336,27 +345,27 @@ private OAuthException convertToOAuthException(ResourceException e, OAuthErrorCo if (error instanceof DefaultError) { DefaultError defaultError = ((DefaultError) error); - Object errorObject = defaultError.getProperty("error"); + Object errorObject = defaultError.getProperty(OAUTH_RESPONSE_ERROR); oauthError = errorObject == null ? oauthError : new OAuthErrorCode(errorObject.toString()); - Object action = defaultError.getProperty("action"); + Object action = defaultError.getProperty(OAUTH_RESPONSE_ACTION); if (action instanceof String) { // get action map from error based on the action Map errorMap = new LinkedHashMap<>(); - errorMap.put("error_description", defaultError.getProperty("error_description")); - errorMap.put("action", action); + exposeOAuthErrorProperty(errorMap, defaultError, OAUTH_RESPONSE_ERROR_DESCRIPTION); + exposeOAuthErrorProperty(errorMap, defaultError, OAUTH_RESPONSE_ACTION); if ("factor_enroll".equals(action)) { - errorMap.put("state", defaultError.getProperty("state")); - errorMap.put("allowedFactorTypes", defaultError.getProperty("allowedFactorTypes")); + exposeOAuthErrorProperty(errorMap, defaultError, OAUTH_RESPONSE_STATE); + exposeOAuthErrorProperty(errorMap, defaultError, OAUTH_RESPONSE_ALLOWED_FACTOR_TYPES); } else if ("factor_challenge".equals(action)) { - errorMap.put("state", defaultError.getProperty("state")); - errorMap.put("allowedFactorTypes", defaultError.getProperty("allowedFactorTypes")); - errorMap.put("factor", defaultError.getProperty("factor")); - errorMap.put("challenge", defaultError.getProperty("challenge")); + exposeOAuthErrorProperty(errorMap, defaultError, OAUTH_RESPONSE_STATE); + exposeOAuthErrorProperty(errorMap, defaultError, OAUTH_RESPONSE_ALLOWED_FACTOR_TYPES); + exposeOAuthErrorProperty(errorMap, defaultError, OAUTH_RESPONSE_FACTOR); + exposeOAuthErrorProperty(errorMap, defaultError, OAUTH_RESPONSE_CHALLENGE); } else if ("factor_select".equals(action)) { - errorMap.put("factors", defaultError.getProperty("factors")); + exposeOAuthErrorProperty(errorMap, defaultError, OAUTH_RESPONSE_FACTORS); } return new OAuthException(oauthError, errorMap, ""); } @@ -365,6 +374,10 @@ else if ("factor_select".equals(action)) { return new OAuthException(oauthError, message); } + private void exposeOAuthErrorProperty(Map errorMap, DefaultError defaultError, String propertyName) { + errorMap.put(propertyName, defaultError.getProperty(propertyName)); + } + protected AccessTokenResult stormpathTokenAuthenticationRequest(HttpServletRequest request, HttpServletResponse response) { OAuthGrantRequestAuthenticationResult authenticationResult; From 4d179800b36ee202d8fd8336751cd47c247d6180 Mon Sep 17 00:00:00 2001 From: Richard Blaylock Date: Fri, 13 Jan 2017 19:25:50 -0800 Subject: [PATCH 11/16] 1211 Add description for the convertToOAuthException method. --- .../sdk/servlet/mvc/AccessTokenController.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/mvc/AccessTokenController.java b/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/mvc/AccessTokenController.java index 6bb3d3a5eb..c550c7472a 100644 --- a/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/mvc/AccessTokenController.java +++ b/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/mvc/AccessTokenController.java @@ -337,8 +337,16 @@ protected AccessTokenResult stormpathFactorChallengeAuthenticationRequest(HttpSe return createAccessTokenResult(request, response, authenticationResult); } - private OAuthException convertToOAuthException(ResourceException e, OAuthErrorCode defaultErrorCode) { - com.stormpath.sdk.error.Error error = e.getStormpathError(); + /** + * Takes a {@link ResourceException ResourceException} thrown when retrieving an oauth token and exposes + * select fields from its Error to be returned as an {@link OAuthException OAuthException}. + * + * @param resourceException the ResourceException to convert + * @param defaultErrorCode the OAuthErrorCode to use in case none is supplied in the underlying Error + * @return an OAuthException exposing select fields depending on the value of the action property + */ + private OAuthException convertToOAuthException(ResourceException resourceException, OAuthErrorCode defaultErrorCode) { + com.stormpath.sdk.error.Error error = resourceException.getStormpathError(); String message = error.getMessage(); OAuthErrorCode oauthError = defaultErrorCode; From 44a3f3ef0c40339831212002c53afcbdceb2cf66 Mon Sep 17 00:00:00 2001 From: Richard Blaylock Date: Fri, 13 Jan 2017 19:32:40 -0800 Subject: [PATCH 12/16] 1211 Add missing license headers. --- ...orChallengeGrantAuthenticationAttempt.java | 15 +++++++++ ...orChallengeGrantRequestAuthentication.java | 15 +++++++++ ...torChallengeGrantRequestAuthenticator.java | 15 +++++++++ ...lengeGrantRequestAuthenticatorFactory.java | 31 +++++++++++++++++++ ...rChallengeRequestAuthenticatorFactory.java | 16 ---------- ...orChallengeGrantAuthenticationAttempt.java | 15 +++++++++ 6 files changed, 91 insertions(+), 16 deletions(-) create mode 100644 impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeGrantRequestAuthenticatorFactory.java delete mode 100644 impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeRequestAuthenticatorFactory.java diff --git a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeGrantAuthenticationAttempt.java b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeGrantAuthenticationAttempt.java index 9becfac045..29ab6d3dbd 100644 --- a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeGrantAuthenticationAttempt.java +++ b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeGrantAuthenticationAttempt.java @@ -1,3 +1,18 @@ +/* + * Copyright 2016 Stormpath, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.stormpath.sdk.impl.oauth; import com.stormpath.sdk.impl.ds.InternalDataStore; diff --git a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeGrantRequestAuthentication.java b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeGrantRequestAuthentication.java index e283a240e7..39e9c59313 100644 --- a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeGrantRequestAuthentication.java +++ b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeGrantRequestAuthentication.java @@ -1,3 +1,18 @@ +/* + * Copyright 2016 Stormpath, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.stormpath.sdk.impl.oauth; import com.stormpath.sdk.lang.Assert; diff --git a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeGrantRequestAuthenticator.java b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeGrantRequestAuthenticator.java index 36db889012..e5ba4766b1 100644 --- a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeGrantRequestAuthenticator.java +++ b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeGrantRequestAuthenticator.java @@ -1,3 +1,18 @@ +/* + * Copyright 2016 Stormpath, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.stormpath.sdk.impl.oauth; import com.stormpath.sdk.application.Application; diff --git a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeGrantRequestAuthenticatorFactory.java b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeGrantRequestAuthenticatorFactory.java new file mode 100644 index 0000000000..890412929b --- /dev/null +++ b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeGrantRequestAuthenticatorFactory.java @@ -0,0 +1,31 @@ +/* + * Copyright 2016 Stormpath, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.stormpath.sdk.impl.oauth; + +import com.stormpath.sdk.application.Application; +import com.stormpath.sdk.impl.application.DefaultApplication; +import com.stormpath.sdk.oauth.OAuthStormpathFactorChallengeGrantRequestAuthenticator; +import com.stormpath.sdk.oauth.OAuthStormpathFactorChallengeGrantRequestAuthenticatorFactory; + +/** + * @since 1.3.1 + */ +public class DefaultOAuthStormpathFactorChallengeGrantRequestAuthenticatorFactory implements OAuthStormpathFactorChallengeGrantRequestAuthenticatorFactory { + @Override + public OAuthStormpathFactorChallengeGrantRequestAuthenticator forApplication(Application application) { + return ((DefaultApplication) application).createStormpathFactorChallengeGrantAuthenticator(); + } +} diff --git a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeRequestAuthenticatorFactory.java b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeRequestAuthenticatorFactory.java deleted file mode 100644 index 67f635b5b3..0000000000 --- a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeRequestAuthenticatorFactory.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.stormpath.sdk.impl.oauth; - -import com.stormpath.sdk.application.Application; -import com.stormpath.sdk.impl.application.DefaultApplication; -import com.stormpath.sdk.oauth.OAuthStormpathFactorChallengeGrantRequestAuthenticator; -import com.stormpath.sdk.oauth.OAuthStormpathFactorChallengeGrantRequestAuthenticatorFactory; - -/** - * @since 1.3.1 - */ -public class DefaultOAuthStormpathFactorChallengeRequestAuthenticatorFactory implements OAuthStormpathFactorChallengeGrantRequestAuthenticatorFactory { - @Override - public OAuthStormpathFactorChallengeGrantRequestAuthenticator forApplication(Application application) { - return ((DefaultApplication) application).createStormpathFactorChallengeGrantAuthenticator(); - } -} diff --git a/impl/src/main/java/com/stormpath/sdk/impl/oauth/OAuthStormpathFactorChallengeGrantAuthenticationAttempt.java b/impl/src/main/java/com/stormpath/sdk/impl/oauth/OAuthStormpathFactorChallengeGrantAuthenticationAttempt.java index e3148ddd95..9bf597b4d9 100644 --- a/impl/src/main/java/com/stormpath/sdk/impl/oauth/OAuthStormpathFactorChallengeGrantAuthenticationAttempt.java +++ b/impl/src/main/java/com/stormpath/sdk/impl/oauth/OAuthStormpathFactorChallengeGrantAuthenticationAttempt.java @@ -1,3 +1,18 @@ +/* + * Copyright 2016 Stormpath, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.stormpath.sdk.impl.oauth; import com.stormpath.sdk.resource.Resource; From 5e1c50fd0ba54f9cb5a19641dbb0799507233359 Mon Sep 17 00:00:00 2001 From: Richard Blaylock Date: Fri, 13 Jan 2017 19:38:01 -0800 Subject: [PATCH 13/16] 1211 Add description to public interface. --- ...AuthStormpathFactorChallengeGrantAuthenticationAttempt.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/impl/src/main/java/com/stormpath/sdk/impl/oauth/OAuthStormpathFactorChallengeGrantAuthenticationAttempt.java b/impl/src/main/java/com/stormpath/sdk/impl/oauth/OAuthStormpathFactorChallengeGrantAuthenticationAttempt.java index 9bf597b4d9..4487bd2c42 100644 --- a/impl/src/main/java/com/stormpath/sdk/impl/oauth/OAuthStormpathFactorChallengeGrantAuthenticationAttempt.java +++ b/impl/src/main/java/com/stormpath/sdk/impl/oauth/OAuthStormpathFactorChallengeGrantAuthenticationAttempt.java @@ -18,6 +18,9 @@ import com.stormpath.sdk.resource.Resource; /** + * This class is used to wrap the information required to request an OAuth token in exchange for verifying + * a challenge to an authentication factor. + * * @since 1.3.1 */ public interface OAuthStormpathFactorChallengeGrantAuthenticationAttempt extends Resource { From 5c14a65bb5ea88b4f9f665366ba582c2167fe5fe Mon Sep 17 00:00:00 2001 From: Richard Blaylock Date: Fri, 13 Jan 2017 19:50:55 -0800 Subject: [PATCH 14/16] 1211 Add more missing license headers. --- ...FactorChallengeGrantRequestAuthentication.java | 15 +++++++++++++++ ...hFactorChallengeGrantRequestAuthenticator.java | 15 +++++++++++++++ ...ChallengeGrantRequestAuthenticatorFactory.java | 15 +++++++++++++++ 3 files changed, 45 insertions(+) diff --git a/api/src/main/java/com/stormpath/sdk/oauth/OAuthStormpathFactorChallengeGrantRequestAuthentication.java b/api/src/main/java/com/stormpath/sdk/oauth/OAuthStormpathFactorChallengeGrantRequestAuthentication.java index 98210d251d..77c3ee79f2 100644 --- a/api/src/main/java/com/stormpath/sdk/oauth/OAuthStormpathFactorChallengeGrantRequestAuthentication.java +++ b/api/src/main/java/com/stormpath/sdk/oauth/OAuthStormpathFactorChallengeGrantRequestAuthentication.java @@ -1,3 +1,18 @@ +/* + * Copyright 2016 Stormpath, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.stormpath.sdk.oauth; /** diff --git a/api/src/main/java/com/stormpath/sdk/oauth/OAuthStormpathFactorChallengeGrantRequestAuthenticator.java b/api/src/main/java/com/stormpath/sdk/oauth/OAuthStormpathFactorChallengeGrantRequestAuthenticator.java index bc3d28c593..9d2efc72b0 100644 --- a/api/src/main/java/com/stormpath/sdk/oauth/OAuthStormpathFactorChallengeGrantRequestAuthenticator.java +++ b/api/src/main/java/com/stormpath/sdk/oauth/OAuthStormpathFactorChallengeGrantRequestAuthenticator.java @@ -1,3 +1,18 @@ +/* + * Copyright 2016 Stormpath, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.stormpath.sdk.oauth; diff --git a/api/src/main/java/com/stormpath/sdk/oauth/OAuthStormpathFactorChallengeGrantRequestAuthenticatorFactory.java b/api/src/main/java/com/stormpath/sdk/oauth/OAuthStormpathFactorChallengeGrantRequestAuthenticatorFactory.java index 6182ab5be8..9cb4a3787b 100644 --- a/api/src/main/java/com/stormpath/sdk/oauth/OAuthStormpathFactorChallengeGrantRequestAuthenticatorFactory.java +++ b/api/src/main/java/com/stormpath/sdk/oauth/OAuthStormpathFactorChallengeGrantRequestAuthenticatorFactory.java @@ -1,3 +1,18 @@ +/* + * Copyright 2016 Stormpath, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.stormpath.sdk.oauth; /** From 3530514f3f6746bebf018e95c05e9c3c0b31aecd Mon Sep 17 00:00:00 2001 From: Richard Blaylock Date: Fri, 13 Jan 2017 20:00:32 -0800 Subject: [PATCH 15/16] 1211 Add descriptions to public interfaces. --- ...AuthStormpathFactorChallengeGrantRequestAuthenticator.java | 4 ++++ ...rmpathFactorChallengeGrantRequestAuthenticatorFactory.java | 2 ++ 2 files changed, 6 insertions(+) diff --git a/api/src/main/java/com/stormpath/sdk/oauth/OAuthStormpathFactorChallengeGrantRequestAuthenticator.java b/api/src/main/java/com/stormpath/sdk/oauth/OAuthStormpathFactorChallengeGrantRequestAuthenticator.java index 9d2efc72b0..25f86ac9c8 100644 --- a/api/src/main/java/com/stormpath/sdk/oauth/OAuthStormpathFactorChallengeGrantRequestAuthenticator.java +++ b/api/src/main/java/com/stormpath/sdk/oauth/OAuthStormpathFactorChallengeGrantRequestAuthenticator.java @@ -17,6 +17,10 @@ /** + * Interface denoting a Stormpath Factor Challenge Grant-specific {@link OAuthRequestAuthenticator}. + * It is used to authenticate an account using a challenge to a factor and receive in exchange + * a valid OAuth 2.0 token. + * * @since 1.3.1 */ public interface OAuthStormpathFactorChallengeGrantRequestAuthenticator extends OAuthRequestAuthenticator { diff --git a/api/src/main/java/com/stormpath/sdk/oauth/OAuthStormpathFactorChallengeGrantRequestAuthenticatorFactory.java b/api/src/main/java/com/stormpath/sdk/oauth/OAuthStormpathFactorChallengeGrantRequestAuthenticatorFactory.java index 9cb4a3787b..0ffbc7568e 100644 --- a/api/src/main/java/com/stormpath/sdk/oauth/OAuthStormpathFactorChallengeGrantRequestAuthenticatorFactory.java +++ b/api/src/main/java/com/stormpath/sdk/oauth/OAuthStormpathFactorChallengeGrantRequestAuthenticatorFactory.java @@ -16,6 +16,8 @@ package com.stormpath.sdk.oauth; /** + * A Stormpath Factor Challenge Grant-specific Authenticator Factory. + * * @since 1.3.1 */ public interface OAuthStormpathFactorChallengeGrantRequestAuthenticatorFactory extends OAuthRequestAuthenticatorFactory { From c8333b18f5412fc36c3cfe8cfd843366d3ddca6f Mon Sep 17 00:00:00 2001 From: Richard Blaylock Date: Fri, 13 Jan 2017 21:34:23 -0800 Subject: [PATCH 16/16] 1211 Adjust for changed class name. --- api/src/main/java/com/stormpath/sdk/oauth/Authenticators.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/main/java/com/stormpath/sdk/oauth/Authenticators.java b/api/src/main/java/com/stormpath/sdk/oauth/Authenticators.java index 43b60f7a51..86568b4495 100644 --- a/api/src/main/java/com/stormpath/sdk/oauth/Authenticators.java +++ b/api/src/main/java/com/stormpath/sdk/oauth/Authenticators.java @@ -135,7 +135,7 @@ private Authenticators() { * @since 1.3.1 */ public static final OAuthStormpathFactorChallengeGrantRequestAuthenticatorFactory OAUTH_STORMPATH_FACTOR_CHALLENGE_GRANT_REQUEST_AUTHENTICATOR = - (OAuthStormpathFactorChallengeGrantRequestAuthenticatorFactory) Classes.newInstance("com.stormpath.sdk.impl.oauth.DefaultOAuthStormpathFactorChallengeRequestAuthenticatorFactory"); + (OAuthStormpathFactorChallengeGrantRequestAuthenticatorFactory) Classes.newInstance("com.stormpath.sdk.impl.oauth.DefaultOAuthStormpathFactorChallengeGrantRequestAuthenticatorFactory"); }