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/FetchCognitoToken.java b/src/main/java/city/makeour/moc/FetchCognitoToken.java index 9be8b45..6649867 100644 --- a/src/main/java/city/makeour/moc/FetchCognitoToken.java +++ b/src/main/java/city/makeour/moc/FetchCognitoToken.java @@ -50,6 +50,25 @@ 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(), + result.expiresIn()); + + return token; + } + public Token fetchTokenWithSrpAuth() throws InvalidKeyException, NoSuchAlgorithmException { InitiateAuthRequest initiateAuthRequest = InitiateAuthRequest.builder() .authFlow(AuthFlowType.USER_SRP_AUTH) @@ -87,10 +106,8 @@ public Token fetchTokenWithSrpAuth() throws InvalidKeyException, NoSuchAlgorithm RespondToAuthChallengeResponse authChallengeResponse = cognitoClient.respondToAuthChallenge(respondRequest); AuthenticationResultType authResult = authChallengeResponse.authenticationResult(); - return new Token(authResult.idToken(), authResult.refreshToken()); - } + Integer expiresIn = authResult.expiresIn(); - public Token fetchToken() throws InvalidKeyException, NoSuchAlgorithmException { - return this.fetchTokenWithSrpAuth(); + return new Token(authResult.idToken(), authResult.refreshToken(), authResult.accessToken(), expiresIn); } } diff --git a/src/main/java/city/makeour/moc/MocClient.java b/src/main/java/city/makeour/moc/MocClient.java index 14526a9..a7a7fda 100644 --- a/src/main/java/city/makeour/moc/MocClient.java +++ b/src/main/java/city/makeour/moc/MocClient.java @@ -35,15 +35,48 @@ 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."); } this.tokenFetcher.setAuthParameters(username, password); - Token token = this.tokenFetcher.fetchToken(); + Token token = this.tokenFetcher.fetchTokenWithSrpAuth(); + + this.setToken(token.getIdToken()); + this.refreshTokenStorage.setRefreshToken(token); + } + + 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()) { + throw new IllegalStateException("No refresh token available."); + } + + RefreshTokenInterface refreshToken = this.refreshTokenStorage.getRefreshToken(); + Token token = this.tokenFetcher.refleshToken(refreshToken.getRefreshToken()); this.setToken(token.getIdToken()); - this.refreshTokenStorage.setRefreshToken(token.getRefreshToken()); } 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 984834d..acd663f 100644 --- a/src/main/java/city/makeour/moc/Token.java +++ b/src/main/java/city/makeour/moc/Token.java @@ -1,12 +1,32 @@ package city.makeour.moc; -public class Token { +import java.time.ZonedDateTime; + +public class Token implements RefreshTokenInterface { 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) { + 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() { @@ -16,4 +36,24 @@ public String getIdToken() { public String getRefreshToken() { return refreshToken; } + + 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(); + } } 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/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()); + } + + } +} 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 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"); } }