-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2 from kafkaesque-io/cognito
AWS Cognito auth plugin
- Loading branch information
Showing
8 changed files
with
280 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
45 changes: 45 additions & 0 deletions
45
java/src/main/java/io/kafkaesque/pulsar/client/auth/AuthenticationCognito.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package io.kafkaesque.pulsar.client.auth; | ||
|
||
import com.google.common.base.Charsets; | ||
|
||
import java.io.IOException; | ||
import java.net.URI; | ||
import java.nio.file.Files; | ||
import java.nio.file.Paths; | ||
import java.util.Map; | ||
import java.util.function.Supplier; | ||
|
||
import org.apache.pulsar.client.api.AuthenticationDataProvider; | ||
import org.apache.pulsar.client.api.PulsarClientException; | ||
|
||
/** | ||
* Cognito JWT based authentication provider. | ||
*/ | ||
public class AuthenticationCognito extends AuthenticationAuth0 { | ||
|
||
/** | ||
* | ||
*/ | ||
private static final long serialVersionUID = 1L; | ||
|
||
private String authMethod = AuthMethod.COGNITO; | ||
|
||
public AuthenticationCognito(String token) { | ||
super(() -> token); | ||
} | ||
|
||
public AuthenticationCognito(Supplier<String> tokenSupplier) { | ||
super(tokenSupplier); | ||
} | ||
|
||
@Override | ||
public String getAuthMethodName() { | ||
return authMethod; | ||
} | ||
|
||
@Override | ||
public AuthenticationDataProvider getAuthData() throws PulsarClientException { | ||
return new AuthenticationDataCognito(tokenSupplier); | ||
} | ||
|
||
} |
93 changes: 93 additions & 0 deletions
93
java/src/main/java/io/kafkaesque/pulsar/client/auth/AuthenticationDataCognito.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
package io.kafkaesque.pulsar.client.auth; | ||
|
||
import java.util.Map; | ||
|
||
import java.util.Set; | ||
import java.util.function.Supplier; | ||
|
||
import javax.naming.AuthenticationException; | ||
|
||
import org.apache.pulsar.common.api.AuthData; | ||
|
||
import org.apache.pulsar.client.api.AuthenticationDataProvider; | ||
|
||
import static java.nio.charset.StandardCharsets.UTF_8; | ||
/** | ||
* This plugin is for AWS Cognito JWT Pulsar authentication data. | ||
*/ | ||
public class AuthenticationDataCognito implements AuthenticationDataProvider { | ||
|
||
/** | ||
* | ||
*/ | ||
private static final long serialVersionUID = 1L; | ||
|
||
private final Supplier<String> token; | ||
|
||
public AuthenticationDataCognito(Supplier<String> token) { | ||
this.token = token; | ||
} | ||
|
||
/* | ||
* HTTP | ||
*/ | ||
|
||
/** | ||
* Check if data for HTTP are available. | ||
* | ||
* @return true if this authentication data contain data for HTTP | ||
*/ | ||
public boolean hasDataForHttp() { | ||
return true; | ||
} | ||
|
||
/** | ||
* | ||
* @return a authentication scheme, or {@code null} if the request will not be authenticated. | ||
*/ | ||
public String getHttpAuthType() { | ||
return null; | ||
} | ||
|
||
/** | ||
* | ||
* @return an enumeration of all the header names | ||
*/ | ||
public Set<Map.Entry<String, String>> getHttpHeaders() throws Exception { | ||
return null; | ||
} | ||
|
||
/* | ||
* Command | ||
*/ | ||
|
||
/** | ||
* Check if data from Pulsar protocol are available. | ||
* | ||
* @return true if this authentication data contain data from Pulsar protocol | ||
*/ | ||
public boolean hasDataFromCommand() { | ||
return token.get() != null; | ||
} | ||
|
||
/** | ||
* | ||
* @return authentication data which will be stored in a command | ||
*/ | ||
public String getCommandData() { | ||
return token.get(); | ||
} | ||
|
||
/** | ||
* For mutual authentication, This method use passed in `data` to evaluate and challenge, | ||
* then returns null if authentication has completed; | ||
* returns authenticated data back to server side, if authentication has not completed. | ||
* | ||
* <p>Mainly used for mutual authentication like sasl. | ||
*/ | ||
public AuthData authenticate(AuthData data) throws AuthenticationException { | ||
byte[] bytes = (hasDataFromCommand() ? this.getCommandData() : "").getBytes(UTF_8); | ||
return AuthData.of(bytes); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
92 changes: 92 additions & 0 deletions
92
java/src/main/java/io/kafkaesque/pulsar/client/auth/cognito/CognitoJWT.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
package io.kafkaesque.pulsar.client.auth.cognito; | ||
|
||
import java.io.UnsupportedEncodingException; | ||
|
||
import com.auth0.jwt.exceptions.JWTVerificationException; | ||
|
||
import org.apache.pulsar.shade.io.netty.util.internal.StringUtil; | ||
import org.apache.pulsar.shade.org.apache.commons.lang3.Validate; | ||
|
||
import kong.unirest.HttpResponse; | ||
import kong.unirest.JsonNode; | ||
import kong.unirest.Unirest; | ||
import kong.unirest.json.JSONObject; | ||
|
||
/** | ||
* Generate a cognito JWT with basic scope validation. | ||
*/ | ||
public class CognitoJWT { | ||
|
||
String tokenServerUrl; | ||
String clientId; | ||
String clientSecret; | ||
String scope; | ||
|
||
private CognitoJWT(String domain, String clientId, String clientSecret, String scope) { | ||
this.tokenServerUrl = Validate.notEmpty(domain); | ||
if (!domain.endsWith("/oauth2/token")) { | ||
this.tokenServerUrl = domain + "/oauth2/token"; | ||
} | ||
this.clientId = Validate.notEmpty(clientId); | ||
this.clientSecret = Validate.notEmpty(clientSecret); | ||
this.scope = Validate.notEmpty(scope); | ||
} | ||
|
||
/** | ||
* Create a CognitoJWT object. | ||
* @param domain | ||
* @param clientId | ||
* @param clientSecret | ||
* @param scope | ||
* @return | ||
*/ | ||
public static CognitoJWT create(String domain, String clientId, String clientSecret, String scope) { | ||
return new CognitoJWT(domain, clientId, clientSecret, scope); | ||
} | ||
|
||
/** | ||
* | ||
* @return | ||
* @throws UnsupportedEncodingException | ||
*/ | ||
public JSONObject generate() throws UnsupportedEncodingException { | ||
|
||
Unirest.config().enableCookieManagement(false); | ||
String reqBody = "grant_type=client_credentials&scope=" + this.scope; | ||
|
||
HttpResponse<JsonNode> response = Unirest.post(this.tokenServerUrl) | ||
.header("content-type", "application/x-www-form-urlencoded") | ||
.basicAuth(this.clientId, this.clientSecret) | ||
.body(reqBody).asJson(); | ||
|
||
Unirest.config().reset(); | ||
int statusCode = response.getStatus(); | ||
JSONObject jsonObj = response.getBody().getObject(); | ||
//TODO: may retry with some 400 or 500 code | ||
if (statusCode != 200) { | ||
throw new JWTVerificationException("invalid aws cognito status code " + statusCode); | ||
} | ||
|
||
return jsonObj; | ||
} | ||
|
||
/** | ||
* Generate and returns a Cognito JWT. | ||
* @return | ||
* @throws JWTVerificationException | ||
*/ | ||
public String generateAndCheck() throws JWTVerificationException{ | ||
JSONObject resp; | ||
try { | ||
resp = generate(); | ||
} catch (UnsupportedEncodingException e) { | ||
throw new JWTVerificationException(e.getMessage()); | ||
} | ||
String token = resp.getString("access_token"); | ||
if (StringUtil.isNullOrEmpty(token)) { | ||
throw new JWTVerificationException("Cognito JWT is empty"); | ||
} | ||
return token; | ||
} | ||
|
||
} |