diff --git a/stream-core/src/main/java/io/getstream/client/model/feeds/BaseFeed.java b/stream-core/src/main/java/io/getstream/client/model/feeds/BaseFeed.java index b6f14ae2..b49982ed 100644 --- a/stream-core/src/main/java/io/getstream/client/model/feeds/BaseFeed.java +++ b/stream-core/src/main/java/io/getstream/client/model/feeds/BaseFeed.java @@ -48,6 +48,11 @@ public String getReadOnlyToken() { return streamRepository.getReadOnlyToken(this); } + @Override + public String getUserSessionToken() { + return streamRepository.getUserSessionToken(this); + } + @Override public void follow(String feedSlug, String userId) throws IOException, StreamClientException { String feedId = String.format("%s:%s", feedSlug, userId); diff --git a/stream-core/src/main/java/io/getstream/client/model/feeds/Feed.java b/stream-core/src/main/java/io/getstream/client/model/feeds/Feed.java index fe429d92..95ac1986 100644 --- a/stream-core/src/main/java/io/getstream/client/model/feeds/Feed.java +++ b/stream-core/src/main/java/io/getstream/client/model/feeds/Feed.java @@ -39,6 +39,12 @@ public interface Feed { */ String getReadOnlyToken(); + /** + * Generate User Session JWT Token. UserId is taken from {@link Feed#getId()}. + * @return Token + */ + String getUserSessionToken(); + /** * Follows the given target feed. * diff --git a/stream-core/src/main/java/io/getstream/client/repo/StreamRepository.java b/stream-core/src/main/java/io/getstream/client/repo/StreamRepository.java index 33c3f687..2a7b62be 100644 --- a/stream-core/src/main/java/io/getstream/client/repo/StreamRepository.java +++ b/stream-core/src/main/java/io/getstream/client/repo/StreamRepository.java @@ -48,6 +48,8 @@ public interface StreamRepository { */ String getReadOnlyToken(BaseFeed feed); + String getUserSessionToken(BaseFeed feed); + /** * Follow a feed. * diff --git a/stream-core/src/main/java/io/getstream/client/util/JwtAuthenticationUtil.java b/stream-core/src/main/java/io/getstream/client/util/JwtAuthenticationUtil.java index b6911d3c..75e25954 100644 --- a/stream-core/src/main/java/io/getstream/client/util/JwtAuthenticationUtil.java +++ b/stream-core/src/main/java/io/getstream/client/util/JwtAuthenticationUtil.java @@ -1,11 +1,11 @@ package io.getstream.client.util; -import java.io.UnsupportedEncodingException; - import com.auth0.jwt.JWT; import com.auth0.jwt.JWTCreator; import com.auth0.jwt.algorithms.Algorithm; +import java.io.UnsupportedEncodingException; + /** * Utility class to generate a JWT token. */ @@ -28,8 +28,41 @@ public class JwtAuthenticationUtil { public static String generateToken(final String secretKey, final String action, final String resource, final String feedId, final String userId) { JWTCreator.Builder jwtBuilder = JWT.create(); - jwtBuilder = jwtBuilder.withClaim("action", action); - jwtBuilder = jwtBuilder.withClaim("resource", resource); + if (null != action) { + jwtBuilder = jwtBuilder.withClaim("action", action); + } + + if (null != resource) { + jwtBuilder = jwtBuilder.withClaim("resource", resource); + } + + if (null != feedId) { + jwtBuilder = jwtBuilder.withClaim("feed_id", feedId); + } + + if (null != userId) { + jwtBuilder = jwtBuilder.withClaim("user_id", userId); + } + + try { + Algorithm algorithm = Algorithm.HMAC256(secretKey); + + return jwtBuilder.sign(algorithm); + } catch (UnsupportedEncodingException exc) { + throw new IllegalStateException("Fatal error: JWT Algorithm unsupported."); + } + } + + /** + * Generate JWT token. + * @param secretKey API Secret + * @param feedId FeedId (if null it will not be added to the payload) + * @param userId UserId (if null it will not be added to the payload) + * @return Token string + */ + public static String generateToken(final String secretKey, final String feedId, final String userId) { + JWTCreator.Builder jwtBuilder = JWT.create(); + if (null != feedId) { jwtBuilder = jwtBuilder.withClaim("feed_id", feedId); } diff --git a/stream-repo-apache/src/main/java/io/getstream/client/apache/repo/StreamRepositoryImpl.java b/stream-repo-apache/src/main/java/io/getstream/client/apache/repo/StreamRepositoryImpl.java index ed03e069..9169e534 100644 --- a/stream-repo-apache/src/main/java/io/getstream/client/apache/repo/StreamRepositoryImpl.java +++ b/stream-repo-apache/src/main/java/io/getstream/client/apache/repo/StreamRepositoryImpl.java @@ -83,6 +83,11 @@ public String getReadOnlyToken(BaseFeed feed) { return JwtAuthenticationUtil.generateToken(secretKey, "read", "*", feed.getFeedSlug().concat(feed.getUserId()), null); } + @Override + public String getUserSessionToken(BaseFeed feed) { + return JwtAuthenticationUtil.generateToken(secretKey, null, feed.getUserId()); + } + @Override public void follow(BaseFeed feed, String targetFeedId, int activityCopyLimit) throws StreamClientException, IOException { HttpPost request = new HttpPost(UriBuilder.fromEndpoint(baseEndpoint) diff --git a/stream-repo-apache/src/test/java/io/getstream/client/apache/IntegrationTest.java b/stream-repo-apache/src/test/java/io/getstream/client/apache/IntegrationTest.java index 2e1938ca..b2d40f7b 100644 --- a/stream-repo-apache/src/test/java/io/getstream/client/apache/IntegrationTest.java +++ b/stream-repo-apache/src/test/java/io/getstream/client/apache/IntegrationTest.java @@ -78,6 +78,15 @@ public void shouldGetReadOnlyToken() throws IOException, StreamClientException, assertThat(map.get("resource").asString(), is(ALL)); } + @Test + public void shouldGetUserSessionToken() throws StreamClientException { + StreamClient streamClient = new StreamClientImpl(CLIENT_CONFIGURATION, API_KEY, API_SECRET); + Feed feed = streamClient.newFeed("feedslug", "aUserId"); + + Map map = verifyToken(feed.getUserSessionToken()); + assertThat(map.get("user_id").asString(), is("aUserId")); + } + @Test public void shouldGetFollowers() throws IOException, StreamClientException { StreamClient streamClient = new StreamClientImpl(CLIENT_CONFIGURATION, API_KEY, diff --git a/stream-repo-okhttp/src/main/java/io/getstream/client/okhttp/repo/StreamRepositoryImpl.java b/stream-repo-okhttp/src/main/java/io/getstream/client/okhttp/repo/StreamRepositoryImpl.java index a1d4f65f..de901ceb 100644 --- a/stream-repo-okhttp/src/main/java/io/getstream/client/okhttp/repo/StreamRepositoryImpl.java +++ b/stream-repo-okhttp/src/main/java/io/getstream/client/okhttp/repo/StreamRepositoryImpl.java @@ -86,6 +86,11 @@ public String getToken(BaseFeed feed) { return StreamRepoUtils.createFeedToken(feed, secretKey); } + @Override + public String getUserSessionToken(BaseFeed feed) { + return JwtAuthenticationUtil.generateToken(secretKey, null, feed.getUserId()); + } + @Override public void follow(BaseFeed feed, String targetFeedId, int activityCopyLimit) throws StreamClientException, IOException { Request.Builder requestBuilder = new Request.Builder().url(UriBuilder.fromEndpoint(baseEndpoint) diff --git a/stream-repo-okhttp/src/test/java/io/getstream/client/okhttp/IntegrationTest.java b/stream-repo-okhttp/src/test/java/io/getstream/client/okhttp/IntegrationTest.java index afbf5944..9d6949b6 100644 --- a/stream-repo-okhttp/src/test/java/io/getstream/client/okhttp/IntegrationTest.java +++ b/stream-repo-okhttp/src/test/java/io/getstream/client/okhttp/IntegrationTest.java @@ -68,6 +68,15 @@ public void shouldGetReadOnlyToken() throws StreamClientException { assertThat(map.get("resource").asString(), is(ALL)); } + @Test + public void shouldGetUserSessionToken() throws StreamClientException { + StreamClient streamClient = new StreamClientImpl(CLIENT_CONFIGURATION, API_KEY, API_SECRET); + Feed feed = streamClient.newFeed("feedslug", "aUserId"); + + Map map = verifyToken(feed.getUserSessionToken()); + assertThat(map.get("user_id").asString(), is("aUserId")); + } + @Test public void shouldGetFollowers() throws IOException, StreamClientException { StreamClient streamClient = new StreamClientImpl(CLIENT_CONFIGURATION, API_KEY,