From ffa01e72e2fa11ed181736b9ecdc8f8a8fe98159 Mon Sep 17 00:00:00 2001 From: Shugo USHIO Date: Sun, 4 May 2025 16:18:28 +0900 Subject: [PATCH 1/5] Add refresh method --- .../city/makeour/moc/FetchCognitoToken.java | 24 +++++++++++++++---- src/main/java/city/makeour/moc/MocClient.java | 17 ++++++++++++- src/main/java/city/makeour/moc/Token.java | 11 ++++++++- .../makeour/moc/TokenFetcherInterface.java | 4 +++- .../makeour/moc/FetchCognitoTokenTest.java | 9 +++++++ 5 files changed, 57 insertions(+), 8 deletions(-) diff --git a/src/main/java/city/makeour/moc/FetchCognitoToken.java b/src/main/java/city/makeour/moc/FetchCognitoToken.java index 9be8b45..45939d1 100644 --- a/src/main/java/city/makeour/moc/FetchCognitoToken.java +++ b/src/main/java/city/makeour/moc/FetchCognitoToken.java @@ -50,6 +50,24 @@ public void setAuthParameters(String username, String password) { this.password = password; } + public Token refleshToken(String refreshToken) { + InitiateAuthRequest initiateAuthRequest = InitiateAuthRequest.builder() + .authFlow(AuthFlowType.REFRESH_TOKEN_AUTH) + .clientId(this.cognitoClientId) + .authParameters(Map.of( + "REFRESH_TOKEN", refreshToken)) + .build(); + InitiateAuthResponse initiateAuthResponse = cognitoClient.initiateAuth(initiateAuthRequest); + AuthenticationResultType result = initiateAuthResponse.authenticationResult(); + + Token token = new Token( + result.idToken(), + result.refreshToken(), + result.accessToken()); + + return token; + } + public Token fetchTokenWithSrpAuth() throws InvalidKeyException, NoSuchAlgorithmException { InitiateAuthRequest initiateAuthRequest = InitiateAuthRequest.builder() .authFlow(AuthFlowType.USER_SRP_AUTH) @@ -87,10 +105,6 @@ public Token fetchTokenWithSrpAuth() throws InvalidKeyException, NoSuchAlgorithm RespondToAuthChallengeResponse authChallengeResponse = cognitoClient.respondToAuthChallenge(respondRequest); AuthenticationResultType authResult = authChallengeResponse.authenticationResult(); - return new Token(authResult.idToken(), authResult.refreshToken()); - } - - public Token fetchToken() throws InvalidKeyException, NoSuchAlgorithmException { - return this.fetchTokenWithSrpAuth(); + return new Token(authResult.idToken(), authResult.refreshToken(), authResult.accessToken()); } } diff --git a/src/main/java/city/makeour/moc/MocClient.java b/src/main/java/city/makeour/moc/MocClient.java index 14526a9..4c99f3e 100644 --- a/src/main/java/city/makeour/moc/MocClient.java +++ b/src/main/java/city/makeour/moc/MocClient.java @@ -41,11 +41,26 @@ public void login(String username, String password) throws InvalidKeyException, } this.tokenFetcher.setAuthParameters(username, password); - Token token = this.tokenFetcher.fetchToken(); + Token token = this.tokenFetcher.fetchTokenWithSrpAuth(); + this.setToken(token.getIdToken()); this.refreshTokenStorage.setRefreshToken(token.getRefreshToken()); } + public void refreshToken() throws InvalidKeyException, NoSuchAlgorithmException { + if (this.tokenFetcher == null) { + throw new IllegalStateException("MocClient is not initialized with Cognito auth info."); + } + + if (this.refreshTokenStorage.hasRefreshToken()) { + String refreshToken = this.refreshTokenStorage.getRefreshToken(); + Token token = this.tokenFetcher.refleshToken(refreshToken); + this.setToken(token.getIdToken()); + } else { + throw new IllegalStateException("No refresh token available."); + } + } + public void setToken(String token) { this.apiClient.addDefaultHeader("Authorization", token); } diff --git a/src/main/java/city/makeour/moc/Token.java b/src/main/java/city/makeour/moc/Token.java index 984834d..41a2d3c 100644 --- a/src/main/java/city/makeour/moc/Token.java +++ b/src/main/java/city/makeour/moc/Token.java @@ -3,8 +3,9 @@ public class Token { private String idToken; private String refreshToken; + private String accessToken; - public Token(String idToken, String refreshToken) { + public Token(String idToken, String refreshToken, String accessToken) { this.idToken = idToken; this.refreshToken = refreshToken; } @@ -16,4 +17,12 @@ public String getIdToken() { public String getRefreshToken() { return refreshToken; } + + public String getAccessToken() { + return accessToken; + } + + public boolean hasRefreshToken() { + return refreshToken != null && !refreshToken.isEmpty(); + } } diff --git a/src/main/java/city/makeour/moc/TokenFetcherInterface.java b/src/main/java/city/makeour/moc/TokenFetcherInterface.java index fc4c7af..3d08399 100644 --- a/src/main/java/city/makeour/moc/TokenFetcherInterface.java +++ b/src/main/java/city/makeour/moc/TokenFetcherInterface.java @@ -6,5 +6,7 @@ public interface TokenFetcherInterface { void setAuthParameters(String username, String password); - Token fetchToken() throws InvalidKeyException, NoSuchAlgorithmException; + Token fetchTokenWithSrpAuth() throws InvalidKeyException, NoSuchAlgorithmException; + + Token refleshToken(String refreshToken) throws InvalidKeyException, NoSuchAlgorithmException; } diff --git a/src/test/java/city/makeour/moc/FetchCognitoTokenTest.java b/src/test/java/city/makeour/moc/FetchCognitoTokenTest.java index 9ea376c..f0c7a57 100644 --- a/src/test/java/city/makeour/moc/FetchCognitoTokenTest.java +++ b/src/test/java/city/makeour/moc/FetchCognitoTokenTest.java @@ -1,6 +1,7 @@ package city.makeour.moc; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; @@ -28,5 +29,13 @@ public void testFetchTokenWithSrpAuth() throws Exception { Token token = fetchCognitoToken.fetchTokenWithSrpAuth(); assertNotNull(token.getIdToken()); assertNotNull(token.getRefreshToken()); + + // Refresh tokenを使ってトークンを更新する トークンのローテーションはしない + Token refreshedToken = fetchCognitoToken.refleshToken(token.getRefreshToken()); + assertNotNull(refreshedToken.getIdToken()); + assertNull(refreshedToken.getRefreshToken()); + + Token refreshedToken2 = fetchCognitoToken.refleshToken(token.getRefreshToken()); + assertNotNull(refreshedToken2.getIdToken()); } } \ No newline at end of file From 5ac082f18db009ae60326e7d2c060169c4a7d6d5 Mon Sep 17 00:00:00 2001 From: Shugo USHIO Date: Sun, 4 May 2025 16:49:26 +0900 Subject: [PATCH 2/5] Added about expiresIn --- .../city/makeour/moc/FetchCognitoToken.java | 7 ++-- src/main/java/city/makeour/moc/Token.java | 33 ++++++++++++++++++- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/main/java/city/makeour/moc/FetchCognitoToken.java b/src/main/java/city/makeour/moc/FetchCognitoToken.java index 45939d1..6649867 100644 --- a/src/main/java/city/makeour/moc/FetchCognitoToken.java +++ b/src/main/java/city/makeour/moc/FetchCognitoToken.java @@ -63,7 +63,8 @@ public Token refleshToken(String refreshToken) { Token token = new Token( result.idToken(), result.refreshToken(), - result.accessToken()); + result.accessToken(), + result.expiresIn()); return token; } @@ -105,6 +106,8 @@ public Token fetchTokenWithSrpAuth() throws InvalidKeyException, NoSuchAlgorithm RespondToAuthChallengeResponse authChallengeResponse = cognitoClient.respondToAuthChallenge(respondRequest); AuthenticationResultType authResult = authChallengeResponse.authenticationResult(); - return new Token(authResult.idToken(), authResult.refreshToken(), authResult.accessToken()); + Integer expiresIn = authResult.expiresIn(); + + return new Token(authResult.idToken(), authResult.refreshToken(), authResult.accessToken(), expiresIn); } } diff --git a/src/main/java/city/makeour/moc/Token.java b/src/main/java/city/makeour/moc/Token.java index 41a2d3c..c6e5a1b 100644 --- a/src/main/java/city/makeour/moc/Token.java +++ b/src/main/java/city/makeour/moc/Token.java @@ -1,13 +1,32 @@ package city.makeour.moc; +import java.time.ZonedDateTime; + public class Token { private String idToken; private String refreshToken; private String accessToken; + private ZonedDateTime expirationDate; + + public Token(String idToken, String refreshToken, String accessToken, Integer expiresIn) { + this(idToken, refreshToken, accessToken, expiresIn, ZonedDateTime.now()); + } - public Token(String idToken, String refreshToken, String accessToken) { + public Token(String idToken, String refreshToken, String accessToken, Integer expiresIn, ZonedDateTime now) { + if (expiresIn <= 0) { + throw new IllegalArgumentException("Expiration date must be greater than 0"); + } this.idToken = idToken; this.refreshToken = refreshToken; + this.accessToken = accessToken; + + if (expiresIn != null) { + if (now == null) { + now = ZonedDateTime.now(); + } + ZonedDateTime date = now.plusSeconds(expiresIn); + this.expirationDate = date; + } } public String getIdToken() { @@ -22,6 +41,18 @@ public String getAccessToken() { return accessToken; } + public ZonedDateTime getExpirationDate() { + return expirationDate; + } + + public boolean isExpired() { + return this.isExpired(ZonedDateTime.now()); + } + + public boolean isExpired(ZonedDateTime now) { + return now.isAfter(expirationDate); + } + public boolean hasRefreshToken() { return refreshToken != null && !refreshToken.isEmpty(); } From 929ae6a2a2ebfc43346ef291549ac9cde7ee0453 Mon Sep 17 00:00:00 2001 From: Shugo USHIO Date: Sun, 4 May 2025 16:58:44 +0900 Subject: [PATCH 3/5] Add refresh token interface and more --- src/main/java/city/makeour/moc/MocClient.java | 12 ++++++------ .../city/makeour/moc/RefreshTokenInterface.java | 16 ++++++++++++++++ .../city/makeour/moc/RefreshTokenStorage.java | 8 ++++---- .../moc/RefreshTokenStorageInterface.java | 4 ++-- src/main/java/city/makeour/moc/Token.java | 2 +- 5 files changed, 29 insertions(+), 13 deletions(-) create mode 100644 src/main/java/city/makeour/moc/RefreshTokenInterface.java diff --git a/src/main/java/city/makeour/moc/MocClient.java b/src/main/java/city/makeour/moc/MocClient.java index 4c99f3e..7027243 100644 --- a/src/main/java/city/makeour/moc/MocClient.java +++ b/src/main/java/city/makeour/moc/MocClient.java @@ -44,7 +44,7 @@ public void login(String username, String password) throws InvalidKeyException, Token token = this.tokenFetcher.fetchTokenWithSrpAuth(); this.setToken(token.getIdToken()); - this.refreshTokenStorage.setRefreshToken(token.getRefreshToken()); + this.refreshTokenStorage.setRefreshToken(token); } public void refreshToken() throws InvalidKeyException, NoSuchAlgorithmException { @@ -52,13 +52,13 @@ public void refreshToken() throws InvalidKeyException, NoSuchAlgorithmException throw new IllegalStateException("MocClient is not initialized with Cognito auth info."); } - if (this.refreshTokenStorage.hasRefreshToken()) { - String refreshToken = this.refreshTokenStorage.getRefreshToken(); - Token token = this.tokenFetcher.refleshToken(refreshToken); - this.setToken(token.getIdToken()); - } else { + if (!this.refreshTokenStorage.hasRefreshToken()) { throw new IllegalStateException("No refresh token available."); } + + RefreshTokenInterface refreshToken = this.refreshTokenStorage.getRefreshToken(); + Token token = this.tokenFetcher.refleshToken(refreshToken.getRefreshToken()); + this.setToken(token.getIdToken()); } public void setToken(String token) { diff --git a/src/main/java/city/makeour/moc/RefreshTokenInterface.java b/src/main/java/city/makeour/moc/RefreshTokenInterface.java new file mode 100644 index 0000000..b60f2f4 --- /dev/null +++ b/src/main/java/city/makeour/moc/RefreshTokenInterface.java @@ -0,0 +1,16 @@ +package city.makeour.moc; + +import java.time.ZonedDateTime; + +public interface RefreshTokenInterface { + + public String getRefreshToken(); + + public ZonedDateTime getExpirationDate(); + + public boolean isExpired(); + + public boolean isExpired(ZonedDateTime now); + + public boolean hasRefreshToken(); +} diff --git a/src/main/java/city/makeour/moc/RefreshTokenStorage.java b/src/main/java/city/makeour/moc/RefreshTokenStorage.java index dddedc5..dc8ae43 100644 --- a/src/main/java/city/makeour/moc/RefreshTokenStorage.java +++ b/src/main/java/city/makeour/moc/RefreshTokenStorage.java @@ -2,17 +2,17 @@ public class RefreshTokenStorage implements RefreshTokenStorageInterface { - private String refreshToken; + private RefreshTokenInterface refreshToken; public RefreshTokenStorage() { this.refreshToken = null; } - public String getRefreshToken() { - return refreshToken; + public RefreshTokenInterface getRefreshToken() { + return this.refreshToken; } - public void setRefreshToken(String refreshToken) { + public void setRefreshToken(RefreshTokenInterface refreshToken) { this.refreshToken = refreshToken; } diff --git a/src/main/java/city/makeour/moc/RefreshTokenStorageInterface.java b/src/main/java/city/makeour/moc/RefreshTokenStorageInterface.java index a73892c..c41d5f0 100644 --- a/src/main/java/city/makeour/moc/RefreshTokenStorageInterface.java +++ b/src/main/java/city/makeour/moc/RefreshTokenStorageInterface.java @@ -2,9 +2,9 @@ public interface RefreshTokenStorageInterface { - public String getRefreshToken(); + public RefreshTokenInterface getRefreshToken(); - public void setRefreshToken(String refreshToken); + public void setRefreshToken(RefreshTokenInterface refreshToken); public boolean hasRefreshToken(); } diff --git a/src/main/java/city/makeour/moc/Token.java b/src/main/java/city/makeour/moc/Token.java index c6e5a1b..acd663f 100644 --- a/src/main/java/city/makeour/moc/Token.java +++ b/src/main/java/city/makeour/moc/Token.java @@ -2,7 +2,7 @@ import java.time.ZonedDateTime; -public class Token { +public class Token implements RefreshTokenInterface { private String idToken; private String refreshToken; private String accessToken; From 29744b0278f73c081e54913ed2052534f9b5467c Mon Sep 17 00:00:00 2001 From: Shugo USHIO Date: Sun, 4 May 2025 17:13:35 +0900 Subject: [PATCH 4/5] add refresh token test --- src/main/java/city/makeour/moc/MocClient.java | 18 +++++++++++++ .../java/city/makeour/moc/MocClientTest.java | 27 +++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/src/main/java/city/makeour/moc/MocClient.java b/src/main/java/city/makeour/moc/MocClient.java index 7027243..a7a7fda 100644 --- a/src/main/java/city/makeour/moc/MocClient.java +++ b/src/main/java/city/makeour/moc/MocClient.java @@ -35,6 +35,24 @@ public void setMocAuthInfo(String cognitoUserPoolId, String cognitoClientId) { this.tokenFetcher = new FetchCognitoToken(cognitoUserPoolId, cognitoClientId); } + public void auth(String username, String password) throws InvalidKeyException, NoSuchAlgorithmException { + if (this.tokenFetcher == null) { + throw new IllegalStateException("MocClient is not initialized with Cognito auth info."); + } + + this.tokenFetcher.setAuthParameters(username, password); + + if (this.refreshTokenStorage.hasRefreshToken()) { + RefreshTokenInterface refreshToken = this.refreshTokenStorage.getRefreshToken(); + if (!refreshToken.isExpired()) { + this.refreshToken(); + } + return; + } + + this.login(username, password); + } + public void login(String username, String password) throws InvalidKeyException, NoSuchAlgorithmException { if (this.tokenFetcher == null) { throw new IllegalStateException("MocClient is not initialized with Cognito auth info."); diff --git a/src/test/java/city/makeour/moc/MocClientTest.java b/src/test/java/city/makeour/moc/MocClientTest.java index 0fa3daf..bb698c6 100644 --- a/src/test/java/city/makeour/moc/MocClientTest.java +++ b/src/test/java/city/makeour/moc/MocClientTest.java @@ -84,6 +84,33 @@ void testSetMocAuthInfo() throws GeneralSecurityException, NoSuchAlgorithmExcept entity.setId("urn:ngsi-ld:TestEntity:" + uuid); client.entities().createEntity("application/json", entity, "keyValues"); + } + @Test + @DisplayName("トークンリフレッシュなど行えるかのテスト") + @EnabledIfEnvironmentVariables({ + @EnabledIfEnvironmentVariable(named = "TEST_COGNITO_USER_POOL_ID", matches = ".*"), + @EnabledIfEnvironmentVariable(named = "TEST_COGNITO_CLIENT_ID", matches = ".*"), + @EnabledIfEnvironmentVariable(named = "TEST_COGNITO_USERNAME", matches = ".*"), + @EnabledIfEnvironmentVariable(named = "TEST_COGNITO_PASSWORD", matches = ".*") + }) + void testAuth() throws GeneralSecurityException, NoSuchAlgorithmException { + String cognitoUserPoolId = System.getenv("TEST_COGNITO_USER_POOL_ID"); + String cognitoClientId = System.getenv("TEST_COGNITO_CLIENT_ID"); + String username = System.getenv("TEST_COGNITO_USERNAME"); + String password = System.getenv("TEST_COGNITO_PASSWORD"); + + MocClient client = new MocClient(); + client.setMocAuthInfo(cognitoUserPoolId, cognitoClientId); + client.auth(username, password); + client.auth(username, password); // トークンリフレッシュを行う + + String uuid = UUID.randomUUID().toString(); + + CreateEntityRequest entity = new CreateEntityRequest(); + entity.setType("TestEntity"); + entity.setId("urn:ngsi-ld:TestEntity:" + uuid); + + client.entities().createEntity("application/json", entity, "keyValues"); } } From 697ab4add522e9eb2dcf3c6de70ad43d24524fbb Mon Sep 17 00:00:00 2001 From: Shugo USHIO Date: Sun, 4 May 2025 17:40:34 +0900 Subject: [PATCH 5/5] Add dependencies --- pom.xml | 5 +++ .../java/city/makeour/moc/examples/Auth.java | 38 +++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 src/main/java/city/makeour/moc/examples/Auth.java diff --git a/pom.xml b/pom.xml index 78d5058..a9b3f68 100644 --- a/pom.xml +++ b/pom.xml @@ -66,6 +66,11 @@ bcprov-jdk15on 1.70 + + org.springframework + spring-webmvc + 5.3.23 + diff --git a/src/main/java/city/makeour/moc/examples/Auth.java b/src/main/java/city/makeour/moc/examples/Auth.java new file mode 100644 index 0000000..4a93c26 --- /dev/null +++ b/src/main/java/city/makeour/moc/examples/Auth.java @@ -0,0 +1,38 @@ +package city.makeour.moc.examples; + +import java.util.UUID; + +import org.springframework.web.client.RestClientResponseException; + +import city.makeour.moc.MocClient; +import city.makeour.ngsi.v2.model.CreateEntityRequest; + +public class Auth { + + public static void main(String[] args) { + // Create a new MocClient instance + MocClient mocClient = new MocClient("https://orion.sandbox.makeour.city"); + + // Set the Cognito auth info + mocClient.setMocAuthInfo("your_cognito_user_pool_id", "your_cognito_client_id"); + + // Authenticate with username and password + try { + mocClient.auth("your_username", "your_password"); + } catch (Exception e) { + e.printStackTrace(); + } + + String uuid = UUID.randomUUID().toString(); + + CreateEntityRequest entity = new CreateEntityRequest(); + entity.setType("TestEntity"); + entity.setId("urn:ngsi-ld:TestEntity:" + uuid); + try { + mocClient.entities().createEntity("application/json", entity, "keyValues"); + } catch (RestClientResponseException e) { + System.err.println("Error creating entity: " + e.getMessage()); + } + + } +}