Skip to content

Add AWS CognitoAuthTokenProvider module #536

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Dec 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions aws-cognito/http-client-authtoken/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.avaje</groupId>
<artifactId>java11-oss</artifactId>
<version>4.5</version>
</parent>

<groupId>io.avaje.aws</groupId>
<artifactId>avaje-cognito-client-token</artifactId>
<version>1.0-RC1</version>

<dependencies>
<dependency>
<groupId>io.avaje</groupId>
<artifactId>avaje-http-client</artifactId>
<version>2.8</version>
</dependency>

<dependency>
<groupId>io.avaje</groupId>
<artifactId>avaje-json-core</artifactId>
<version>3.0-RC5</version>
</dependency>

<!-- test dependencies -->
<dependency>
<groupId>io.avaje</groupId>
<artifactId>avaje-json-node</artifactId>
<version>3.0-RC5</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>io.avaje</groupId>
<artifactId>junit</artifactId>
<version>1.5</version>
<scope>test</scope>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package io.avaje.aws.client.cognito;

import io.avaje.http.client.AuthToken;
import io.avaje.http.client.AuthTokenProvider;
import io.avaje.http.client.BasicAuthIntercept;
import io.avaje.http.client.HttpClientRequest;
import io.avaje.json.simple.SimpleMapper;

import java.net.http.HttpResponse;
import java.time.Instant;

final class AmzCognitoAuthTokenProvider implements CognitoAuthTokenProvider.Builder {

private String url;
private String clientId;
private String clientSecret;
private String scope;

@Override
public CognitoAuthTokenProvider.Builder url(String url) {
this.url = url;
return this;
}

@Override
public CognitoAuthTokenProvider.Builder clientId(String clientId) {
this.clientId = clientId;
return this;
}

@Override
public CognitoAuthTokenProvider.Builder clientSecret(String clientSecret) {
this.clientSecret = clientSecret;
return this;
}

@Override
public CognitoAuthTokenProvider.Builder scope(String scope) {
this.scope = scope;
return this;
}

@Override
public AuthTokenProvider build() {
return new Provider(url, clientId, clientSecret, scope);
}

private static final class Provider implements AuthTokenProvider {

private static final SimpleMapper MAPPER = SimpleMapper.builder().build();

private final String url;
private final String clientId;
private final String scope;
private final String authHeader;

public Provider(String url, String clientId, String clientSecret, String scope) {
this.url = url;
this.clientId = clientId;
this.scope = scope;
this.authHeader = "Basic " + BasicAuthIntercept.encode(clientId, clientSecret);
}

@Override
public AuthToken obtainToken(HttpClientRequest request) {
HttpResponse<String> res = request
.url(url)
.header("Authorization", authHeader)
.formParam("grant_type", "client_credentials")
.formParam("client_id", clientId)
.formParam("scope", scope)
.POST()
.asString();

if (res.statusCode() != 200) {
throw new IllegalStateException("Error response getting access token statusCode:" + res.statusCode() + " res:" + res);
}
return decodeAuthToken(res.body());
}

private AuthToken decodeAuthToken(String responseBody) {
final var responseMap = MAPPER.fromJsonObject(responseBody);
final var accessToken = (String) responseMap.get("access_token");
final var expiresIn = (Long) responseMap.get("expires_in");

var validUntil = Instant.now()
.plusSeconds(expiresIn)
.minusSeconds(60);

return AuthToken.of(accessToken, validUntil);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package io.avaje.aws.client.cognito;

import io.avaje.http.client.AuthTokenProvider;

/**
* AuthTokenProvider for AWS Cognito providing Bearer access tokens.
*
* <pre>{@code
*
* AuthTokenProvider authTokenProvider = CognitoAuthTokenProvider.builder()
* .url("https://foo.amazoncognito.com/oauth2/token")
* .clientId("<something>")
* .clientSecret("<something>")
* .scope("default/default")
* .build();
*
* // specify the authTokenProvider on the HttpClient ...
*
* HttpClient client = HttpClient.builder()
* .authTokenProvider(authTokenProvider)
* .baseUrl(myApplicationUrl)
* .build();
*
* }</pre>
*/
public interface CognitoAuthTokenProvider extends AuthTokenProvider {

/**
* Return a builder for the CognitoAuthTokenProvider.
*/
static Builder builder() {
return new AmzCognitoAuthTokenProvider();
}

/**
* The builder for the AWS Cognito AuthTokenProvider.
*/
interface Builder {

/**
* Set the url used to obtain access tokens.
*/
Builder url(String url);

/**
* Set the clientId.
*/
Builder clientId(String clientId);

/**
* Set the clientSecret.
*/
Builder clientSecret(String clientSecret);

/**
* Set the scope.
*/
Builder scope(String scope);

/**
* Build and return the AuthTokenProvider.
*/
AuthTokenProvider build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module io.avaje.aws.client.cognito {

exports io.avaje.aws.client.cognito;

requires transitive io.avaje.http.client;
requires transitive io.avaje.json;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package io.avaje.aws.client.cognito;

import io.avaje.http.client.AuthTokenProvider;
import io.avaje.http.client.HttpClientRequest;
import io.avaje.http.client.HttpClientResponse;
import org.junit.jupiter.api.Test;

import java.net.http.HttpResponse;

import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.*;

@SuppressWarnings("unchecked")
class CognitoAuthTokenProviderTest {

@Test
void obtainToken() {
final String url = "https://<something>.amazoncognito.com/oauth2/token";
final String clientId = "<something>";
final String clientSecret = "<something>";

AuthTokenProvider authTokenProvider = CognitoAuthTokenProvider.builder()
.url(url)
.clientId(clientId)
.clientSecret(clientSecret)
.scope("default/default")
.build();

HttpResponse<String> httpResponse = mock(HttpResponse.class);
when(httpResponse.statusCode()).thenReturn(200);
when(httpResponse.body()).thenReturn("{\"access_token\":\"1234\",\"expires_in\":3600}");

HttpClientResponse clientResponse = mock(HttpClientResponse.class);
when(clientResponse.asString()).thenReturn(httpResponse);

HttpClientRequest httpClientRequest = mock(HttpClientRequest.class);
when(httpClientRequest.url(anyString())).thenReturn(httpClientRequest);

when(httpClientRequest.header(anyString(), anyString())).thenReturn(httpClientRequest);
when(httpClientRequest.formParam(anyString(), anyString())).thenReturn(httpClientRequest);
when(httpClientRequest.POST()).thenReturn(clientResponse);

// act
authTokenProvider.obtainToken(httpClientRequest);

// verify the Authorization header has been obtained and set
verify(httpClientRequest).header("Authorization", "Basic PHNvbWV0aGluZz46PHNvbWV0aGluZz4=");
}
}
15 changes: 15 additions & 0 deletions aws-cognito/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.avaje</groupId>
<artifactId>java11-oss</artifactId>
<version>4.5</version>
</parent>

<artifactId>aws-cognito</artifactId>
<packaging>pom</packaging>

</project>
2 changes: 2 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@
<profile>
<id>test21</id>
<modules>
<!-- release http-client-authtoken on its own cadence -->
<module>aws-cognito/http-client-authtoken</module>
<module>htmx-nima</module>
<module>htmx-nima-jstache</module>
<module>http-generator-helidon</module>
Expand Down
Loading