diff --git a/api/src/main/java/com/stormpath/sdk/challenge/google/GoogleAuthenticatorChallenge.java b/api/src/main/java/com/stormpath/sdk/challenge/google/GoogleAuthenticatorChallenge.java index 6a0d54afe2..5e30f866f9 100644 --- a/api/src/main/java/com/stormpath/sdk/challenge/google/GoogleAuthenticatorChallenge.java +++ b/api/src/main/java/com/stormpath/sdk/challenge/google/GoogleAuthenticatorChallenge.java @@ -29,6 +29,13 @@ * * @since 1.1.0 */ -public interface GoogleAuthenticatorChallenge extends Challenge{ +public interface GoogleAuthenticatorChallenge extends Challenge { + /** + * A GoogleAuthenticatorChallenge can be validated at the same time the challenge is created by setting the code on the challenge. + * + * @param code the code the validated at the same time the GoogleAuthenticatorChallenge is created + * @since 1.4.0 + */ + void setCode(String code); } diff --git a/api/src/main/java/com/stormpath/sdk/challenge/google/GoogleAuthenticatorChallenges.java b/api/src/main/java/com/stormpath/sdk/challenge/google/GoogleAuthenticatorChallenges.java index 15b9d5d83a..4774954a1a 100644 --- a/api/src/main/java/com/stormpath/sdk/challenge/google/GoogleAuthenticatorChallenges.java +++ b/api/src/main/java/com/stormpath/sdk/challenge/google/GoogleAuthenticatorChallenges.java @@ -58,7 +58,7 @@ public final class GoogleAuthenticatorChallenges extends Challenges { } private static final Class BUILDER_CLASS = - Classes.forName("com.stormpath.sdk.impl.challenge.DefaultCreateChallengeRequestBuilder"); + Classes.forName("com.stormpath.sdk.impl.challenge.google.DefaultGoogleAuthenticatorCreateChallengeRequestBuilder"); //Prevent instantiation outside of outer class. //Use getInstance() to retrieve the singleton instance. @@ -152,9 +152,9 @@ public static EqualsExpressionFactory status() { * * @since 1.1.0 */ - public static CreateChallengeRequestBuilder newCreateRequestFor(GoogleAuthenticatorChallenge challenge) { - Constructor ctor = Classes.getConstructor(BUILDER_CLASS, Challenge.class); - return (CreateChallengeRequestBuilder) Classes.instantiate(ctor, challenge); + public static GoogleAuthenticatorCreateChallengeRequestBuilder newCreateRequestFor(GoogleAuthenticatorChallenge challenge) { + Constructor ctor = Classes.getConstructor(BUILDER_CLASS, GoogleAuthenticatorChallenge.class); + return (GoogleAuthenticatorCreateChallengeRequestBuilder) Classes.instantiate(ctor, challenge); } private static EqualsExpressionFactory newEqualsExpressionFactory(String propName) { diff --git a/api/src/main/java/com/stormpath/sdk/challenge/google/GoogleAuthenticatorCreateChallengeRequest.java b/api/src/main/java/com/stormpath/sdk/challenge/google/GoogleAuthenticatorCreateChallengeRequest.java new file mode 100644 index 0000000000..d7029f8294 --- /dev/null +++ b/api/src/main/java/com/stormpath/sdk/challenge/google/GoogleAuthenticatorCreateChallengeRequest.java @@ -0,0 +1,27 @@ +/* + * Copyright 2017 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.challenge.google; + +import com.stormpath.sdk.challenge.CreateChallengeRequest; + +/** + * A Google Authenticator specific create challenge request + * + * Google Authenticator challenges can validate a code at the same time that the challenge is created. + * + * @since 1.4.0 + */ +public interface GoogleAuthenticatorCreateChallengeRequest extends CreateChallengeRequest {} diff --git a/api/src/main/java/com/stormpath/sdk/challenge/google/GoogleAuthenticatorCreateChallengeRequestBuilder.java b/api/src/main/java/com/stormpath/sdk/challenge/google/GoogleAuthenticatorCreateChallengeRequestBuilder.java new file mode 100644 index 0000000000..ce0dc15f40 --- /dev/null +++ b/api/src/main/java/com/stormpath/sdk/challenge/google/GoogleAuthenticatorCreateChallengeRequestBuilder.java @@ -0,0 +1,35 @@ +/* + * Copyright 2017 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.challenge.google; + +import com.stormpath.sdk.challenge.CreateChallengeRequestBuilder; + +/** + * A builder to construct {@link GoogleAuthenticatorCreateChallengeRequest}s Google Authenticator specific create challenge requests. + * + * Google Authenticator can both create the challenge and set the code in one API call. + * See {@link com.stormpath.sdk.factor.google.GoogleAuthenticatorFactor} + * + * @since 1.4.0 + */ +public interface GoogleAuthenticatorCreateChallengeRequestBuilder extends CreateChallengeRequestBuilder { + + /** + * @param code to be used to validate the challenge for the {@link com.stormpath.sdk.factor.google.GoogleAuthenticatorFactor} + * @return GoogleAuthenticatorCreateChallengeRequestBuilder for method chaining with the builder pattern + */ + GoogleAuthenticatorCreateChallengeRequestBuilder withCode(String code); +} diff --git a/api/src/main/java/com/stormpath/sdk/factor/google/GoogleAuthenticatorFactor.java b/api/src/main/java/com/stormpath/sdk/factor/google/GoogleAuthenticatorFactor.java index 3ee4d49885..3d3c380b32 100644 --- a/api/src/main/java/com/stormpath/sdk/factor/google/GoogleAuthenticatorFactor.java +++ b/api/src/main/java/com/stormpath/sdk/factor/google/GoogleAuthenticatorFactor.java @@ -22,8 +22,16 @@ * A {@code GoogleAuthenticatorFactor} is a Factor that represents a two-step verification services using * the Time-based One-time Password Algorithm (TOTP) and HMAC-based One-time Password Algorithm (HOTP), * for authenticating users of mobile applications by Google. - * When issuing a challenge via an GoogleAuthenticatorFactor, a code is sent to the Google Authenticator app, - * and the user can enter the received code back into the system to verify/complete the challenge. + *

+ * The TOTP algorithm determines the next valid code without the requirement for any communication between server and + * TOTP client. + *

+ * As such, the challenge can be created with the code at the same time: + *

+ * + *

+ * GoogleAuthenticatorChallenge challenge = googleAuthenticatorFactor.createChallenge(code);
+ * 
* * @since 1.1.0 */ @@ -80,4 +88,13 @@ public interface GoogleAuthenticatorFactor extends AbstractInstanceResource implements Challenge { public static final EnumProperty STATUS = new EnumProperty<>("status", Enum.class); - static final StringProperty CODE = new StringProperty("code"); + public static final StringProperty CODE = new StringProperty("code"); static final ResourceReference ACCOUNT = new ResourceReference<>("account", Account.class); static final ResourceReference FACTOR = new ResourceReference<>("factor", Factor.class); public static final DateProperty CREATED_AT = new DateProperty("createdAt"); @@ -92,16 +92,11 @@ public Challenge setFactor(T factor) { @Override public boolean validate(String code) { Assert.notNull(code, "code can not be null."); - setCode(code); + setProperty(CODE, code); Challenge returnedChallenge = getDataStore().create(getHref(), this); if ((returnedChallenge.getStatus()).name().equals("SUCCESS")) { return true; } return false; } - - protected Challenge setCode(String code) { - setProperty(CODE, code); - return this; - } } diff --git a/impl/src/main/java/com/stormpath/sdk/impl/challenge/DefaultCreateChallengeRequestBuilder.java b/impl/src/main/java/com/stormpath/sdk/impl/challenge/DefaultCreateChallengeRequestBuilder.java index 5ee8e9de20..093405c341 100644 --- a/impl/src/main/java/com/stormpath/sdk/impl/challenge/DefaultCreateChallengeRequestBuilder.java +++ b/impl/src/main/java/com/stormpath/sdk/impl/challenge/DefaultCreateChallengeRequestBuilder.java @@ -24,10 +24,10 @@ /** * @since 1.1.0 */ -public class DefaultCreateChallengeRequestBuilder implements CreateChallengeRequestBuilder { +public class DefaultCreateChallengeRequestBuilder implements CreateChallengeRequestBuilder { - private final T challenge; - private ChallengeOptions options; + protected final T challenge; + protected ChallengeOptions options; public DefaultCreateChallengeRequestBuilder(T challenge) { Assert.notNull(challenge, "Challenge can't be null."); diff --git a/impl/src/main/java/com/stormpath/sdk/impl/challenge/google/DefaultGoogleAuthenticatorChallenge.java b/impl/src/main/java/com/stormpath/sdk/impl/challenge/google/DefaultGoogleAuthenticatorChallenge.java index 843120c1fc..ff7c8b170c 100644 --- a/impl/src/main/java/com/stormpath/sdk/impl/challenge/google/DefaultGoogleAuthenticatorChallenge.java +++ b/impl/src/main/java/com/stormpath/sdk/impl/challenge/google/DefaultGoogleAuthenticatorChallenge.java @@ -27,7 +27,7 @@ /** * @since 1.1.0 */ -public class DefaultGoogleAuthenticatorChallenge extends AbstractChallenge implements GoogleAuthenticatorChallenge{ +public class DefaultGoogleAuthenticatorChallenge extends AbstractChallenge implements GoogleAuthenticatorChallenge { static final Map PROPERTY_DESCRIPTORS = AbstractChallenge.PROPERTY_DESCRIPTORS; @@ -53,4 +53,9 @@ public GoogleAuthenticatorChallengeStatus getStatus() { } return GoogleAuthenticatorChallengeStatus.valueOf(value.toUpperCase()); } + + @Override + public void setCode(String code) { + setProperty(CODE, code); + } } diff --git a/impl/src/main/java/com/stormpath/sdk/impl/challenge/google/DefaultGoogleAuthenticatorCreateChallengeRequest.java b/impl/src/main/java/com/stormpath/sdk/impl/challenge/google/DefaultGoogleAuthenticatorCreateChallengeRequest.java new file mode 100644 index 0000000000..4b18c2a83a --- /dev/null +++ b/impl/src/main/java/com/stormpath/sdk/impl/challenge/google/DefaultGoogleAuthenticatorCreateChallengeRequest.java @@ -0,0 +1,31 @@ +/* + * Copyright 2017 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.challenge.google; + +import com.stormpath.sdk.challenge.Challenge; +import com.stormpath.sdk.challenge.ChallengeOptions; +import com.stormpath.sdk.challenge.google.GoogleAuthenticatorCreateChallengeRequest; +import com.stormpath.sdk.impl.challenge.DefaultCreateChallengeRequest; + +/** + * @since 1.4.0 + */ +public class DefaultGoogleAuthenticatorCreateChallengeRequest extends DefaultCreateChallengeRequest implements GoogleAuthenticatorCreateChallengeRequest { + + public DefaultGoogleAuthenticatorCreateChallengeRequest(Challenge challenge, ChallengeOptions options) { + super(challenge, options); + } +} diff --git a/impl/src/main/java/com/stormpath/sdk/impl/challenge/google/DefaultGoogleAuthenticatorCreateChallengeRequestBuilder.java b/impl/src/main/java/com/stormpath/sdk/impl/challenge/google/DefaultGoogleAuthenticatorCreateChallengeRequestBuilder.java new file mode 100644 index 0000000000..2722d6434b --- /dev/null +++ b/impl/src/main/java/com/stormpath/sdk/impl/challenge/google/DefaultGoogleAuthenticatorCreateChallengeRequestBuilder.java @@ -0,0 +1,42 @@ +/* + * Copyright 2017 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.challenge.google; + +import com.stormpath.sdk.challenge.google.GoogleAuthenticatorCreateChallengeRequest; +import com.stormpath.sdk.challenge.google.GoogleAuthenticatorChallenge; +import com.stormpath.sdk.challenge.google.GoogleAuthenticatorCreateChallengeRequestBuilder; +import com.stormpath.sdk.impl.challenge.DefaultCreateChallengeRequestBuilder; + +/** + * @since 1.4.0 + */ +public class DefaultGoogleAuthenticatorCreateChallengeRequestBuilder extends DefaultCreateChallengeRequestBuilder implements GoogleAuthenticatorCreateChallengeRequestBuilder { + + public DefaultGoogleAuthenticatorCreateChallengeRequestBuilder(GoogleAuthenticatorChallenge challenge) { + super(challenge); + } + + @Override + public GoogleAuthenticatorCreateChallengeRequestBuilder withCode(String code) { + challenge.setCode(code); + return this; + } + + @Override + public GoogleAuthenticatorCreateChallengeRequest build() { + return new DefaultGoogleAuthenticatorCreateChallengeRequest(challenge, options); + } +} diff --git a/impl/src/main/java/com/stormpath/sdk/impl/factor/google/DefaultGoogleAuthenticatorFactor.java b/impl/src/main/java/com/stormpath/sdk/impl/factor/google/DefaultGoogleAuthenticatorFactor.java index 42b09c8299..5f439133c8 100644 --- a/impl/src/main/java/com/stormpath/sdk/impl/factor/google/DefaultGoogleAuthenticatorFactor.java +++ b/impl/src/main/java/com/stormpath/sdk/impl/factor/google/DefaultGoogleAuthenticatorFactor.java @@ -18,6 +18,7 @@ import com.stormpath.sdk.challenge.google.GoogleAuthenticatorChallenge; import com.stormpath.sdk.factor.FactorType; import com.stormpath.sdk.factor.google.GoogleAuthenticatorFactor; +import com.stormpath.sdk.impl.challenge.google.DefaultGoogleAuthenticatorChallenge; import com.stormpath.sdk.impl.ds.InternalDataStore; import com.stormpath.sdk.impl.factor.AbstractFactor; import com.stormpath.sdk.impl.resource.Property; @@ -89,6 +90,13 @@ public String getBase64QrImage() { return getString(BASE64_QR_IMAGE); } + @Override + public GoogleAuthenticatorChallenge createChallenge(String code) { + GoogleAuthenticatorChallenge challenge = new DefaultGoogleAuthenticatorChallenge(getDataStore()); + challenge.setCode(code); + return createChallenge(challenge); + } + protected FactorType getConcreteFactorType() { return FactorType.GOOGLE_AUTHENTICATOR; }