diff --git a/arlas-iam-core/pom.xml b/arlas-iam-core/pom.xml
index b05bbcb..eaf5fcb 100644
--- a/arlas-iam-core/pom.xml
+++ b/arlas-iam-core/pom.xml
@@ -5,7 +5,7 @@
arlas-iam-parent
io.arlas.iam
- 24.1.0-rc.1
+ 24.1.1-rc.2
4.0.0
diff --git a/arlas-iam-core/src/main/java/io/arlas/iam/core/AuthService.java b/arlas-iam-core/src/main/java/io/arlas/iam/core/AuthService.java
index f3e4525..baf8cbb 100644
--- a/arlas-iam-core/src/main/java/io/arlas/iam/core/AuthService.java
+++ b/arlas-iam-core/src/main/java/io/arlas/iam/core/AuthService.java
@@ -17,7 +17,7 @@ public interface AuthService {
LoginSession login(String email, String password, String issuer) throws ArlasException;
DecodedJWT verifyToken(String token);
void logout(UUID userId);
- LoginSession refresh(String authHeader, String refreshToken, String issuer) throws ArlasException;
+ LoginSession refresh(String userId, String refreshToken, String issuer) throws ArlasException;
String createPermissionToken(String subject, String orgFilter, String issuer, Date iat) throws ArlasException;
String createPermissionToken(String keyId, String keySecret, String issuer) throws ArlasException;
String createPermissionToken(HttpHeaders headers, String orgFilter) throws ArlasException;
diff --git a/arlas-iam-core/src/main/java/io/arlas/iam/impl/HibernateAuthService.java b/arlas-iam-core/src/main/java/io/arlas/iam/impl/HibernateAuthService.java
index a1480f2..60460c0 100644
--- a/arlas-iam-core/src/main/java/io/arlas/iam/impl/HibernateAuthService.java
+++ b/arlas-iam-core/src/main/java/io/arlas/iam/impl/HibernateAuthService.java
@@ -297,18 +297,8 @@ public void logout(UUID userId) {
}
@Override
- public LoginSession refresh(String authHeader, String refreshToken, String issuer) throws ArlasException {
- if (authHeader == null || !authHeader.toLowerCase().startsWith("bearer ")) {
- throw new InvalidTokenException("Invalid access token: " + authHeader);
- }
- User user;
- try {
- String accessToken = authHeader.substring(7);
- DecodedJWT t = JWT.decode(accessToken);
- user = readUser(UUID.fromString(t.getSubject()), true);
- } catch (JWTDecodeException e) {
- throw new InvalidTokenException("Invalid access token: " + authHeader, e);
- }
+ public LoginSession refresh(String userId, String refreshToken, String issuer) throws ArlasException {
+ var user = readUser(UUID.fromString(userId), true);
RefreshToken token = tokenDao.read(refreshToken).orElseThrow(() -> new InvalidTokenException("Invalid refresh token."));
if (user.is(token.getUserId()) && token.getExpiryDate() >= System.currentTimeMillis() / 1000) {
diff --git a/arlas-iam-rest/pom.xml b/arlas-iam-rest/pom.xml
index dec8514..2756730 100644
--- a/arlas-iam-rest/pom.xml
+++ b/arlas-iam-rest/pom.xml
@@ -5,7 +5,7 @@
arlas-iam-parent
io.arlas.iam
- 24.1.0-rc.1
+ 24.1.1-rc.2
4.0.0
@@ -15,7 +15,7 @@
io.arlas.iam
arlas-iam-core
- 24.1.0-rc.1
+ 24.1.1-rc.2
diff --git a/arlas-iam-rest/src/main/java/io/arlas/iam/rest/model/output/LoginData.java b/arlas-iam-rest/src/main/java/io/arlas/iam/rest/model/output/LoginData.java
index 08dda19..63e24c7 100644
--- a/arlas-iam-rest/src/main/java/io/arlas/iam/rest/model/output/LoginData.java
+++ b/arlas-iam-rest/src/main/java/io/arlas/iam/rest/model/output/LoginData.java
@@ -2,18 +2,14 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import io.arlas.iam.model.LoginSession;
-import io.arlas.iam.model.RefreshToken;
public class LoginData {
@JsonProperty("access_token")
public String accessToken; // JWT
- @JsonProperty("refresh_token")
- public RefreshToken refreshToken;
public UserData user;
public LoginData(LoginSession loginSession) {
this.accessToken = loginSession.accessToken;
- this.refreshToken = loginSession.refreshToken;
this.user = new UserData(loginSession.user, true);
}
}
diff --git a/arlas-iam-rest/src/main/java/io/arlas/iam/rest/model/output/RefreshTokenCookie.java b/arlas-iam-rest/src/main/java/io/arlas/iam/rest/model/output/RefreshTokenCookie.java
new file mode 100644
index 0000000..85a0d66
--- /dev/null
+++ b/arlas-iam-rest/src/main/java/io/arlas/iam/rest/model/output/RefreshTokenCookie.java
@@ -0,0 +1,33 @@
+package io.arlas.iam.rest.model.output;
+
+import io.arlas.iam.exceptions.InvalidTokenException;
+import io.arlas.iam.model.RefreshToken;
+
+public class RefreshTokenCookie {
+ public String userId;
+ public String refreshToken;
+
+ public RefreshTokenCookie(RefreshToken rt) {
+ this.userId = rt.getUserId().toString();
+ this.refreshToken = rt.getValue();
+ }
+
+ public RefreshTokenCookie(String cookieValue) throws InvalidTokenException {
+ String[] v = parseCookieValue(cookieValue);
+ this.userId = v[0];
+ this.refreshToken = v[1];
+ }
+
+
+ public String getCookieValue() {
+ return String.join("/", this.userId, this.refreshToken);
+ }
+
+ public String[] parseCookieValue(String cookieValue) throws InvalidTokenException {
+ if (cookieValue.contains("/")) {
+ return cookieValue.split("/");
+ } else {
+ throw new InvalidTokenException("Refresh Token format invalid.");
+ }
+ }
+}
diff --git a/arlas-iam-rest/src/main/java/io/arlas/iam/rest/service/IAMRestService.java b/arlas-iam-rest/src/main/java/io/arlas/iam/rest/service/IAMRestService.java
index bcc0250..0a058c3 100644
--- a/arlas-iam-rest/src/main/java/io/arlas/iam/rest/service/IAMRestService.java
+++ b/arlas-iam-rest/src/main/java/io/arlas/iam/rest/service/IAMRestService.java
@@ -10,10 +10,7 @@
import io.arlas.filter.core.IdentityParam;
import io.arlas.iam.core.AuthService;
import io.arlas.iam.exceptions.*;
-import io.arlas.iam.model.ApiKey;
-import io.arlas.iam.model.ForbiddenOrganisation;
-import io.arlas.iam.model.OrganisationMember;
-import io.arlas.iam.model.User;
+import io.arlas.iam.model.*;
import io.arlas.iam.rest.model.input.*;
import io.arlas.iam.rest.model.output.*;
import io.arlas.iam.util.ArlasAuthServerConfiguration;
@@ -56,10 +53,12 @@ public class IAMRestService {
protected final AuthService authService;
private final ArlasAuthConfiguration configuration;
+ private final long refreshTokenTtl;
public IAMRestService(AuthService authService, ArlasAuthServerConfiguration configuration) {
this.authService = authService;
this.configuration = configuration.arlasAuthConfiguration;
+ this.refreshTokenTtl = configuration.arlasAuthConfiguration.refreshTokenTTL / 1000L;
}
private void logUAM(HttpServletRequest request, HttpHeaders headers, String action, String log) {
@@ -144,9 +143,16 @@ public Response login(
@ApiParam(name = "loginDef", required = true)
@NotNull @Valid LoginDef loginDef
) throws ArlasException {
- LoginData loginData = new LoginData(authService.login(loginDef.email, loginDef.password, uriInfo.getBaseUri().getHost()));
+ LoginSession loginSession = authService.login(loginDef.email, loginDef.password, uriInfo.getBaseUri().getHost());
+ LoginData loginData = new LoginData(loginSession);
+ String refreshTokenCookieValue = String.format(
+ "refresh_token=%s; Path=/; Max-Age=%s; Secure; HttpOnly; SameSite=Strict",
+ new RefreshTokenCookie(loginSession.refreshToken).getCookieValue(),
+ refreshTokenTtl);
Response response = Response.ok(uriInfo.getRequestUriBuilder().build())
.entity(loginData)
+ // setting manually as we can't use the NewCookie object because it doesn't accept SameSite attribute before jax-rs version 3.1
+ .header("Set-Cookie", refreshTokenCookieValue)
.type(MediaType.APPLICATION_JSON_TYPE)
.build();
MDC.put(USER_ID, loginData.user.id.toString());
@@ -178,12 +184,13 @@ public Response logout(
logUAM(request, headers, "session", "user-logout");
return Response.ok(uriInfo.getRequestUriBuilder().build())
.entity("Session deleted.")
+ .header("Set-Cookie", "refresh_token=; Max-Age=0")
.type(MediaType.TEXT_PLAIN_TYPE)
.build();
}
@Timed
- @Path("session/{refreshToken}")
+ @Path("session/refresh")
@PUT
@Produces(UTF8JSON)
@Consumes(UTF8JSON)
@@ -201,13 +208,16 @@ public Response logout(
public Response refresh(
@Context UriInfo uriInfo,
@Context HttpHeaders headers,
- @Context HttpServletRequest request,
+ @Context HttpServletRequest request,
- @ApiParam(name = "refreshToken", required = true)
- @PathParam(value = "refreshToken") String refreshToken
+ @CookieParam("refresh_token") Cookie refreshToken
) throws ArlasException {
+ if (refreshToken == null) {
+ throw new InvalidTokenException("Missing refresh token in cookie");
+ }
+ RefreshTokenCookie rt = new RefreshTokenCookie(refreshToken.getValue());
return Response.ok(uriInfo.getRequestUriBuilder().build())
- .entity(new LoginData(authService.refresh(headers.getHeaderString(HttpHeaders.AUTHORIZATION), refreshToken, uriInfo.getBaseUri().getHost())))
+ .entity(new LoginData(authService.refresh(rt.userId, rt.refreshToken, uriInfo.getBaseUri().getHost())))
.type(MediaType.APPLICATION_JSON_TYPE)
.build();
}
diff --git a/arlas-iam-server/pom.xml b/arlas-iam-server/pom.xml
index d145991..f13a08d 100644
--- a/arlas-iam-server/pom.xml
+++ b/arlas-iam-server/pom.xml
@@ -5,7 +5,7 @@
arlas-iam-parent
io.arlas.iam
- 24.1.0-rc.1
+ 24.1.1-rc.2
4.0.0
@@ -15,7 +15,7 @@
io.arlas.iam
arlas-iam-rest
- 24.1.0-rc.1
+ 24.1.1-rc.2
diff --git a/arlas-iam-tests/pom.xml b/arlas-iam-tests/pom.xml
index c872fe3..3f9b80d 100644
--- a/arlas-iam-tests/pom.xml
+++ b/arlas-iam-tests/pom.xml
@@ -5,7 +5,7 @@
arlas-iam-parent
io.arlas.iam
- 24.1.0-rc.1
+ 24.1.1-rc.2
4.0.0
diff --git a/arlas-iam-tests/src/test/java/io/arlas/iam/test/AuthEndpoints.java b/arlas-iam-tests/src/test/java/io/arlas/iam/test/AuthEndpoints.java
index ee99c87..d109c0e 100644
--- a/arlas-iam-tests/src/test/java/io/arlas/iam/test/AuthEndpoints.java
+++ b/arlas-iam-tests/src/test/java/io/arlas/iam/test/AuthEndpoints.java
@@ -91,12 +91,11 @@ protected Response login(String email, String password) {
.post(arlasAppPath.concat("session"));
}
- protected Response refreshToken(String userId, String refreshToken) {
+ protected Response refreshToken(String refreshToken) {
return given()
- .header(AUTH_HEADER, getToken(userId))
+ .cookie("refresh_token", refreshToken)
.contentType("application/json")
- .pathParam("refreshToken", refreshToken)
- .put(arlasAppPath.concat("session/{refreshToken}"));
+ .put(arlasAppPath.concat("session/refresh"));
}
protected Response logout(String userId) {
diff --git a/arlas-iam-tests/src/test/java/io/arlas/iam/test/AuthITUser.java b/arlas-iam-tests/src/test/java/io/arlas/iam/test/AuthITUser.java
index 2315e35..def8b30 100644
--- a/arlas-iam-tests/src/test/java/io/arlas/iam/test/AuthITUser.java
+++ b/arlas-iam-tests/src/test/java/io/arlas/iam/test/AuthITUser.java
@@ -1,6 +1,8 @@
package io.arlas.iam.test;
import io.restassured.path.json.JsonPath;
+import io.restassured.response.ExtractableResponse;
+import io.restassured.response.Response;
import io.restassured.response.ValidatableResponse;
import org.junit.FixMethodOrder;
import org.junit.Test;
@@ -35,9 +37,10 @@ public void test002CreateUserInvalidEmail() {
@Test
public void test010Login() {
- JsonPath json = login(USER1).then().statusCode(200).extract().jsonPath();
+ ExtractableResponse response = login(USER1).then().statusCode(200).extract();
+ JsonPath json = response.jsonPath();
token1 = json.get("access_token");
- refreshToken1 = json.get("refresh_token.value");
+ refreshToken1 = response.cookie("refresh_token");
token2 = login(USER2).then().statusCode(200)
.extract().jsonPath().get("access_token");
tokenAdmin = login(ADMIN, ADMIN_PASSWORD).then().statusCode(200)
@@ -46,7 +49,7 @@ public void test010Login() {
@Test
public void test011RefreshToken() {
- token1 = refreshToken(userId1, refreshToken1).then().statusCode(200)
+ token1 = refreshToken(refreshToken1).then().statusCode(200)
.extract().jsonPath().get("access_token");
}
@@ -67,6 +70,8 @@ public void test015ChangePassword() {
logout(USER1).then().statusCode(200);
+ refreshToken(refreshToken1).then().statusCode(401);
+
token1 = login(USER1, "newsecret").then().statusCode(200)
.extract().jsonPath().get("access_token");
diff --git a/conf/configuration.yaml b/conf/configuration.yaml
index e591624..5238e59 100644
--- a/conf/configuration.yaml
+++ b/conf/configuration.yaml
@@ -41,10 +41,10 @@ swagger: # Configuration of SWAGGER for generating documentation and APIs
arlas_cors:
enabled: ${ARLAS_CORS_ENABLED:-true}
allowed_origins: ${ARLAS_CORS_ALLOWED_ORIGINS:-"*"}
- allowed_headers: ${ARLAS_CORS_ALLOWED_HEADERS:-"arlas-user,arlas-groups,arlas-organization,arlas-org-filter,X-Requested-With,Content-Type,Accept,Origin,Authorization,WWW-Authenticate"}
+ allowed_headers: ${ARLAS_CORS_ALLOWED_HEADERS:-"arlas-user,arlas-groups,arlas-organization,arlas-org-filter,X-Requested-With,Content-Type,Accept,Origin,Authorization,WWW-Authenticate,Set-Cookie"}
allowed_methods: ${ARLAS_CORS_ALLOWED_METHODS:-"OPTIONS,GET,PUT,POST,DELETE,HEAD"}
allowed_credentials: ${ARLAS_CORS_ALLOWED_CREDENTIALS:-true}
- exposed_headers: ${ARLAS_CORS_EXPOSED_HEADERS:-"Content-Type,Authorization,X-Requested-With,Content-Length,Accept,Origin,Location,WWW-Authenticate"}
+ exposed_headers: ${ARLAS_CORS_EXPOSED_HEADERS:-"Content-Type,Authorization,X-Requested-With,Content-Length,Accept,Origin,Location,WWW-Authenticate,Set-Cookie"}
arlas_auth:
# Access token time to live in millisecond
@@ -53,7 +53,7 @@ arlas_auth:
refresh_token_ttl: ${ARLAS_REFRESH_TOKEN_TTL:-600000}
# Verify token time to live in millisecond
verify_token_ttl: ${ARLAS_VERIFY_TOKEN_TTL:-86400000}
- public_uris: [${ARLAS_AUTH_PUBLIC_URIS:-swagger,swagger.*,session:POST,session/.*:PUT,users:POST,users/.*:POST,organisations/check:GET}]
+ public_uris: [${ARLAS_AUTH_PUBLIC_URIS:-swagger,swagger.*,session:POST,session/refresh:PUT,users:POST,users/.*:POST,organisations/check:GET}]
header_user: ${ARLAS_HEADER_USER:-arlas-user}
header_group: ${ARLAS_HEADER_GROUP:-arlas-groups}
anonymous_value: ${ARLAS_ANONYMOUS_VALUE:-anonymous}
diff --git a/docker/docker-files/docker-compose.yml b/docker/docker-files/docker-compose.yml
index 159d57e..308d73e 100644
--- a/docker/docker-files/docker-compose.yml
+++ b/docker/docker-files/docker-compose.yml
@@ -45,7 +45,7 @@ services:
- ARLAS_IAM_PREFIX="${ARLAS_IAM_PREFIX:-/arlas_iam_server}"
- ARLAS_IAM_APP_PATH="${ARLAS_IAM_APP_PATH:-/}"
- ARLAS_ANONYMOUS_VALUE="${ARLAS_ANONYMOUS_VALUE:-anonymous}"
- - ARLAS_AUTH_PUBLIC_URIS=${ARLAS_AUTH_PUBLIC_URIS:-swagger,swagger.*,session:POST,session/.*:PUT,users:POST,users/.*:POST}
+ - ARLAS_AUTH_PUBLIC_URIS=${ARLAS_AUTH_PUBLIC_URIS:-swagger,swagger.*,session:POST,session/refresh:PUT,users:POST,users/.*:POST}
- ARLAS_IAM_PRIVATE_ORG="${ARLAS_IAM_PRIVATE_ORG:-false}"
- ARLAS_IAM_HIBERNATE_URL="${ARLAS_IAM_HIBERNATE_URL:-jdbc:postgresql://db:5432/arlas_iam}"
- ARLAS_IAM_HIBERNATE_USER="${ARLAS_IAM_HIBERNATE_USER:-pg-user}"
diff --git a/pom.xml b/pom.xml
index aee312e..1fc3a8e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
io.arlas.iam
arlas-iam-parent
- 24.1.0-rc.1
+ 24.1.1-rc.2
arlas-iam-core
arlas-iam-rest