diff --git a/src/main/groovy/com/stormpath/tck/authentication/CookieIT.groovy b/src/main/groovy/com/stormpath/tck/authentication/CookieIT.groovy index ecf11a1..3bcbc44 100644 --- a/src/main/groovy/com/stormpath/tck/authentication/CookieIT.groovy +++ b/src/main/groovy/com/stormpath/tck/authentication/CookieIT.groovy @@ -129,15 +129,6 @@ class CookieIT extends AbstractIT { } else { assertTrue accessTokenCookie.maxAge * 1000L + now - accessTokenTtl < 2000 } - - def refreshTokenCookie = response.detailedCookies.get("refresh_token") - def refreshTokenTtl = JwtUtils.parseJwt(refreshTokenCookie.value).getBody().getExpiration().time - // some integrations use max-age and some use expires - if (refreshTokenCookie.expiryDate) { - assertEquals refreshTokenCookie.expiryDate.time, refreshTokenTtl - } else { - assertTrue refreshTokenCookie.maxAge * 1000L + now - refreshTokenTtl < 2000 - } } /** Passing refresh token as access token should fail diff --git a/src/main/groovy/com/stormpath/tck/errors/ErrorsIT.groovy b/src/main/groovy/com/stormpath/tck/errors/ErrorsIT.groovy index 67658ab..71d60b8 100644 --- a/src/main/groovy/com/stormpath/tck/errors/ErrorsIT.groovy +++ b/src/main/groovy/com/stormpath/tck/errors/ErrorsIT.groovy @@ -22,6 +22,7 @@ import org.testng.annotations.Test import static com.jayway.restassured.RestAssured.given import static com.stormpath.tck.util.FrameworkConstants.MeRoute import static com.stormpath.tck.util.FrameworkConstants.MissingRoute +import static org.hamcrest.core.StringStartsWith.startsWith; class ErrorsIT extends AbstractIT { @@ -52,6 +53,6 @@ class ErrorsIT extends AbstractIT { .then() .statusCode(401) // 401 with Accept JSON header does not return JSON - .header("WWW-Authenticate", "Bearer realm=\"My Application\"") + .header("WWW-Authenticate", startsWith("Bearer")) } } diff --git a/src/main/groovy/com/stormpath/tck/forgot/ChangePasswordIT.groovy b/src/main/groovy/com/stormpath/tck/forgot/ChangePasswordIT.groovy index 80c2bb0..6c69f21 100644 --- a/src/main/groovy/com/stormpath/tck/forgot/ChangePasswordIT.groovy +++ b/src/main/groovy/com/stormpath/tck/forgot/ChangePasswordIT.groovy @@ -188,14 +188,14 @@ class ChangePasswordIT extends AbstractIT { deleteOnClassTeardown(account.href) given() - .body([email: account.email]) + .contentType(ContentType.URLENC) + .param("email", account.email) .when() .post(ForgotRoute) .then() .statusCode(200) - // TODO - will need to make this configurable for Okta - String rawChangePasswordEmail = account.getEmail("stormpath.com") + String rawChangePasswordEmail = account.getEmail("okta.com") String changePasswordHref = StringUtils.extractChangePasswordHref(rawChangePasswordEmail, "sptoken") def response = given() @@ -229,14 +229,14 @@ class ChangePasswordIT extends AbstractIT { deleteOnClassTeardown(account.href) given() + .contentType(ContentType.JSON) .body([email: account.email]) .when() .post(ForgotRoute) .then() .statusCode(200) - // TODO - will need to make this configurable for Okta - String rawChangePasswordEmail = account.getEmail("stormpath.com") + String rawChangePasswordEmail = account.getEmail("okta.com") String changePasswordHref = StringUtils.extractChangePasswordHref(rawChangePasswordEmail, "sptoken") String sptoken = StringUtils.extractTokenFromHref(changePasswordHref, "sptoken") diff --git a/src/main/groovy/com/stormpath/tck/oauth2/Oauth2IT.groovy b/src/main/groovy/com/stormpath/tck/oauth2/Oauth2IT.groovy index 8b9e959..9903d95 100644 --- a/src/main/groovy/com/stormpath/tck/oauth2/Oauth2IT.groovy +++ b/src/main/groovy/com/stormpath/tck/oauth2/Oauth2IT.groovy @@ -128,7 +128,7 @@ class Oauth2IT extends AbstractIT { .extract() .path("access_token") - assertTrue(JwtUtils.extractJwtClaim(accessToken, "sub") == account.href) + assertTrue(JwtUtils.extractJwtClaim(accessToken, "sub") == account.email) } /** Password grant flow with username/password and access_token cookie present @@ -156,7 +156,7 @@ class Oauth2IT extends AbstractIT { .extract() .path("access_token") // @formatter:on - assertTrue(JwtUtils.extractJwtClaim(accessToken, "sub") == account.href) + assertTrue(JwtUtils.extractJwtClaim(accessToken, "sub") == account.email) } /** Password grant flow with email/password @@ -178,7 +178,7 @@ class Oauth2IT extends AbstractIT { .extract() .path("access_token") - assertTrue(JwtUtils.extractJwtClaim(accessToken, "sub") == account.href) + assertTrue(JwtUtils.extractJwtClaim(accessToken, "sub") == account.email) } /** Refresh grant flow @@ -215,7 +215,7 @@ class Oauth2IT extends AbstractIT { .path("access_token") assertNotEquals(accessToken, newAccessToken, "The new access token should not equal to the old access token") - assertTrue(JwtUtils.extractJwtClaim(accessToken, "sub") == account.href, "The access token should be a valid jwt for the test user") + assertTrue(JwtUtils.extractJwtClaim(accessToken, "sub") == account.email, "The access token should be a valid jwt for the test user") } /** Refresh grant flow should fail without valid refresh token diff --git a/src/main/groovy/com/stormpath/tck/util/EnvUtils.groovy b/src/main/groovy/com/stormpath/tck/util/EnvUtils.groovy index 4b20aae..b2571f2 100644 --- a/src/main/groovy/com/stormpath/tck/util/EnvUtils.groovy +++ b/src/main/groovy/com/stormpath/tck/util/EnvUtils.groovy @@ -22,16 +22,19 @@ class EnvUtils { public static final String stormpathHtmlEnabled = getVal("STORMPATH_TCK_HTML_ENABLED", "true") - public static final String jwtSigningKey public static final String facebookClientId public static final String facebookClientSecret + public static final String jwtSigningKeyModulus + public static final String jwtSigningKeyExponent + static { - jwtSigningKey = getVal("JWT_SIGNING_KEY") + jwtSigningKeyModulus = getVal("JWT_SIGNING_KEY_MOD") + jwtSigningKeyExponent = getVal("JWT_SIGNING_KEY_EXP") facebookClientId = getVal("FACEBOOK_CLIENT_ID") facebookClientSecret = getVal("FACEBOOK_CLIENT_SECRET") - if (jwtSigningKey == null || facebookClientId == null || facebookClientSecret == null) { - fail("JWT_SIGNING_KEY, FACEBOOK_CLIENT_ID and FACEBOOK_CLIENT_SECRET environment variables are required") + if (jwtSigningKeyModulus == null || jwtSigningKeyExponent == null || facebookClientId == null || facebookClientSecret == null) { + fail("JWT_SIGNING_KEY_MOD, JWT_SIGNING_KEY_EXP, FACEBOOK_CLIENT_ID and FACEBOOK_CLIENT_SECRET environment variables are required. The JWT signing key modulus (n) and exponent (e) can be found at https://.oktapreview.com/oauth2//v1/keys") } } diff --git a/src/main/groovy/com/stormpath/tck/util/JwtUtils.groovy b/src/main/groovy/com/stormpath/tck/util/JwtUtils.groovy index 7b5ff30..9a0cfb1 100644 --- a/src/main/groovy/com/stormpath/tck/util/JwtUtils.groovy +++ b/src/main/groovy/com/stormpath/tck/util/JwtUtils.groovy @@ -19,16 +19,23 @@ import io.jsonwebtoken.Claims import io.jsonwebtoken.Jws import io.jsonwebtoken.Jwts +import java.security.Key +import java.security.KeyFactory +import java.security.spec.RSAPublicKeySpec + class JwtUtils { + static Key getPublicKey() { + def modulus = new BigInteger(1, Base64.getUrlDecoder().decode(EnvUtils.jwtSigningKeyModulus)) + def exponent = new BigInteger(1, Base64.getUrlDecoder().decode(EnvUtils.jwtSigningKeyExponent)) + return KeyFactory.getInstance("RSA").generatePublic(new RSAPublicKeySpec(modulus, exponent)) + } static String extractJwtClaim(String jwt, String property) { - String secret = EnvUtils.jwtSigningKey - Claims claims = Jwts.parser().setSigningKey(secret.getBytes()).parseClaimsJws(jwt).getBody() + Claims claims = Jwts.parser().setSigningKey(getPublicKey()).parseClaimsJws(jwt).getBody() return (String) claims.get(property) } static Jws parseJwt(String jwt) { - String secret = EnvUtils.jwtSigningKey - return Jwts.parser().setSigningKey(secret.getBytes()).parseClaimsJws(jwt) + return Jwts.parser().setSigningKey(getPublicKey()).parseClaimsJws(jwt) } } diff --git a/src/main/groovy/com/stormpath/tck/util/TestAccount.groovy b/src/main/groovy/com/stormpath/tck/util/TestAccount.groovy index f60611d..0f6747e 100644 --- a/src/main/groovy/com/stormpath/tck/util/TestAccount.groovy +++ b/src/main/groovy/com/stormpath/tck/util/TestAccount.groovy @@ -19,6 +19,7 @@ import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.databind.JsonMappingException import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.ObjectMapper +import com.jayway.restassured.http.ContentType import static com.jayway.restassured.RestAssured.get import static com.jayway.restassured.RestAssured.given @@ -62,6 +63,7 @@ class TestAccount { void registerOnServer() { href = given() + .contentType(ContentType.JSON) .body(getPropertiesMap()) .when() .post(RegisterRoute) @@ -97,7 +99,7 @@ class TestAccount { String emailId = null int count = 0 - while (emailId == null && count++ < 30) { + while (emailId == null && count++ < 90) { def jsonResponse = get(GUERILLA_MAIL_BASE + "?f=get_email_list&offset=0&sid_token=" + guerillaEmail.getToken()).asString()